Creating a simple CSG code for steel shapes

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Extruded Shape with Cylinder Holes - Three.js</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
        #csgButton {
            position: absolute;
            top: 20px;
            left: 20px;
            z-index: 10;
            padding: 10px;
            background-color: #0077ff;
            color: white;
            border: none;
            cursor: pointer;
            font-size: 16px;
        }
        #container {
            width: 100vw;
            height: 100vh;
            position: relative;
        }
    </style>
</head>
<body>
    <div id="container"></div>
    <button id="csgButton">Apply CSG</button>
    <script type="importmap">
        {
            "imports": {
                "three": "https://cdn.jsdelivr.net/npm/three@0.128/build/three.module.js",
                "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.128/examples/jsm/"
            }}
    </script>
    <!-- Three.js Libraries -->
    
    <script type = 'module'>
import *as THREE from "three"
import {OrbitControls} from "three/addons/controls/OrbitControls.js"
        import CSG from "https://cdn.jsdelivr.net/gh/manthrax/THREE-CSGMesh/three-csg.js"
        import {BufferGeometryUtils} from "three/addons/utils/BufferGeometryUtils.js"
        let scene, camera, renderer, controls, beamMesh, cylinders = [];

        init();
        animate();

        function init() {
            // Scene and camera setup
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(0, 50, 100);

            // Renderer setup
            renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.getElementById('container').appendChild(renderer.domElement);

            // OrbitControls
            controls = new OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            controls.dampingFactor = 0.05;

            // Lighting
            const ambientLight = new THREE.AmbientLight(0x404040);
            scene.add(ambientLight);

            const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
            directionalLight.position.set(10, 20, 20);
            scene.add(directionalLight);

            // Create the wide flange beam
            beamMesh = createWideFlange('W12x50', [0, 0, 0], 20);

          //  beamMesh.geometry = new THREE.BoxGeometry(10,10,30);
            scene.add(beamMesh);

            // Create a row of cylinders (bolt holes)
            const cylinderRadius = 1;
            const cylinderHeight = 22; // Longer than beam depth for full subtraction
            const numberOfCylinders = 5;
            const cylinderSpacing = 4;

            for (let i = 0; i < numberOfCylinders; i++) {
                const zPos = i * cylinderSpacing - (numberOfCylinders - 1) * cylinderSpacing / 2;
                const cylinderMesh = createCylinder(2, 0, zPos, cylinderRadius, cylinderHeight);


            //    cylinderMesh.geometry = new THREE.BoxGeometry(1,20,1)
                scene.add(cylinderMesh); // Add cylinders to the scene
                cylinders.push(cylinderMesh); // Store the cylinders for later CSG subtraction
            }


            // Add event listener for CSG subtraction
            document.getElementById('csgButton').addEventListener('click', applyCSGSubtraction);
        }

        // Function to apply CSG subtraction
        function applyCSGSubtraction() {
            let m = CSG;

            let g = beamMesh.geometry;

            let beamCSG = CSG.fromMesh(beamMesh,0);

            cylinders.forEach(cylinderMesh => {

                let cylinderCSG = CSG.fromMesh(cylinderMesh,1);
                beamCSG = beamCSG.subtract(cylinderCSG);
                cylinderMesh.visible = false;
            });

            const resultMesh = CSG.toMesh(beamCSG,beamMesh.matrix,[beamMesh.material,cylinders[0].material]);//toMesh(new THREE.MeshStandardMaterial({ color: 0x0077ff }));
            resultMesh.geometry.computeVertexNormals();
            resultMesh.updateMatrix();
            resultMesh.updateMatrixWorld();

            // Remove original beam and add the CSG result
            scene.remove(beamMesh);

            scene.add(resultMesh);
        }

        // Function to create and extrude the wide flange shape
        function createWideFlange(sizeLabel, position, extrusionLength) {
            const wideFlangeSizes = {
                'W12x50': [12, 8, 0.5, 1] // [beamDepth, flangeWidth, webThickness, flangeDepth]
            };

            const [beamDepth, flangeWidth, webThickness, flangeDepth] = wideFlangeSizes[sizeLabel];
            const shape = new THREE.Shape();
            const filletRadius = flangeDepth / 2;

            // Draw the shape
            shape.moveTo(-flangeWidth / 2, -beamDepth / 2);
            shape.lineTo(flangeWidth / 2, -beamDepth / 2);
            shape.lineTo(flangeWidth / 2, -beamDepth / 2 + flangeDepth - filletRadius);
            shape.quadraticCurveTo(flangeWidth / 2, -beamDepth / 2 + flangeDepth, flangeWidth / 2 - filletRadius, -beamDepth / 2 + flangeDepth);
            shape.lineTo(webThickness / 2 + filletRadius, -beamDepth / 2 + flangeDepth);
            shape.quadraticCurveTo(webThickness / 2, -beamDepth / 2 + flangeDepth, webThickness / 2, -beamDepth / 2 + flangeDepth + filletRadius);
            shape.lineTo(webThickness / 2, beamDepth / 2 - flangeDepth - filletRadius);
            shape.quadraticCurveTo(webThickness / 2, beamDepth / 2 - flangeDepth, webThickness / 2 + filletRadius, beamDepth / 2 - flangeDepth);
            shape.lineTo(flangeWidth / 2 - filletRadius, beamDepth / 2 - flangeDepth);
            shape.quadraticCurveTo(flangeWidth / 2, beamDepth / 2 - flangeDepth, flangeWidth / 2, beamDepth / 2);
            shape.lineTo(flangeWidth / 2, beamDepth / 2);
            shape.lineTo(-flangeWidth / 2, beamDepth / 2);
            shape.lineTo(-flangeWidth / 2, beamDepth / 2 - flangeDepth + filletRadius);
            shape.quadraticCurveTo(-flangeWidth / 2, beamDepth / 2 - flangeDepth, -flangeWidth / 2 + filletRadius, beamDepth / 2 - flangeDepth);
            shape.lineTo(-webThickness / 2 - filletRadius, beamDepth / 2 - flangeDepth);
            shape.quadraticCurveTo(-webThickness / 2, beamDepth / 2 - flangeDepth, -webThickness / 2, beamDepth / 2 - flangeDepth - filletRadius);
            shape.lineTo(-webThickness / 2, -beamDepth / 2 + flangeDepth + filletRadius);
            shape.quadraticCurveTo(-webThickness / 2, -beamDepth / 2 + flangeDepth, -webThickness / 2 - filletRadius, -beamDepth / 2 + flangeDepth);
            shape.lineTo(-flangeWidth / 2 + filletRadius, -beamDepth / 2 + flangeDepth);
            shape.quadraticCurveTo(-flangeWidth / 2, -beamDepth / 2 + flangeDepth, -flangeWidth / 2, -beamDepth / 2);

            const geometry = new THREE.ExtrudeGeometry(shape, {
                depth: extrusionLength,
                bevelEnabled: false,
            });

            const material = new THREE.MeshStandardMaterial({ color: 0x0077ff });
            const mesh = new THREE.Mesh(geometry, material);
            mesh.position.set(...position);
            mesh.updateMatrix();
            mesh.updateMatrixWorld();
            return mesh;
        }

       // Function to create a cylinder
        function createCylinder(x, y, z, radius, height) {
            const geometry = new THREE.CylinderGeometry(radius, radius, height, 32);
            const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
            const cylinder = new THREE.Mesh(geometry, material);
            cylinder.position.set(x, y , z+10);
            cylinder.updateMatrix();
            cylinder.updateMatrixWorld();
            return cylinder;
        }

        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            renderer.render(scene, camera);
        }
    </script>
</body>
</html>

https://manthrax.github.io/glider/csgtest.html

4 Likes