Thanks for the reply. I’ve attached the code as requested.
While it is quite comprehensive, this serves as a solid foundation. The plan is to begin with this and eventually integrate shape sizes stored in a database, allowing for easy retrieval. Additionally, we aim to incorporate preformatted ‘clip angles’ similar to those depicted in some of the attached images.
Some of the Channels or W shapes may feature holes on multiple surfaces, such as the flange or web. The primary idea is to enable users to position the first hole, then array the remaining holes with a specified on-center spacing.
For reference, you can explore the actual shape sizes and gauge specifications for the holes using the AISC Steel Detailing Tool. I’ve relied on this tool for years to configure steel shapes in AutoCAD.
Structural Steel Dimensioning Tool | American Institute of Steel Construction (aisc.org)
Here is an example of a connection with notches and holes in the clip angles or beams:
500px-C18-05.png (500×314) (steelconstruction.info)
The user should have the ability to adjust the gap between the notch and the shape it is intended to fit. The existing CSG method I’ve reviewed will not suffice, as it consistently creates a watertight fit, whereas the minimum gap typically required is 1/2 inch.
Miter and bevel cuts are likely the most straightforward to implement initially. This process involves extruding the shape and then making an angled cut after the extrusion, allowing the user to select the portion to retain.
Here is a link to miter and bevel cuts which also includes a few more examples:
3 Different Types of Miter Saws & Their Uses (with Pictures) | House Grail
These standard photos demonstrate the purpose and importance of properly positioning notches and holes, as you can see, some feature slots instead of holes.
Standard Clip Angles:
Simple Connection with clips and a notch:
The final photo shows a baseplate, essentially an extruded rectangle with holes, which is about the extent I can manage before the code breaks. I can successfully create code that places holes in a shape before extrusion, but my challenge lies in adding holes after extrusion using CSG or a comparable method.
The baseplate code is located below this section, and when viewed in wireframe (which I have currently disabled), it becomes clear that the geometry is incorrect. The wireframe jumps erratically between holes, creating an unusual pattern. For now, I’ve removed the wireframe material while I work to identify the cause of this issue.
I plan on integrating a better GUI but this is feasible for now until I get all of the features required built and configured into the control panel on the right… maybe even adding tabulation or command input later down the road to keep everything in this basic layout.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Steel Drawing Tool</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<style>
body {
margin: 0;
height: 100vh;
display: flex;
background-color: #1a1a1a;
}
#container {
width: 80%; /* Takes up 80% of the width */
display: flex;
justify-content: center;
align-items: center;
background-color: #282828;
}
#controls {
width: 20%; /* Takes up 20% of the width */
padding: 10px;
background-color: #333;
color: white;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
box-sizing: border-box;
overflow-y: auto;
}
label {
margin: 5px 0;
font-size: 0.9em;
}
select, input[type="number"], button {
margin-bottom: 10px;
padding: 5px;
}
button {
background-color: #555;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #777;
}
.shape-list {
margin-top: 20px;
width: 100%;
background-color: #444;
padding: 2px;
border-radius: 5px;
}
.shape-item {
display: flex;
flex-direction: column;
padding: 2px;
border-bottom: 1px solid #555;
}
.shape-item:last-child {
border-bottom: none;
}
.shape-item input {
width: calc(100% - 10px);
margin: 2px 0;
}
.position-container {
display: flex; /* Flexbox for side by side */
justify-content: space-between; /* Space between inputs */
margin-bottom: 10px; /* Space below the position container */
}
.position-input, .extrusion-input {
width: 30px; /* Width for each input */
}
</style>
</head>
<body>
<div id="container">
<!-- Main content where the 3D scene will be rendered -->
</div>
<div id="controls">
<label for="steelShape">Steel Shape:</label>
<select id="steelShape" onchange="updateSteelSizes()">
<option value="" disabled selected>Select Shape</option>
<option value="wideFlange">Wide Flange</option>
<option value="channel">Channel</option>
<option value="tee">Tee</option>
<option value="angle">Angle</option>
<option value="rectHSS">Rectangular HSS</option>
<option value="squareHSS">Square HSS</option>
<option value="pipe">Pipe</option>
</select>
<label for="steelSize">Steel Size:</label>
<select id="steelSize">
<option value="" disabled selected>Select Size</option>
</select>
<label>Position:</label>
<div class="position-container">
<input type="number" id="xPosition" class="position-input" value="0" step="1" placeholder="X">
<input type="number" id="yPosition" class="position-input" value="0" step="1" placeholder="Y">
<input type="number" id="zPosition" class="position-input" value="0" step="1" placeholder="Z">
</div>
<label for="extrusionLength">Extrusion Length:</label>
<input type="number" id="extrusionLength" class="extrusion-input" value="1" step="0.1">
<button id="drawSteelButton" onclick="drawSteel()">Draw Steel</button>
<div class="shape-list" id="shapeList"></div>
</div>
<!-- Three.js Libraries -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128/examples/js/controls/OrbitControls.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
camera.position.set(0, 100, 200);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 100, 10);
scene.add(light);
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const steelShapes = {
wideFlange: ['W8x10', 'W10x15', 'W12x22'],
channel: ['C6x8.2', 'C8x11.5', 'C10x15.3'],
tee: ['T6x8.2', 'T8x11.5', 'T10x15.3'],
angle: ['L3x3x1/4', 'L4x4x1/4', 'L5x5x1/2'],
rectHSS: ['HSS3x2x1/4', 'HSS4x2x1/4', 'HSS5x2x1/2'],
squareHSS: ['HSS3x3x1/4', 'HSS4x4x1/4', 'HSS5x5x1/2'],
pipe: ['Pipe1', 'Pipe1-1/4', 'Pipe1-1/2']
};
const wideFlangeSizes = {
"W8x10": [8, 4, 0.3, 0.4],
"W10x15": [10, 5, 0.4, 0.5],
"W12x22": [12, 6, 0.5, 0.6]
};
const channelSizes = {
"C6x8.2": [6, 3, 0.25, 0.35],
"C8x11.5": [8, 4, 0.35, 0.45],
"C10x15.3": [10, 5, 0.45, 0.55]
};
const teeSizes = {
"T6x8.2": [6, 3, 0.25, 0.35],
"T8x11.5": [8, 4, 0.35, 0.45],
"T10x15.3": [10, 5, 0.45, 0.55]
};
const angleSizes = {
"L3x3x1/4": [3, 0.25],
"L4x4x1/4": [4, 0.25],
"L5x5x1/2": [5, 0.5]
};
const rectHSSSizes = {
"HSS3x2x1/4": [3, 2, 0.25],
"HSS4x2x1/4": [4, 2, 0.25],
"HSS5x2x1/2": [5, 2, 0.5]
};
const squareHSSSizes = {
"HSS3x3x1/4": [3, 3, 0.25],
"HSS4x4x1/4": [4, 4, 0.25],
"HSS5x5x1/2": [5, 5, 0.5]
};
const pipeSizes = {
"Pipe1": [1, 0.1875],
"Pipe1-1/4": [1.25, 0.25],
"Pipe1-1/2": [1.5, 0.3]
};
const shapesAdded = [];
function updateSteelSizes() {
const shapeSelect = document.getElementById('steelShape').value;
const sizeSelect = document.getElementById('steelSize');
sizeSelect.innerHTML = '<option value="" disabled selected>Select Size</option>';
if (shapeSelect && steelShapes[shapeSelect]) {
steelShapes[shapeSelect].forEach(size => {
const option = document.createElement('option');
option.value = size;
option.text = size;
sizeSelect.add(option);
});
}
}
function drawSteel() {
const selectedShape = document.getElementById('steelShape').value;
const selectedSize = document.getElementById('steelSize').value;
const xPos = parseFloat(document.getElementById('xPosition').value);
const yPos = parseFloat(document.getElementById('yPosition').value);
const zPos = parseFloat(document.getElementById('zPosition').value);
const extrusionLength = parseFloat(document.getElementById('extrusionLength').value);
if (!selectedShape || !selectedSize) {
alert('Please select both a shape and size.');
return;
}
if (isNaN(extrusionLength) || extrusionLength <= 0) {
alert('Please enter a valid extrusion length greater than 0.');
return;
}
let shapeMesh;
let hasHoles = false; // Flag to indicate if the shape has holes
switch (selectedShape) {
case 'wideFlange':
shapeMesh = createWideFlange(selectedSize, [xPos, yPos, zPos], extrusionLength);
break;
case 'channel':
shapeMesh = createChannel(selectedSize, [xPos, yPos, zPos], extrusionLength);
break;
case 'tee':
shapeMesh = createTee(selectedSize, [xPos, yPos, zPos], extrusionLength);
break;
case 'angle':
shapeMesh = createAngle(selectedSize, [xPos, yPos, zPos], extrusionLength);
break;
case 'rectHSS':
shapeMesh = createRectHSS(selectedSize, [xPos, yPos, zPos], extrusionLength);
hasHoles = true; // RectHSS has holes
break;
case 'squareHSS':
shapeMesh = createSquareHSS(selectedSize, [xPos, yPos, zPos], extrusionLength);
hasHoles = true; // SquareHSS has holes
break;
case 'pipe':
shapeMesh = createPipe(selectedSize, [xPos, yPos, zPos], extrusionLength);
hasHoles = true; // Pipe has holes
break;
default:
alert('Invalid shape selected.');
return;
}
shapesAdded.push({
shape: selectedShape,
size: selectedSize,
position: [xPos, yPos, zPos],
extrusion: extrusionLength,
rotation: [0, 0, 0], // Initialize rotation values
mesh: shapeMesh,
hasHoles: hasHoles // Add hasHoles property
});
updateShapeList();
animate();
}
function updateShapeList() {
const shapeListDiv = document.getElementById('shapeList');
shapeListDiv.innerHTML = '';
shapesAdded.forEach((shape, index) => {
const itemDiv = document.createElement('div');
itemDiv.className = 'shape-item';
// Create a textbox for shape text
const shapeTextBox = document.createElement('input');
shapeTextBox.type = 'text';
shapeTextBox.value = `${shape.size}`;
shapeTextBox.readOnly = true;
shapeTextBox.style.fontFamily = 'Arial';
shapeTextBox.style.fontSize = '12px';
shapeTextBox.style.height = '20px';
shapeTextBox.style.marginRight = '5px';
// Create a container for position inputs and extrusion length
const positionContainer = document.createElement('div');
positionContainer.className = 'input-container';
positionContainer.style.display = 'flex';
// X Position Input
const xInput = document.createElement('input');
xInput.type = 'number';
xInput.value = shape.position[0];
xInput.oninput = (e) => updatePosition(index, 0, parseFloat(e.target.value));
xInput.style.width = '30px';
xInput.style.height = '12px';
xInput.style.fontSize = '10px';
positionContainer.appendChild(xInput);
// Y Position Input
const yInput = document.createElement('input');
yInput.type = 'number';
yInput.value = shape.position[1];
yInput.oninput = (e) => updatePosition(index, 1, parseFloat(e.target.value));
yInput.style.width = '30px';
yInput.style.height = '12px';
yInput.style.fontSize = '10px';
positionContainer.appendChild(yInput);
// Z Position Input
const zInput = document.createElement('input');
zInput.type = 'number';
zInput.value = shape.position[2];
zInput.oninput = (e) => updatePosition(index, 2, parseFloat(e.target.value));
zInput.style.width = '30px';
zInput.style.height = '12px';
zInput.style.fontSize = '10px';
positionContainer.appendChild(zInput);
// Extrusion Length Input
const extrusionInput = document.createElement('input');
extrusionInput.type = 'number';
extrusionInput.value = shape.extrusion;
extrusionInput.oninput = (e) => updateExtrusion(index, parseFloat(e.target.value));
extrusionInput.style.width = '30px';
extrusionInput.style.height = '12px';
extrusionInput.style.fontSize = '10px';
extrusionInput.style.marginLeft = '5px';
positionContainer.appendChild(extrusionInput);
// Delete Button
const deleteButton = document.createElement('button');
deleteButton.innerHTML = '<i class="fa fa-times"></i>';
deleteButton.onclick = () => deleteShape(index);
deleteButton.style.width = '20px';
deleteButton.style.height = '20px';
deleteButton.style.marginTop = '5px';
deleteButton.style.marginLeft = '5px';
deleteButton.style.fontSize = '10px';
deleteButton.style.cursor = 'pointer';
deleteButton.style.backgroundColor = '#ff4d4d';
deleteButton.style.color = '#fff';
deleteButton.style.border = 'none';
deleteButton.style.borderRadius = '1px';
positionContainer.appendChild(deleteButton);
// Add position container to itemDiv
itemDiv.appendChild(shapeTextBox);
itemDiv.appendChild(positionContainer);
// Create a container for rotation inputs
const rotationContainer = document.createElement('div');
rotationContainer.className = 'input-container';
rotationContainer.style.display = 'flex';
// Rotation Inputs
const xRotationInput = createRotationInput('X', shape.rotation[0], index, 0);
const yRotationInput = createRotationInput('Y', shape.rotation[1], index, 1);
const zRotationInput = createRotationInput('Z', shape.rotation[2], index, 2);
rotationContainer.appendChild(xRotationInput);
rotationContainer.appendChild(yRotationInput);
rotationContainer.appendChild(zRotationInput);
// Rotation Button
const rotationButton = document.createElement('button');
rotationButton.innerHTML = '<i class="fa fa-sync-alt"></i>';
rotationButton.disabled = true; // Makes the button unclickable
rotationButton.style.width = '20px';
rotationButton.style.height = '20px';
rotationButton.style.marginLeft = '5px';
rotationButton.style.fontSize = '10px';
rotationButton.style.backgroundColor = 'transparent'; // Makes it clear
rotationButton.style.color = 'black'; // Optional: set text color
rotationButton.style.border = '1px solid transparent'; // Optional: clear border
rotationContainer.appendChild(rotationButton);
// Add rotation container to itemDiv
itemDiv.appendChild(rotationContainer);
// Add itemDiv to the shape list
shapeListDiv.appendChild(itemDiv);
});
}
function createRotationInput(axis, value, index, rotationIndex) {
const rotationInput = document.createElement('input');
rotationInput.type = 'number';
rotationInput.value = value;
rotationInput.placeholder = `${axis} Rotation`;
rotationInput.oninput = (e) => updateRotation(index, rotationIndex, parseFloat(e.target.value));
rotationInput.style.width = '40px';
rotationInput.style.height = '12px';
rotationInput.style.fontSize = '10px';
rotationInput.style.marginLeft = '5px';
return rotationInput;
}
function updateRotation(index, rotationIndex, value) {
if (isNaN(value)) return;
shapesAdded[index].rotation[rotationIndex] = value;
rotateShape(index);
}
function rotateShape(index) {
const shape = shapesAdded[index];
shape.mesh.rotation.set(
THREE.MathUtils.degToRad(shape.rotation[0]),
THREE.MathUtils.degToRad(shape.rotation[1]),
THREE.MathUtils.degToRad(shape.rotation[2])
);
animate(); // Refresh the animation/render
}
function createWideFlange(sizeLabel, position, extrusionLength) {
const [beamDepth, flangeWidth, webThickness, flangeDepth] = wideFlangeSizes[sizeLabel];
const shape = new THREE.Shape();
const filletRadius = flangeDepth / 2;
// Start drawing the shape
shape.moveTo(-flangeWidth / 2, -beamDepth / 2); // Start point
shape.lineTo(flangeWidth / 2, -beamDepth / 2); // Top horizontal line
shape.lineTo(flangeWidth / 2, -beamDepth / 2 + flangeDepth - filletRadius); // Top right vertical line
shape.quadraticCurveTo(flangeWidth / 2, -beamDepth / 2 + flangeDepth, flangeWidth / 2 - filletRadius, -beamDepth / 2 + flangeDepth); // Top right arc
shape.lineTo(webThickness / 2 + filletRadius, -beamDepth / 2 + flangeDepth); // Right web line
shape.quadraticCurveTo(webThickness / 2, -beamDepth / 2 + flangeDepth, webThickness / 2, -beamDepth / 2 + flangeDepth + filletRadius); // Right web arc
shape.lineTo(webThickness / 2, beamDepth / 2 - flangeDepth - filletRadius); // Bottom right vertical line
shape.quadraticCurveTo(webThickness / 2, beamDepth / 2 - flangeDepth, webThickness / 2 + filletRadius, beamDepth / 2 - flangeDepth); // Bottom right arc
shape.lineTo(flangeWidth / 2 - filletRadius, beamDepth / 2 - flangeDepth); // Bottom right horizontal line
shape.quadraticCurveTo(flangeWidth / 2, beamDepth / 2 - flangeDepth, flangeWidth / 2, beamDepth / 2); // Bottom right arc
shape.lineTo(flangeWidth / 2, beamDepth / 2); // Bottom right vertical line
shape.lineTo(-flangeWidth / 2, beamDepth / 2); // Bottom left horizontal line
shape.lineTo(-flangeWidth / 2, beamDepth / 2 - flangeDepth + filletRadius); // Bottom left vertical line
shape.quadraticCurveTo(-flangeWidth / 2, beamDepth / 2 - flangeDepth, -flangeWidth / 2 + filletRadius, beamDepth / 2 - flangeDepth); // Bottom left arc
shape.lineTo(-webThickness / 2 - filletRadius, beamDepth / 2 - flangeDepth); // Left web line
shape.quadraticCurveTo(-webThickness / 2, beamDepth / 2 - flangeDepth, -webThickness / 2, beamDepth / 2 - flangeDepth - filletRadius); // Left web arc
shape.lineTo(-webThickness / 2, -beamDepth / 2 + flangeDepth + filletRadius); // Top left vertical line
shape.quadraticCurveTo(-webThickness / 2, -beamDepth / 2 + flangeDepth, -webThickness / 2 - filletRadius, -beamDepth / 2 + flangeDepth); // Top left arc
shape.lineTo(-flangeWidth / 2 + filletRadius, -beamDepth / 2 + flangeDepth); // Top left horizontal line
shape.quadraticCurveTo(-flangeWidth / 2, -beamDepth / 2 + flangeDepth, -flangeWidth / 2, -beamDepth / 2); // Top left arc
const geometry = new THREE.ExtrudeGeometry(shape, {
depth: extrusionLength,
bevelEnabled: false,
});
const material = new THREE.MeshStandardMaterial({ color: 0x808080 });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(...position);
scene.add(mesh);
return mesh;
}
function createChannel(sizeLabel, position, extrusionLength) {
const [beamDepth, flangeWidth, webThickness, flangeDepth] = channelSizes[sizeLabel];
const shape = new THREE.Shape();
const filletRadius = flangeDepth / 2;
// Start drawing the shape
shape.moveTo(-flangeWidth / 2, -beamDepth / 2); // Start point
// Left side
shape.lineTo(-flangeWidth / 2, beamDepth / 2); // Vertical line
shape.lineTo(flangeWidth / 2, beamDepth / 2); // Bottom horizontal line
// Bottom right arc
shape.lineTo(flangeWidth / 2, beamDepth / 2 - flangeDepth + filletRadius);
shape.quadraticCurveTo(
flangeWidth / 2,
beamDepth / 2 - flangeDepth,
flangeWidth / 2 - filletRadius,
beamDepth / 2 - flangeDepth
);
// Bottom web line
shape.lineTo(-flangeWidth / 2 + webThickness + filletRadius, beamDepth / 2 - flangeDepth);
// Bottom left arc
shape.quadraticCurveTo(
-flangeWidth / 2 + webThickness,
beamDepth / 2 - flangeDepth,
-flangeWidth / 2 + webThickness,
beamDepth / 2 - flangeDepth - filletRadius
);
// Left web line
shape.lineTo(-flangeWidth / 2 + webThickness, -beamDepth / 2 + flangeDepth + filletRadius);
// Top left arc
shape.quadraticCurveTo(
-flangeWidth / 2 + webThickness,
-beamDepth / 2 + flangeDepth,
-flangeWidth / 2 + webThickness + filletRadius,
-beamDepth / 2 + flangeDepth
);
// Top web line
shape.lineTo(flangeWidth / 2 - filletRadius, -beamDepth / 2 + flangeDepth);
// Top right arc
shape.quadraticCurveTo(
flangeWidth / 2,
-beamDepth / 2 + flangeDepth,
flangeWidth / 2,
-beamDepth / 2 + flangeDepth - filletRadius
);
// Top right vertical line
shape.lineTo(flangeWidth / 2, -beamDepth / 2);
// Close the shape automatically
shape.autoClose = true;
const geometry = new THREE.ExtrudeGeometry(shape, {
depth: extrusionLength,
bevelEnabled: false,
});
const material = new THREE.MeshStandardMaterial({ color: 0x808080 });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(...position);
scene.add(mesh);
return mesh;
}
function createTee(sizeLabel, position, extrusionLength) {
const [beamDepth, flangeWidth, webThickness, flangeDepth] = teeSizes[sizeLabel]; // Assuming teeSizes is defined
const shape = new THREE.Shape();
const filletRadius = flangeDepth / 2;
// Start drawing the Tee shape
shape.moveTo(-flangeWidth / 2, -beamDepth / 2); // Top-left corner
// Top horizontal line
shape.lineTo(flangeWidth / 2, -beamDepth / 2);
// Right flange corner (rounded)
shape.lineTo(flangeWidth / 2, -beamDepth / 2 + webThickness - filletRadius);
shape.quadraticCurveTo(
flangeWidth / 2,
-beamDepth / 2 + webThickness,
flangeWidth / 2 - filletRadius,
-beamDepth / 2 + webThickness
);
// Right side of flange
shape.lineTo(webThickness / 2 + filletRadius, -beamDepth / 2 + webThickness);
// Right-top corner at Web (rounded)
shape.quadraticCurveTo(
webThickness / 2,
-beamDepth / 2 + webThickness,
webThickness / 2,
-beamDepth / 2 + webThickness + filletRadius
);
// Right vertical line down
shape.lineTo(webThickness / 2, beamDepth / 2 - filletRadius);
// Right-bottom corner (rounded)
shape.quadraticCurveTo(
webThickness / 2,
beamDepth / 2,
webThickness / 2 - filletRadius,
beamDepth / 2
);
// Bottom horizontal line
shape.lineTo(-webThickness / 2 + filletRadius, beamDepth / 2);
// Bottom-left corner (rounded)
shape.quadraticCurveTo(
-webThickness / 2,
beamDepth / 2,
-webThickness / 2,
beamDepth / 2 - filletRadius
);
// Left vertical line up
shape.lineTo(-webThickness / 2, -beamDepth / 2 + webThickness + filletRadius);
// Left-top corner at Web (rounded)
shape.quadraticCurveTo(
-webThickness / 2,
-beamDepth / 2 + webThickness,
-webThickness / 2 - filletRadius,
-beamDepth / 2 + webThickness
);
// Left side of flange
shape.lineTo(-flangeWidth / 2 + filletRadius, -beamDepth / 2 + webThickness);
// Left flange corner (rounded)
shape.quadraticCurveTo(
-flangeWidth / 2,
-beamDepth / 2 + webThickness,
-flangeWidth / 2,
-beamDepth / 2 + webThickness - filletRadius
);
const geometry = new THREE.ExtrudeGeometry(shape, {
depth: extrusionLength,
bevelEnabled: false,
});
const material = new THREE.MeshStandardMaterial({ color: 0x808080 });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(...position);
scene.add(mesh);
return mesh;
}
function createAngle(sizeLabel, position, extrusionLength) {
const [legLength, thickness] = angleSizes[sizeLabel]; // Assuming angleSizes is defined
const shape = new THREE.Shape();
const filletRadius = thickness / 2;
// Start drawing the angle shape at the insertion point
shape.moveTo(0, 0); // Insertion point
// Draw the first leg
shape.lineTo(legLength, 0);
// Draw the rounded corner
shape.lineTo(legLength, -filletRadius);
shape.quadraticCurveTo(
legLength,
-thickness,
legLength - filletRadius,
-thickness
);
// Draw the horizontal line to the second corner
shape.lineTo(thickness + filletRadius, -thickness);
shape.quadraticCurveTo(
thickness,
-thickness,
thickness,
-thickness - filletRadius
);
// Draw the vertical line to the third corner
shape.lineTo(thickness, -legLength + filletRadius);
shape.quadraticCurveTo(
thickness,
-legLength,
thickness - filletRadius,
-legLength
);
// Draw the final vertical line down to the last point
shape.lineTo(0, -legLength);
// Close the path
shape.autoClose = true;
const geometry = new THREE.ExtrudeGeometry(shape, {
depth: extrusionLength,
bevelEnabled: false,
});
const material = new THREE.MeshStandardMaterial({ color: 0x808080 });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(...position);
scene.add(mesh);
return mesh;
}
function createRectHSS(sizeLabel, position, extrusionLength) {
// Get dimensions from rectHSSSizes
const [sideLength, sideWidth, wallThickness] = rectHSSSizes[sizeLabel];
const outerWidth = sideLength;
const outerHeight = sideWidth;
const innerWidth = outerWidth - 2 * wallThickness;
const innerHeight = outerHeight - 2 * wallThickness;
const filletRadius = wallThickness * 2; // Fillet radius
const innerFilletRadius = filletRadius - wallThickness; // Inner Fillet radius
// Create outer shape with fillets
const outerShape = new THREE.Shape();
outerShape.moveTo(outerWidth / 2, -outerHeight / 2 + filletRadius);
outerShape.lineTo(outerWidth / 2, outerHeight / 2 - filletRadius);
outerShape.absarc(outerWidth / 2 - filletRadius, outerHeight / 2 - filletRadius, filletRadius, 0, Math.PI / 2, false);
outerShape.lineTo(-outerWidth / 2 + filletRadius, outerHeight / 2);
outerShape.absarc(-outerWidth / 2 + filletRadius, outerHeight / 2 - filletRadius, filletRadius, Math.PI / 2, Math.PI, false);
outerShape.lineTo(-outerWidth / 2, -outerHeight / 2 + filletRadius);
outerShape.absarc(-outerWidth / 2 + filletRadius, -outerHeight / 2 + filletRadius, filletRadius, Math.PI, (3 * Math.PI) / 2, false);
outerShape.lineTo(outerWidth / 2 - filletRadius, -outerHeight / 2);
outerShape.absarc(outerWidth / 2 - filletRadius, -outerHeight / 2 + filletRadius, filletRadius, (3 * Math.PI) / 2, 0, false);
outerShape.closePath();
// Create inner shape with fillets (hollow)
const innerShape = new THREE.Path(); // Changed from THREE.Shape to THREE.Path
innerShape.moveTo(innerWidth / 2, -innerHeight / 2 + innerFilletRadius);
innerShape.lineTo(innerWidth / 2, innerHeight / 2 - innerFilletRadius);
innerShape.absarc(innerWidth / 2 - innerFilletRadius, innerHeight / 2 - innerFilletRadius, innerFilletRadius, 0, Math.PI / 2, false);
innerShape.lineTo(-innerWidth / 2 + innerFilletRadius, innerHeight / 2);
innerShape.absarc(-innerWidth / 2 + innerFilletRadius, innerHeight / 2 - innerFilletRadius, innerFilletRadius, Math.PI / 2, Math.PI, false);
innerShape.lineTo(-innerWidth / 2, -innerHeight / 2 + innerFilletRadius);
innerShape.absarc(-innerWidth / 2 + innerFilletRadius, -innerHeight / 2 + innerFilletRadius, innerFilletRadius, Math.PI, (3 * Math.PI) / 2, false);
innerShape.lineTo(innerWidth / 2 - innerFilletRadius, -innerHeight / 2);
innerShape.absarc(innerWidth / 2 - innerFilletRadius, -innerHeight / 2 + innerFilletRadius, innerFilletRadius, (3 * Math.PI) / 2, 0, false);
innerShape.closePath();
// Add the inner shape as a hole to the outer shape
outerShape.holes.push(innerShape); // Use the inner shape directly
const geometry = new THREE.ExtrudeGeometry(outerShape, {
depth: extrusionLength,
bevelEnabled: false,
});
const material = new THREE.MeshStandardMaterial({ color: 0x808080 });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(...position);
scene.add(mesh);
return mesh;
}
function createSquareHSS(sizeLabel, position, extrusionLength) {
// Get dimensions from squareHSSSizes
const [sideLength, sideWidth, wallThickness] = squareHSSSizes[sizeLabel];
const outerWidth = sideLength;
const outerHeight = sideWidth;
const innerWidth = outerWidth - 2 * wallThickness;
const innerHeight = outerHeight - 2 * wallThickness;
const filletRadius = wallThickness * 2; // Fillet radius
const innerFilletRadius = filletRadius - wallThickness; // Inner Fillet radius
// Create outer shape with fillets
const outerShape = new THREE.Shape();
outerShape.moveTo(outerWidth / 2, -outerHeight / 2 + filletRadius);
outerShape.lineTo(outerWidth / 2, outerHeight / 2 - filletRadius);
outerShape.absarc(outerWidth / 2 - filletRadius, outerHeight / 2 - filletRadius, filletRadius, 0, Math.PI / 2, false);
outerShape.lineTo(-outerWidth / 2 + filletRadius, outerHeight / 2);
outerShape.absarc(-outerWidth / 2 + filletRadius, outerHeight / 2 - filletRadius, filletRadius, Math.PI / 2, Math.PI, false);
outerShape.lineTo(-outerWidth / 2, -outerHeight / 2 + filletRadius);
outerShape.absarc(-outerWidth / 2 + filletRadius, -outerHeight / 2 + filletRadius, filletRadius, Math.PI, (3 * Math.PI) / 2, false);
outerShape.lineTo(outerWidth / 2 - filletRadius, -outerHeight / 2);
outerShape.absarc(outerWidth / 2 - filletRadius, -outerHeight / 2 + filletRadius, filletRadius, (3 * Math.PI) / 2, 0, false);
outerShape.closePath();
// Create inner shape with fillets (hollow)
const innerShape = new THREE.Path(); // Changed from THREE.Shape to THREE.Path
innerShape.moveTo(innerWidth / 2, -innerHeight / 2 + innerFilletRadius);
innerShape.lineTo(innerWidth / 2, innerHeight / 2 - innerFilletRadius);
innerShape.absarc(innerWidth / 2 - innerFilletRadius, innerHeight / 2 - innerFilletRadius, innerFilletRadius, 0, Math.PI / 2, false);
innerShape.lineTo(-innerWidth / 2 + innerFilletRadius, innerHeight / 2);
innerShape.absarc(-innerWidth / 2 + innerFilletRadius, innerHeight / 2 - innerFilletRadius, innerFilletRadius, Math.PI / 2, Math.PI, false);
innerShape.lineTo(-innerWidth / 2, -innerHeight / 2 + innerFilletRadius);
innerShape.absarc(-innerWidth / 2 + innerFilletRadius, -innerHeight / 2 + innerFilletRadius, innerFilletRadius, Math.PI, (3 * Math.PI) / 2, false);
innerShape.lineTo(innerWidth / 2 - innerFilletRadius, -innerHeight / 2);
innerShape.absarc(innerWidth / 2 - innerFilletRadius, -innerHeight / 2 + innerFilletRadius, innerFilletRadius, (3 * Math.PI) / 2, 0, false);
innerShape.closePath();
// Add the inner shape as a hole to the outer shape
outerShape.holes.push(innerShape); // Use the inner shape directly
const geometry = new THREE.ExtrudeGeometry(outerShape, {
depth: extrusionLength,
bevelEnabled: false,
});
const material = new THREE.MeshStandardMaterial({ color: 0x808080 });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(...position);
scene.add(mesh);
return mesh;
}
function createPipe(sizeLabel, position, extrusionLength) {
// Get dimensions from pipeSizes
const [outerDiameter, wallThickness] = pipeSizes[sizeLabel];
const innerDiameter = outerDiameter - wallThickness * 2;
// Create outer shape (circle)
const outerShape = new THREE.Shape();
outerShape.absarc(0, 0, outerDiameter / 2, 0, Math.PI * 2, false); // Create outer circle
// Create inner shape (circle)
const innerShape = new THREE.Path();
innerShape.absarc(0, 0, innerDiameter / 2, 0, Math.PI * 2, false); // Create inner circle
// Add the inner shape as a hole to the outer shape
outerShape.holes.push(innerShape); // Use the inner shape directly
const geometry = new THREE.ExtrudeGeometry(outerShape, {
depth: extrusionLength,
bevelEnabled: false,
});
const material = new THREE.MeshStandardMaterial({ color: 0x808080 });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(...position);
scene.add(mesh);
return mesh;
}
function updatePosition(index, axis, value) {
if (isNaN(value)) return;
shapesAdded[index].position[axis] = value;
shapesAdded[index].mesh.position.set(...shapesAdded[index].position);
animate(); // Refresh the animation/render
}
function updateExtrusion(index, value) {
if (isNaN(value) || value <= 0) return;
shapesAdded[index].extrusion = value;
// Redraw the shape with the updated extrusion length
let shapeMesh;
if (shapesAdded[index].shape === 'wideFlange') {
shapeMesh = createWideFlange(shapesAdded[index].size, shapesAdded[index].position, value);
} else if (shapesAdded[index].shape === 'channel') {
shapeMesh = createChannel(shapesAdded[index].size, shapesAdded[index].position, value);
} else if (shapesAdded[index].shape === 'tee') {
shapeMesh = createTee(shapesAdded[index].size, shapesAdded[index].position, value);
} else if (shapesAdded[index].shape === 'angle') {
shapeMesh = createAngle(shapesAdded[index].size, shapesAdded[index].position, value);
} else if (shapesAdded[index].shape === 'rectHSS') {
shapeMesh = createRectHSS(shapesAdded[index].size, shapesAdded[index].position, value);
} else if (shapesAdded[index].shape === 'squareHSS') {
shapeMesh = createSquareHSS(shapesAdded[index].size, shapesAdded[index].position, value);
} else if (shapesAdded[index].shape === 'pipe') {
shapeMesh = createPipe(shapesAdded[index].size, shapesAdded[index].position, value);
}
// Clean up the old mesh
scene.remove(shapesAdded[index].mesh);
shapesAdded[index].mesh.geometry.dispose(); // Dispose of the old geometry
shapesAdded[index].mesh = shapeMesh; // Update mesh reference
scene.add(shapeMesh); // Add new mesh to the scene
animate(); // Refresh the animation/render
}
function deleteShape(index) {
const shape = shapesAdded[index];
scene.remove(shape.mesh);
shapesAdded.splice(index, 1);
updateShapeList();
animate(); // Refresh the animation/render
}
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>
And here is the code for the baseplates:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Polygon with Cylinder in Between</title>
<style>
body {
margin: 0;
overflow: hidden;
display: flex;
height: 100vh;
background-color: #1a1a1a;
}
#container {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
#controls {
padding: 5px;
background-color: #333;
color: white;
display: flex;
position: absolute;
bottom: 10px;
width: 100%;
justify-content: center;
}
input, button{
width: 50px;
padding: 5px;
margin-right: 10px;
background-color: #555;
color: white;
border: none;
border-radius: 5px;
}
button, select {
padding: 5px;
margin-right: 10px;
background-color: #555;
color: white;
border: none;
border-radius: 5px;
}
button:hover, select:hover {
background-color: #777;
}
#materialSelect {
position: absolute;
top: 10px;
right: 10px;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="controls">
<!-- Shape Selection -->
<select id="shapeSelect" onchange="switchShape()">
<option value="polygonal">Polygonal</option>
<option value="rectangular">Rectangular</option>
<option value="circular">Circular</option>
</select>
<!-- Circlular Controls -->
<div id="circularControls">
<input type="number" id="numHoles" placeholder="Number of holes" min="1" value="4">
<input type="number" id="circleDiameter" placeholder="Circle diameter" min="1" value="100">
<input type="number" id="extrudedCircleDiameter" placeholder="Extruded circle diameter" min="1" value="50">
</div>
<!-- Polygonal Controls -->
<div id="polygonControls">
<input type="number" id="sides" placeholder="Number of sides" min="3" value="4">
<input type="number" id="polygonDiameter" placeholder="Polygon diameter" min="1" value="100">
<input type="number" id="extrudedDiameter" placeholder="Extruded polygon diameter" min="1" value="50">
</div>
<!-- Common Controls (both Polygonal and Rectangular) -->
<div id="commonControls">
<input type="number" id="holeDiameter" placeholder="Hole diameter" min="1" value="10">
<input type="number" id="distance" placeholder="Distance from corners" min="1" value="10">
<input type="number" id="extrusionAmount" placeholder="Extrusion amount" min="1" value="10">
</div>
<!-- Rectangular Controls -->
<div id="rectangularControls" style="display: none;">
<input type="number" id="rectLength" placeholder="Length" min="1" value="100">
<input type="number" id="rectWidth" placeholder="Width" min="1" value="50">
<input type="number" id="holeColumns" placeholder="Columns" min="2" value="2">
<input type="number" id="holeRows" placeholder="Rows" min="2" value="2">
</div>
<!-- Update Button -->
<button onclick="updateShape()">Update Shape</button>
</div>
<select id="materialSelect" onchange="updateMaterial()">
<option value="basic">Basic Material</option>
<option value="wireframe">Wireframe</option>
<option value="metallic">Metallic</option>
</select>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128/examples/js/controls/OrbitControls.js"></script>
<script>
let scene, camera, renderer, controls;
let polygonMesh, holes = [];
let extrusionMesh, extrudedPolygonMesh;
let cylinderMesh;
let rectangularMesh;
let circularMesh;
let materialType = 'basic';
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 150;
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(0, 1, 1).normalize();
scene.add(directionalLight);
updatePolygonAndHoles();
animate();
}
function createPolygonVertices(sides, diameter) {
const vertices = [];
const radius = diameter / 2;
for (let i = 0; i < sides; i++) {
const angle = (i / sides) * Math.PI * 2;
const x = radius * Math.cos(angle);
const y = radius * Math.sin(angle);
vertices.push(new THREE.Vector3(x, y, 0));
}
return vertices;
}
function createPolygonMesh(vertices) {
const geometry = new THREE.BufferGeometry().setFromPoints(vertices);
const material = new THREE.LineBasicMaterial({ color: 0x00ff00 });
return new THREE.LineLoop(geometry, material);
}
function createHole(radius) {
return new THREE.CircleGeometry(radius, 32);
}
function createExtrusionShape(vertices, holes, extrusionAmount) {
const shape = new THREE.Shape(vertices);
holes.forEach(hole => {
const holePath = new THREE.Path(); // Avoid naming conflict
holePath.absarc(hole.x, hole.y, hole.radius, 0, Math.PI * 2, false);
shape.holes.push(holePath);
});
const geometry = new THREE.ExtrudeGeometry(shape, { depth: extrusionAmount, bevelEnabled: false });
const material = getMaterial();
return new THREE.Mesh(geometry, material);
}
function createCylinder(baseDiameter, topDiameter, height, sides) {
const geometry = new THREE.CylinderGeometry(baseDiameter / 2, topDiameter / 2, height, sides);
const material = new THREE.MeshBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.5 });
return new THREE.Mesh(geometry, material);
}
function updatePolygonAndHoles() {
if (circularMesh) {
scene.remove(circularMesh);
}
if (polygonMesh) {
scene.remove(polygonMesh);
}
holes.forEach(hole => scene.remove(hole));
holes = [];
if (extrusionMesh) {
scene.remove(extrusionMesh);
}
if (extrudedPolygonMesh) {
scene.remove(extrudedPolygonMesh);
}
if (cylinderMesh) {
scene.remove(cylinderMesh);
}
if (rectangularMesh) {
scene.remove(rectangularMesh);
}
const sides = parseInt(document.getElementById('sides').value);
const polygonDiameter = parseInt(document.getElementById('polygonDiameter').value);
const extrudedDiameter = parseInt(document.getElementById('extrudedDiameter').value);
const holeDiameter = parseInt(document.getElementById('holeDiameter').value);
const distance = parseInt(document.getElementById('distance').value);
const extrusionAmount = parseInt(document.getElementById('extrusionAmount').value);
const vertices = createPolygonVertices(sides, polygonDiameter);
polygonMesh = createPolygonMesh(vertices);
const extrudedVertices = createPolygonVertices(sides, extrudedDiameter);
extrudedPolygonMesh = createPolygonMesh(extrudedVertices);
extrudedPolygonMesh.position.z = extrusionAmount;
const holeRadius = holeDiameter / 2;
const positions = vertices.map(vertex => {
const angle = Math.atan2(vertex.y, vertex.x);
return {
x: vertex.x - (distance * Math.cos(angle)),
y: vertex.y - (distance * Math.sin(angle)),
radius: holeRadius
};
});
positions.forEach(pos => {
const hole = createHole(pos.radius);
const holeMesh = new THREE.Mesh(hole, new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide, transparent: true, opacity: 0 }));
holeMesh.position.set(pos.x, pos.y, 0);
holes.push(holeMesh);
scene.add(holeMesh);
});
extrusionMesh = createExtrusionShape(vertices, positions, extrusionAmount);
scene.add(extrusionMesh);
const cylinderHeight = extrusionAmount;
cylinderMesh = createCylinder(extrudedDiameter, polygonDiameter, cylinderHeight, sides);
cylinderMesh.position.z = cylinderHeight / 2;
const rotationAngle = -(Math.PI / sides) * 2 + Math.PI / 2;
cylinderMesh.rotation.y = rotationAngle;
cylinderMesh.rotation.x = Math.PI / 2;
}
function updateCircularMesh() {
if (circularMesh) {
scene.remove(circularMesh);
}
if (polygonMesh) {
scene.remove(polygonMesh);
}
holes.forEach(hole => scene.remove(hole));
holes = [];
if (extrusionMesh) {
scene.remove(extrusionMesh);
}
if (extrudedPolygonMesh) {
scene.remove(extrudedPolygonMesh);
}
if (cylinderMesh) {
scene.remove(cylinderMesh);
}
if (rectangularMesh) {
scene.remove(rectangularMesh);
}
const numHoles = parseInt(document.getElementById('numHoles').value);
const circleDiameter = parseInt(document.getElementById('circleDiameter').value);
const extrudedCircleDiameter = parseInt(document.getElementById('extrudedCircleDiameter').value);
const holeDiameter = parseInt(document.getElementById('holeDiameter').value);
const distance = parseInt(document.getElementById('distance').value);
const extrusionAmount = parseInt(document.getElementById('extrusionAmount').value);
// Create the vertices for the main shape
const vertices = createPolygonVertices(32, circleDiameter);
polygonMesh = createPolygonMesh(vertices);
// Create the vertices for the extruded shape
const extrudedVertices = createPolygonVertices(32, extrudedCircleDiameter);
extrudedCircularMesh = createPolygonMesh(extrudedVertices);
extrudedCircularMesh.position.z = extrusionAmount;
// Calculate hole properties
const holeRadius = holeDiameter / 2;
// Calculate the positions of the holes evenly around the center
const positions = [];
for (let i = 0; i < numHoles; i++) {
const angle = (i * 2 * Math.PI) / numHoles; // Calculate angle for each hole
positions.push({
x: ((circleDiameter/2-distance) * Math.cos(angle)), // Position x
y: ((circleDiameter/2-distance) * Math.sin(angle)), // Position y
radius: holeRadius
});
}
// Create and add holes to the scene
positions.forEach(pos => {
const hole = createHole(pos.radius);
const holeMesh = new THREE.Mesh(hole, new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide, transparent: true, opacity: 0 }));
holeMesh.position.set(pos.x, pos.y, 0);
holes.push(holeMesh);
scene.add(holeMesh);
});
// Create and add the extrusion shape to the scene
circularMesh = createExtrusionShape(vertices, positions, extrusionAmount);
scene.add(circularMesh);
}
function createRectangularMesh(length, width, holeDiameter, holeColumns, holeRows, distance, extrusionAmount) {
const shape = new THREE.Shape();
// Define the four corners of the rectangle
const topLeft = { x: -length / 2, y: width / 2 };
const topRight = { x: length / 2, y: width / 2 };
const bottomLeft = { x: -length / 2, y: -width / 2 };
const bottomRight = { x: length / 2, y: -width / 2 };
// Create the rectangular shape
shape.moveTo(bottomLeft.x, bottomLeft.y);
shape.lineTo(bottomRight.x, bottomRight.y);
shape.lineTo(topRight.x, topRight.y);
shape.lineTo(topLeft.x, topLeft.y);
shape.closePath();
// Calculate hole positions based on the number of columns and rows
const holeRadius = holeDiameter / 2;
// Calculate spacing between holes
const spacingX = (length - distance * 2) / (holeColumns -1);
const spacingY = (width - distance * 2) / (holeRows - 1);
// Loop through rows and columns to place holes
for (let row = 0; row < holeRows; row++) {
for (let col = 0; col < holeColumns; col++) {
// Calculate the x position for the holes
const x = holeColumns === 1 ? 0 : -length / 2 + distance + (col * spacingX);
// Calculate the y position for the holes
const y = holeRows === 1 ? 0 : width / 2 - distance - (row * spacingY);
shape.holes.push(new THREE.Path().absarc(x, y, holeRadius, 0, Math.PI * 2, false));
}
}
// Create the extruded geometry
const geometry = new THREE.ExtrudeGeometry(shape, { depth: extrusionAmount, bevelEnabled: false });
const material = getMaterial();
return new THREE.Mesh(geometry, material);
}
function updateRectangularMesh() {
if (circularMesh) {
scene.remove(circularMesh);
}
if (rectangularMesh) {
scene.remove(rectangularMesh);
}
if (extrusionMesh) {
scene.remove(extrusionMesh);
}
if (cylinderMesh) {
scene.remove(cylinderMesh);
}
if (polygonMesh) {
scene.remove(polygonMesh);
}
if (extrudedPolygonMesh) {
scene.remove(extrudedPolygonMesh);
}
const length = parseInt(document.getElementById('rectLength').value);
const width = parseInt(document.getElementById('rectWidth').value);
const holeDiameter = parseInt(document.getElementById('holeDiameter').value);
const holeColumns = parseInt(document.getElementById('holeColumns').value);
const holeRows = parseInt(document.getElementById('holeRows').value);
const distance = parseInt(document.getElementById('distance').value);
const extrusionAmount = parseInt(document.getElementById('extrusionAmount').value);
rectangularMesh = createRectangularMesh(length, width, holeDiameter, holeColumns, holeRows, distance, extrusionAmount);
scene.add(rectangularMesh);
}
function getMaterial() {
if (materialType === 'wireframe') {
return new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
} else if (materialType === 'metallic') {
return new THREE.MeshStandardMaterial({ color: 0x888888, metalness: 1.0, roughness: 0.4 });
} else {
return new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.5 });
}
}
function switchShape() {
const shape = document.getElementById('shapeSelect').value;
const polygonControls = document.getElementById('polygonControls');
const rectangularControls = document.getElementById('rectangularControls');
const circularControls = document.getElementById('circularControls');
if (shape === 'rectangular') {
circularControls.style.display = 'none';
polygonControls.style.display = 'none';
rectangularControls.style.display = 'block';
}
else if (shape === 'circular') {
rectangularControls.style.display = 'none';
polygonControls.style.display = 'none';
circularControls.style.display = 'block';
}
else {
polygonControls.style.display = 'block';
rectangularControls.style.display = 'none';
circularControls.style.display = 'none';
}
}
function updateShape() {
const shape = document.getElementById('shapeSelect').value;
if (shape === 'rectangular') {
updateRectangularMesh();
}
else if (shape === 'circular') {
updateCircularMesh();
}
else {
updatePolygonAndHoles();
}
}
function updateMaterial() {
materialType = document.getElementById('materialSelect').value;
updatePolygonAndHoles();
}
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
init();
</script>
</body>
</html>