<!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>
4 Likes
