Skip to content

Commit 3dca911

Browse files
committed
examples scripts update
1 parent 55579da commit 3dca911

File tree

2 files changed

+358
-71
lines changed

2 files changed

+358
-71
lines changed

examples/scripts/slice-arch.js

Lines changed: 152 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,131 @@
11
/**
2-
* Example showing how to slice an STL model with support structures
3-
* This demonstrates the support generation functionality of Polyslice
4-
* by loading an STL file from the resources folder
2+
* Example showing how to slice an "arch" using CSG (default) or an STL with support structures.
3+
* This demonstrates support generation with either a procedurally-built arch (box - cylinder)
4+
* or a provided STL. The default is the CSG arch.
55
*
66
* Usage:
7-
* node examples/scripts/slice-arch.js
7+
* node examples/scripts/slice-arch.js # uses CSG arch (default)
8+
* node examples/scripts/slice-arch.js --use-stl # loads block.test.stl instead
89
*
9-
* The example loads block.test.stl by default. You can modify the script
10-
* to load strip.test.stl or other STL files with overhanging features.
10+
* You can change the STL path or CSG params below.
1111
*/
1212

13-
const { Polyslice, Printer, Filament } = require('../../src/index');
14-
const fs = require('fs');
15-
const path = require('path');
13+
const { Polyslice, Printer, Filament } = require("../../src/index");
14+
const fs = require("fs");
15+
const path = require("path");
16+
const THREE = require("three");
17+
const { Brush, Evaluator, SUBTRACTION } = require("three-bvh-csg");
18+
19+
// Export a mesh as an STL (binary) using three's STLExporter (ESM-only)
20+
async function exportMeshAsSTL(object, outPath) {
21+
const mod = await import("three/examples/jsm/exporters/STLExporter.js");
22+
const STLExporter = mod.STLExporter || mod.default?.STLExporter || mod.default;
23+
const exporter = new STLExporter();
24+
const data = exporter.parse(object, { binary: true });
25+
26+
let nodeBuffer;
27+
if (typeof data === "string") {
28+
nodeBuffer = Buffer.from(data, "utf8");
29+
} else if (ArrayBuffer.isView(data)) {
30+
nodeBuffer = Buffer.from(data.buffer, data.byteOffset || 0, data.byteLength);
31+
} else if (data instanceof ArrayBuffer) {
32+
nodeBuffer = Buffer.from(data);
33+
} else {
34+
throw new Error("Unexpected STLExporter output type");
35+
}
1636

17-
console.log('Polyslice Support Generation Example');
18-
console.log('====================================\n');
37+
fs.writeFileSync(outPath, nodeBuffer);
38+
return outPath;
39+
}
40+
41+
console.log("Polyslice Support Generation Example");
42+
console.log("====================================\n");
1943

2044
// Create printer and filament configuration objects.
21-
const printer = new Printer('Ender5');
22-
const filament = new Filament('GenericPLA');
45+
const printer = new Printer("Ender5");
46+
const filament = new Filament("GenericPLA");
2347

24-
console.log('Printer & Filament Configuration:');
48+
console.log("Printer & Filament Configuration:");
2549
console.log(`- Printer: ${printer.model}`);
2650
console.log(`- Build Volume: ${printer.getSizeX()}x${printer.getSizeY()}x${printer.getSizeZ()}mm`);
2751
console.log(`- Filament: ${filament.name} (${filament.type.toUpperCase()})`);
2852
console.log(`- Brand: ${filament.brand}\n`);
2953

