Skip to content

Commit 6e2cef6

Browse files
jgphilpottCopilotMugen87
authored
Add GCodeExporter and external example for Polyslice G-code slicer (#32502)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jgphilpott <4128208+jgphilpott@users.noreply.github.com> Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
1 parent b416b35 commit 6e2cef6

File tree

4 files changed

+300
-0
lines changed

4 files changed

+300
-0
lines changed

examples/files.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@
554554
"misc_controls_trackball",
555555
"misc_controls_transform",
556556
"misc_exporter_draco",
557+
"misc_exporter_gcode",
557558
"misc_exporter_gltf",
558559
"misc_exporter_obj",
559560
"misc_exporter_ply",

examples/misc_exporter_gcode.html

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js webgl - exporter - GCode</title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
7+
<link type="text/css" rel="stylesheet" href="main.css">
8+
</head>
9+
<body>
10+
<div id="info">
11+
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - exporter - <a href="https://github.com/jgphilpott/polyslice" target="_blank" rel="noopener">GCode</a>
12+
<br>Slice 3D models to G-code for 3D printing
13+
<br><a href="https://www.youtube.com/watch?v=V2h3SiafXRc" target="_blank" rel="noopener">Watch the Demo Video</a>
14+
</div>
15+
16+
<script type="importmap">
17+
{
18+
"imports": {
19+
"three": "../build/three.module.js",
20+
"three/addons/": "./jsm/"
21+
}
22+
}
23+
</script>
24+
25+
<script type="module">
26+
27+
import * as THREE from 'three';
28+
import Polyslice from 'https://unpkg.com/@jgphilpott/polyslice@25.12.8/dist/index.browser.esm.js';
29+
30+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
31+
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
32+
33+
// Make THREE available globally for Polyslice
34+
window.THREE = THREE;
35+
36+
let camera, scene, renderer, mesh;
37+
38+
const params = {
39+
addCube: addCube,
40+
addCylinder: addCylinder,
41+
addCone: addCone,
42+
addSphere: addSphere,
43+
addTorus: addTorus,
44+
exportToGCode: exportToGCode,
45+
geometryName: 'cube',
46+
printer: 'Ender3',
47+
filament: 'GenericPLA',
48+
layerHeight: 0.2,
49+
infillDensity: 20,
50+
infillPattern: 'grid'
51+
};
52+
53+
// Dropdown options (extend as supported by Polyslice profiles)
54+
const PRINTER_OPTIONS = [ 'Ender3', 'UltimakerS5', 'PrusaI3MK3S', 'AnycubicI3Mega', 'BambuLabP1P' ];
55+
const FILAMENT_OPTIONS = [ 'GenericPLA', 'GenericPETG', 'GenericABS' ];
56+
57+
init();
58+
59+
function init() {
60+
61+
renderer = new THREE.WebGLRenderer( { antialias: true } );
62+
renderer.setPixelRatio( window.devicePixelRatio );
63+
renderer.setSize( window.innerWidth, window.innerHeight );
64+
renderer.setAnimationLoop( animate );
65+
document.body.appendChild( renderer.domElement );
66+
67+
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
68+
// Use Z-up coordinate system
69+
THREE.Object3D.DEFAULT_UP.set( 0, 0, 1 );
70+
camera.up.set( 0, 0, 1 );
71+
camera.position.set( 42, 42, 42 );
72+
73+
scene = new THREE.Scene();
74+
scene.background = new THREE.Color( 0xa0a0a0 );
75+
76+
const ambientLight = new THREE.AmbientLight( 0xffffff, 0.5 );
77+
scene.add( ambientLight );
78+
79+
const directionalLight = new THREE.DirectionalLight( 0xffffff, 2.5 );
80+
directionalLight.position.set( 0, 200, 100 );
81+
scene.add( directionalLight );
82+
83+
// Add ground plane for reference (XY plane when Z is up)
84+
const gridHelper = new THREE.GridHelper( 220, 10 );
85+
gridHelper.rotation.x = - Math.PI / 2; // rotate from XZ to XY
86+
scene.add( gridHelper );
87+
88+
const gui = new GUI();
89+
90+
let h = gui.addFolder( 'Printer & Filament' );
91+
h.add( params, 'printer', PRINTER_OPTIONS ).name( 'Printer' );
92+
h.add( params, 'filament', FILAMENT_OPTIONS ).name( 'Filament' );
93+
94+
h = gui.addFolder( 'Slicer Settings' );
95+
h.add( params, 'layerHeight', 0.1, 0.4, 0.05 ).name( 'Layer Height (mm)' );
96+
h.add( params, 'infillDensity', 0, 100, 5 ).name( 'Infill Density (%)' );
97+
h.add( params, 'infillPattern', [ 'grid', 'triangles', 'hexagons' ] ).name( 'Infill Pattern' );
98+
99+
h = gui.addFolder( 'Geometry Selection' );
100+
h.add( params, 'addCube' ).name( 'Cube' );
101+
h.add( params, 'addCylinder' ).name( 'Cylinder' );
102+
h.add( params, 'addCone' ).name( 'Cone' );
103+
h.add( params, 'addSphere' ).name( 'Sphere' );
104+
h.add( params, 'addTorus' ).name( 'Torus' );
105+
106+
h = gui.addFolder( 'Export' );
107+
h.add( params, 'exportToGCode' ).name( 'Export G-code' );
108+
109+
gui.open();
110+
111+
addCube();
112+
113+
window.addEventListener( 'resize', onWindowResize );
114+
115+
const controls = new OrbitControls( camera, renderer.domElement );
116+
controls.target.set( 0, 0, 0 );
117+
controls.update();
118+
119+
}
120+
121+
function exportToGCode() {
122+
123+
try {
124+
125+
// Check if Polyslice is loaded
126+
if ( typeof Polyslice === 'undefined' ) {
127+
128+
alert( 'Polyslice library failed to load from CDN.\n\nPossible solutions:\n1. Disable ad blockers or browser extensions\n2. Check your network connection\n3. Ensure unpkg.com is not blocked by your firewall' );
129+
return;
130+
131+
}
132+
133+
// Create printer and filament configurations using Polyslice
134+
const printer = new Polyslice.Printer( params.printer || 'Ender3' );
135+
const filament = new Polyslice.Filament( params.filament || 'GenericPLA' );
136+
137+
// Create the slicer instance with user-defined settings
138+
const slicer = new Polyslice.Polyslice( {
139+
printer: printer,
140+
filament: filament,
141+
layerHeight: params.layerHeight,
142+
infillPattern: params.infillPattern,
143+
infillDensity: params.infillDensity,
144+
verbose: true
145+
} );
146+
147+
// Slice the current mesh directly
148+
const gcode = slicer.slice( mesh );
149+
150+
// Download the G-code file (include geometry name)
151+
const name = params.geometryName || 'model';
152+
saveString( gcode, `${ name }-geometry.gcode` );
153+
154+
} catch ( error ) {
155+
156+
console.error( 'Error exporting to G-code:', error );
157+
alert( 'Error exporting to G-code. Check console for details.' );
158+
alert( 'Note: Polyslice is an external library. Please ensure it is loaded correctly.' );
159+
160+
}
161+
162+
}
163+
164+
function clearScene() {
165+
166+
if ( mesh ) {
167+
168+
mesh.geometry.dispose();
169+
mesh.material.dispose();
170+
scene.remove( mesh );
171+
172+
}
173+
174+
}
175+
176+
// Ensure the mesh sits on the XY plane with min Z = 0
177+
function placeOnXYPlane( object ) {
178+
179+
// Update world matrix so bounding box reflects transforms
180+
object.updateMatrixWorld( true );
181+
182+
const box = new THREE.Box3().setFromObject( object );
183+
const minZ = box.min.z;
184+
185+
if ( isFinite( minZ ) ) {
186+
187+
// Shift object upward by -minZ so it rests on z=0
188+
object.position.z -= minZ;
189+
object.updateMatrixWorld( true );
190+
191+
}
192+
193+
}
194+
195+
function addCube() {
196+
197+
clearScene();
198+
199+
const material = new THREE.MeshLambertMaterial( { color: 0x00cc00 } );
200+
const geometry = new THREE.BoxGeometry( 10, 10, 10 );
201+
mesh = new THREE.Mesh( geometry, material );
202+
params.geometryName = 'cube';
203+
placeOnXYPlane( mesh );
204+
scene.add( mesh );
205+
206+
}
207+
208+
function addCylinder() {
209+
210+
clearScene();
211+
212+
const material = new THREE.MeshLambertMaterial( { color: 0x00cc00 } );
213+
const geometry = new THREE.CylinderGeometry( 5, 5, 10, 42 );
214+
mesh = new THREE.Mesh( geometry, material );
215+
mesh.rotation.x = Math.PI / 2;
216+
params.geometryName = 'cylinder';
217+
placeOnXYPlane( mesh );
218+
scene.add( mesh );
219+
220+
}
221+
222+
function addCone() {
223+
224+
clearScene();
225+
226+
const material = new THREE.MeshLambertMaterial( { color: 0x00cc00 } );
227+
const geometry = new THREE.ConeGeometry( 5, 10, 42 );
228+
mesh = new THREE.Mesh( geometry, material );
229+
mesh.rotation.x = Math.PI / 2;
230+
params.geometryName = 'cone';
231+
placeOnXYPlane( mesh );
232+
scene.add( mesh );
233+
234+
}
235+
236+
function addSphere() {
237+
238+
clearScene();
239+
240+
const material = new THREE.MeshLambertMaterial( { color: 0x00cc00 } );
241+
const geometry = new THREE.SphereGeometry( 5, 42, 42 );
242+
mesh = new THREE.Mesh( geometry, material );
243+
params.geometryName = 'sphere';
244+
placeOnXYPlane( mesh );
245+
scene.add( mesh );
246+
247+
}
248+
249+
function addTorus() {
250+
251+
clearScene();
252+
253+
const material = new THREE.MeshLambertMaterial( { color: 0x00cc00 } );
254+
const geometry = new THREE.TorusGeometry( 5, 2, 24, 100 );
255+
mesh = new THREE.Mesh( geometry, material );
256+
params.geometryName = 'torus';
257+
placeOnXYPlane( mesh );
258+
scene.add( mesh );
259+
260+
}
261+
262+
const link = document.createElement( 'a' );
263+
link.style.display = 'none';
264+
document.body.appendChild( link );
265+
266+
function save( blob, filename ) {
267+
268+
link.href = URL.createObjectURL( blob );
269+
link.download = filename;
270+
link.click();
271+
272+
}
273+
274+
function saveString( text, filename ) {
275+
276+
save( new Blob( [ text ], { type: 'text/plain' } ), filename );
277+
278+
}
279+
280+
function onWindowResize() {
281+
282+
camera.aspect = window.innerWidth / window.innerHeight;
283+
camera.updateProjectionMatrix();
284+
285+
renderer.setSize( window.innerWidth, window.innerHeight );
286+
287+
}
288+
289+
function animate() {
290+
291+
renderer.render( scene, camera );
292+
293+
}
294+
295+
</script>
296+
297+
</body>
298+
</html>
19.5 KB
Loading

examples/tags.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"misc_controls_orbit": [ "rotation" ],
66
"misc_controls_trackball": [ "rotation" ],
77
"misc_controls_transform": [ "scale", "rotate", "translate" ],
8+
"misc_exporter_gcode": [ "community" ],
89
"misc_raycaster_helper": [ "community" ],
910
"physics_ammo_break": [ "community" ],
1011
"physics_ammo_cloth": [ "integration", "community" ],

0 commit comments

Comments
 (0)