30-
// Main async function to load STL and slice
31-
async function main() {
32-
// Load the test STL file with overhangs
33-
const stlPath = path.join(__dirname, '..', '..', 'resources', 'support', 'block.test.stl');
34-
35-
console.log('Loading STL file...');
36-
console.log(`- Path: ${stlPath}\n`);
54+
// Toggle: default to CSG arch; enable STL via flag/env
55+
const useStl = process.argv.includes("--use-stl") || process.env.ARCH_USE_STL === "1";
3756

38-
let mesh;
57+
// CSG Arch parameters
58+
const ARCH_WIDTH = 40; // X dimension (mm)
59+
const ARCH_HEIGHT = 10; // Y dimension (mm)
60+
const ARCH_THICKNESS = 20; // Z dimension (mm)
61+
const ARCH_RADIUS = 15; // Radius of semi-circular cut (mm)
3962

40-
try {
41-
// Load STL using three.js STLLoader with parse method
42-
// This is more reliable in Node.js than using the URL-based load method
43-
const THREE = require('three');
44-
const { STLLoader } = await import('three/examples/jsm/loaders/STLLoader.js');
63+
// STL path (if --use-stl)
64+
const stlPath = path.join(__dirname, "..", "..", "resources", "support", "block.test.stl");
4565

46-
const buffer = fs.readFileSync(stlPath);
47-
const loader = new STLLoader();
48-
const geometry = loader.parse(buffer.buffer);
49-
// geometry.rotateX(Math.PI);
66+
/**
67+
* Build an arch by subtracting a horizontal cylinder (lying flat) from a box.
68+
* Box is centered at origin; cylinder axis along X (flat), offset +Y by half height to form a semi-circle cut.
69+
* Final mesh is lifted so the bottom sits on Z=0 for printing.
70+
*/
71+
function createArchMesh(width = ARCH_WIDTH, height = ARCH_HEIGHT, thickness = ARCH_THICKNESS, radius = ARCH_RADIUS) {
72+
73+
// Base box
74+
const boxGeo = new THREE.BoxGeometry(width, height, thickness);
75+
const boxBrush = new Brush(boxGeo);
76+
boxBrush.updateMatrixWorld();
77+
78+
// Cylinder lying flat along X: default cylinder axis is Y, rotate about Z by 90° -> axis becomes X
79+
const cylLength = width * 1.25; // ensure it spans the box width
80+
const cylGeo = new THREE.CylinderGeometry(radius, radius, cylLength, 48);
81+
const cylBrush = new Brush(cylGeo);
82+
cylBrush.position.z = -height
83+
cylBrush.updateMatrixWorld();
84+
85+
// Subtract cylinder from box to form arch opening
86+
const evalCSG = new Evaluator();
87+
const resultBrush = evalCSG.evaluate(boxBrush, cylBrush, SUBTRACTION);
88+
89+
// Create mesh and place on build plate
90+
const mat = new THREE.MeshBasicMaterial();
91+
const archMesh = new THREE.Mesh(resultBrush.geometry, mat);
92+
archMesh.position.set(0, 0, thickness / 2);
93+
archMesh.updateMatrixWorld();
94+
return archMesh;
95+
}
5096

51-
// Create mesh with the loaded geometry
52-
const material = new THREE.MeshPhongMaterial({ color: 0x808080, specular: 0x111111, shininess: 200 });
53-
mesh = new THREE.Mesh(geometry, material);
97+
// Main async function to create or load the model and slice
98+
async function main() {
99+
let mesh;
54100

55-
console.log('✅ STL file loaded successfully');
101+
if (useStl) {
102+
console.log("Loading STL file...");
103+
console.log(`- Path: ${stlPath}\n`);
104+
try {
105+
const { STLLoader } = await import("three/examples/jsm/loaders/STLLoader.js");
106+
const buffer = fs.readFileSync(stlPath);
107+
const loader = new STLLoader();
108+
const geometry = loader.parse(buffer.buffer);
109+
const material = new THREE.MeshPhongMaterial({ color: 0x808080, specular: 0x111111, shininess: 200 });
110+
mesh = new THREE.Mesh(geometry, material);
111+
console.log("✅ STL file loaded successfully");
112+
console.log(`- Geometry type: ${mesh.geometry.type}`);
113+
console.log(`- Vertices: ${mesh.geometry.attributes.position.count}`);
114+
console.log(`- Triangles: ${mesh.geometry.attributes.position.count / 3}\n`);
115+
} catch (error) {
116+
console.error("❌ Failed to load STL file:", error.message);
117+
console.error(error.stack);
118+
process.exit(1);
119+
}
120+
} else {
121+
console.log("Building CSG arch (box minus cylinder) ...\n");
122+
mesh = createArchMesh();
123+
// Simple inspection
124+
const pos = mesh.geometry.attributes.position;
125+
console.log("✅ Arch mesh created via CSG");
56126
console.log(`- Geometry type: ${mesh.geometry.type}`);
57-
console.log(`- Vertices: ${mesh.geometry.attributes.position.count}`);
58-
console.log(`- Triangles: ${mesh.geometry.attributes.position.count / 3}\n`);
59-
} catch (error) {
60-
console.error('❌ Failed to load STL file:', error.message);
61-
console.error(error.stack);
62-
process.exit(1);
127+
console.log(`- Vertices: ${pos ? pos.count : "(unknown)"}`);
128+
if (pos) console.log(`- Triangles (approx): ${(pos.count / 3) | 0}\n`);
63129
}
64130

65131
// Create slicer instance with support enabled.
@@ -68,66 +134,76 @@ async function main() {
68134
filament: filament,
69135
shellSkinThickness: 0.8,
70136
shellWallThickness: 0.8,
71-
lengthUnit: 'millimeters',
72-
timeUnit: 'seconds',
73-
infillPattern: 'hexagons',
137+
lengthUnit: "millimeters",
138+
timeUnit: "seconds",
139+
infillPattern: "hexagons",
74140
infillDensity: 30,
75141
bedTemperature: 0,
76142
layerHeight: 0.2,
77143
testStrip: false,
78144
verbose: true,
79145
supportEnabled: true,
80-
supportType: 'normal',
81-
supportPlacement: 'buildPlate',
146+
supportType: "normal",
147+
supportPlacement: "buildPlate",
82148
supportThreshold: 45
83149
});
84150

85-
console.log('Slicer Configuration:');
151+
console.log("Slicer Configuration:");
86152
console.log(`- Layer Height: ${slicer.getLayerHeight()}mm`);
87153
console.log(`- Nozzle Temperature: ${slicer.getNozzleTemperature()}°C`);
88154
console.log(`- Bed Temperature: ${slicer.getBedTemperature()}°C`);
89155
console.log(`- Fan Speed: ${slicer.getFanSpeed()}%`);
90156
console.log(`- Nozzle Diameter: ${slicer.getNozzleDiameter()}mm`);
91157
console.log(`- Filament Diameter: ${slicer.getFilamentDiameter()}mm`);
92-
console.log(`- Support Enabled: ${slicer.getSupportEnabled() ? 'Yes' : 'No'}`);
158+
console.log(`- Support Enabled: ${slicer.getSupportEnabled() ? "Yes" : "No"}`);
93159
console.log(`- Support Type: ${slicer.getSupportType()}`);
94160
console.log(`- Support Placement: ${slicer.getSupportPlacement()}`);
95161
console.log(`- Support Threshold: ${slicer.getSupportThreshold()}°`);
96-
console.log(`- Verbose Comments: ${slicer.getVerbose() ? 'Enabled' : 'Disabled'}\n`);
162+
console.log(`- Verbose Comments: ${slicer.getVerbose() ? "Enabled" : "Disabled"}\n`);
97163

98164
// Slice the model with support generation.
99-
console.log('Slicing model with support generation...');
165+
console.log("Slicing model with support generation...");
100166
const startTime = Date.now();
101167
const gcode = slicer.slice(mesh);
102168
const endTime = Date.now();
103169

104170
console.log(`Slicing completed in ${endTime - startTime}ms\n`);
105171

106172
// Analyze the G-code output.
107-
const lines = gcode.split('\n');
108-
const layerLines = lines.filter(line => line.includes('LAYER:'));
109-
const supportLines = lines.filter(line => line.toLowerCase().includes('support'));
173+
const lines = gcode.split("\n");
174+
const layerLines = lines.filter(line => line.includes("LAYER:"));
175+
const supportLines = lines.filter(line => line.toLowerCase().includes("support"));
110176

111-
console.log('G-code Analysis:');
177+
console.log("G-code Analysis:");
112178
console.log(`- Total lines: ${lines.length}`);
113179
console.log(`- Layers: ${layerLines.length}`);
114180
console.log(`- Support-related lines: ${supportLines.length}\n`);
115181

116182
// Save G-code to file.
117-
const outputDir = path.join(__dirname, '..', 'output');
183+
const outputDir = path.join(__dirname, "..", "output");
118184

119185
if (!fs.existsSync(outputDir)) {
120186
fs.mkdirSync(outputDir, { recursive: true });
121187
}
122188

123-
const outputPath = path.join(outputDir, 'block-with-supports.gcode');
124-
fs.writeFileSync(outputPath, gcode);
189+
const baseName = useStl ? "block-with-supports" : "arch-with-supports";
190+
const gcodePath = path.join(outputDir, `${baseName}.gcode`);
191+
const stlPath = path.join(outputDir, `${baseName}.stl`);
192+
fs.writeFileSync(gcodePath, gcode);
125193

126-
console.log(`✅ G-code saved to: ${outputPath}\n`);
194+
// Export STL for the mesh used (mirrors slice-holes behavior)
195+
try {
196+
await exportMeshAsSTL(mesh, stlPath);
197+
console.log(`🧊 STL saved to: ${stlPath}`);
198+
} catch (e) {
199+
console.warn(`⚠️ Failed to export STL: ${e.message}`);
200+
}
201+
202+
console.log(`✅ G-code saved to: ${gcodePath}\n`);
127203

128204
// Display support generation info.
129205
if (supportLines.length > 0) {
130-
console.log('Support Generation Details:');
206+
console.log("Support Generation Details:");
131207
supportLines.slice(0, 10).forEach(line => {
132208
console.log(` ${line.trim()}`);
133209
});
@@ -136,11 +212,11 @@ async function main() {
136212
console.log(` ... (${supportLines.length - 10} more support lines)\n`);
137213
}
138214
} else {
139-
console.log('⚠️ No support structures detected in G-code\n');
215+
console.log("⚠️ No support structures detected in G-code\n");
140216
}
141217

142218
// Display some layer information.
143-
console.log('Layer Information:');
219+
console.log("Layer Information:");
144220
const sampleLayers = layerLines.slice(0, 5);
145221
sampleLayers.forEach(line => {
146222
console.log(`- ${line.trim()}`);
@@ -150,16 +226,21 @@ async function main() {
150226
console.log(`... (${layerLines.length - 5} more layers)\n`);
151227
}
152228

153-
console.log('✅ Support generation example completed successfully!');
154-
console.log('\nNotes:');
155-
console.log('- If no supports were generated, the model may not have overhangs');
156-
console.log('- The block.test.stl is a simple rectangular block without overhangs');
157-
console.log('- Try the strip.test.stl file which may have overhanging features');
158-
console.log('\nNext steps:');
159-
console.log('- Load the G-code in a visualizer to inspect the sliced model');
160-
console.log('- Try different support thresholds (30°, 45°, 60°) to see the effect');
161-
console.log('- Create or load models with overhangs to test support generation');
162-
console.log('- Experiment with supportPlacement: "buildPlate" vs "everywhere"');
229+
console.log("✅ Support generation example completed successfully!");
230+
console.log("\nNotes:");
231+
console.log("- If no supports were generated, the model may not have overhangs");
232+
if (useStl) {
233+
console.log("- The block.test.stl is a simple rectangular block without overhangs");
234+
console.log("- Try the strip.test.stl file which may have overhanging features");
235+
} else {
236+
console.log("- The CSG arch subtracts a cylinder to create a semi-circular opening");
237+
console.log("- Tweak ARCH_* params at top to adjust width/height/radius");
238+
}
239+
console.log("\nNext steps:");
240+
console.log("- Load the G-code in a visualizer to inspect the sliced model");
241+
console.log("- Try different support thresholds (30°, 45°, 60°) to see the effect");
242+
console.log("- Create or load models with overhangs to test support generation");
243+
console.log("- Experiment with supportPlacement: \"buildPlate\" vs \"everywhere\"");
163244
}
164245

165246
// Run the main function

0 commit comments

Comments
 (0)