mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-25 23:17:57 +01:00
Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
788cd3d476
52
README.md
52
README.md
@ -1,10 +1,12 @@
|
|||||||
# Doodle3D-Slicer
|
# Doodle3D-Slicer
|
||||||
|
|
||||||
JavaScript gcode slicer, Intended to use with the Doodle3D WiFi-Box
|
JavaScript gcode slicer, Intended to use with the Doodle3D WiFi-Box
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { defaultSettings, Slicer } from 'Doodle3D/Doodle3D-Slicer';
|
import { defaultSettings, sliceGeometry } from 'Doodle3D/Doodle3D-Slicer';
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
...defaultSettings.base,
|
...defaultSettings.base,
|
||||||
@ -15,10 +17,46 @@ const settings = {
|
|||||||
|
|
||||||
const geometry = new THREE.TorusGeometry(20, 10, 30, 30).clone();
|
const geometry = new THREE.TorusGeometry(20, 10, 30, 30).clone();
|
||||||
|
|
||||||
const slicer = new SLICER.Slicer();
|
const gcode = await sliceGeometry(settings, geometry);
|
||||||
slicer.setGeometry(geometry);
|
|
||||||
const gcode = await slicer.slice(settings, ({ progress: { done, total, action } }) => {
|
|
||||||
const percentage = `${(done / total * 100).toFixed()}%`
|
|
||||||
console.log(action, percentage);
|
|
||||||
}));
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# API
|
||||||
|
|
||||||
|
**Settings**
|
||||||
|
```javascript
|
||||||
|
import { defaultSettings } from 'Doodle3D/Doodle3D-Slicer';
|
||||||
|
|
||||||
|
const settings = {
|
||||||
|
...defaultSettings.base,
|
||||||
|
...defaultSettings.material.pla,
|
||||||
|
...defaultSettings.printer.ultimaker2go,
|
||||||
|
...defaultSettings.quality.high
|
||||||
|
};
|
||||||
|
```
|
||||||
|
Create settings object to be used by the slicer
|
||||||
|
|
||||||
|
**Slice Mesh**
|
||||||
|
```javascript
|
||||||
|
import { sliceMesh } from 'Doodle3D/Doodle3D-Slicer';
|
||||||
|
|
||||||
|
GCode: String = sliceMesh(settings: Object, mesh: THREE.Mesh, [sync: Boolean = false, onProgress: Func ])
|
||||||
|
```
|
||||||
|
Slice function that accepts Meshes
|
||||||
|
- Settings: settings object (see [settings](#settings))
|
||||||
|
- Mesh: THREE.Mesh instance that contains the geometry
|
||||||
|
- Sync: determines if the slicing progress will be sync (blocking) or async (non-blocking). A webworker is used to slice async
|
||||||
|
- onProgress: progress callback
|
||||||
|
|
||||||
|
**Slice Geometry**
|
||||||
|
```javascript
|
||||||
|
import { sliceGeometry } from 'Doodle3D/Doodle3D-Slicer';
|
||||||
|
|
||||||
|
GCode: String = sliceGeometry(settings: Object, geometry: THREE.Geometry | THREE.BufferGeometry, [matrix: THREE.Matrix, sync: Boolean = false, onProgress: Func ])
|
||||||
|
```
|
||||||
|
|
||||||
|
Slice function that accepts Geometry
|
||||||
|
- Settings: settings object (see [settings](#settings))
|
||||||
|
- Geometry: THREE.Geometry instance
|
||||||
|
- matrix: matrix that can control the scale, rotation and position of the model
|
||||||
|
- Sync: determines if the slicing progress will be sync (blocking) or async (non-blocking). A webworker is used to slice async
|
||||||
|
- onProgress: progress callback
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { defaultSettings, Slicer } from 'src/index.js';
|
import { defaultSettings, sliceGeometry } from 'src/index.js';
|
||||||
import { saveAs } from 'file-saver';
|
import fileSaver from 'file-saver';
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
...defaultSettings.base,
|
...defaultSettings.base,
|
||||||
@ -12,16 +12,16 @@ const settings = {
|
|||||||
const jsonLoader = new THREE.JSONLoader();
|
const jsonLoader = new THREE.JSONLoader();
|
||||||
jsonLoader.load('models/airplane.json', async geometry => {
|
jsonLoader.load('models/airplane.json', async geometry => {
|
||||||
geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / -2));
|
geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / -2));
|
||||||
geometry.applyMatrix(new THREE.Matrix4().setPosition(new THREE.Vector3(50, -0.1, 50)));
|
geometry.applyMatrix(new THREE.Matrix4().setPosition(new THREE.Vector3(50, -0.0, 50)));
|
||||||
geometry.computeFaceNormals();
|
geometry.computeFaceNormals();
|
||||||
|
|
||||||
const slicer = new Slicer().setGeometry(geometry);
|
const onProgress = ({ progress: { done, total, action } }) => {
|
||||||
const gcode = await slicer.slice(settings)
|
const percentage = `${(done / total * 100).toFixed()}%`
|
||||||
.progress(({ progress: { done, total, action } }) => {
|
document.write(`<p>${action}, ${percentage}</p>`);
|
||||||
const percentage = `${(done / total * 100).toFixed()}%`
|
};
|
||||||
document.write(`<p>${action}, ${percentage}</p>`);
|
|
||||||
});
|
const gcode = await sliceGeometry(settings, geometry, null, false, onProgress);
|
||||||
|
|
||||||
const file = new File([gcode], 'gcode.gcode', { type: 'text/plain' });
|
const file = new File([gcode], 'gcode.gcode', { type: 'text/plain' });
|
||||||
saveAs(file);
|
fileSaver.saveAs(file);
|
||||||
});
|
});
|
||||||
|
115
src/Slicer.js
115
src/Slicer.js
@ -2,64 +2,73 @@ import * as THREE from 'three';
|
|||||||
import slice from './sliceActions/slice.js';
|
import slice from './sliceActions/slice.js';
|
||||||
import SlicerWorker from './slicerWorker.js!worker';
|
import SlicerWorker from './slicerWorker.js!worker';
|
||||||
|
|
||||||
export default class {
|
export function sliceMesh(settings, mesh, sync = false, onProgress) {
|
||||||
setMesh(mesh) {
|
if (typeof mesh === 'undefined' || !mesh.isMesh) {
|
||||||
mesh.updateMatrix();
|
throw new Error('Provide mesh is not intance of THREE.Mesh');
|
||||||
|
|
||||||
return this.setGeometry(mesh.geometry, mesh.matrix);
|
|
||||||
}
|
}
|
||||||
setGeometry(geometry, matrix) {
|
|
||||||
if (geometry.isBufferGeometry) {
|
|
||||||
geometry = new THREE.Geometry().fromBufferGeometry(geometry);
|
|
||||||
} else if (geometry.isGeometry) {
|
|
||||||
geometry = geometry.clone();
|
|
||||||
} else {
|
|
||||||
throw new Error('Geometry is not an instance of BufferGeometry or Geometry');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof matrix !== 'undefined') {
|
mesh.updateMatrix();
|
||||||
geometry.applyMatrix(matrix);
|
const { geometry, matrix } = mesh;
|
||||||
}
|
return sliceGeometry(settings, geometry, matrix, sync, onProgress);
|
||||||
|
}
|
||||||
|
|
||||||
this.geometry = geometry;
|
export function sliceGeometry(settings, geometry, matrix, sync = false, onProgress) {
|
||||||
|
if (typeof geometry === 'undefined') {
|
||||||
return this;
|
throw new Error('Missing required geometry argument');
|
||||||
|
} else if (geometry.isBufferGeometry) {
|
||||||
|
geometry = new THREE.Geometry().fromBufferGeometry(geometry);
|
||||||
|
} else if (geometry.isGeometry) {
|
||||||
|
geometry = geometry.clone();
|
||||||
|
} else {
|
||||||
|
throw new Error('Geometry is not an instance of BufferGeometry or Geometry');
|
||||||
}
|
}
|
||||||
sliceSync(settings, onProgress) {
|
|
||||||
return slice(this.geometry, settings, onProgress);
|
if (matrix) {
|
||||||
|
geometry.applyMatrix(matrix);
|
||||||
}
|
}
|
||||||
slice(settings, onProgress) {
|
|
||||||
if (!this.geometry) {
|
|
||||||
throw new Error('Geometry is not set, use Slicer.setGeometry or Slicer.setMesh first');
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
if (sync) {
|
||||||
// create the slicer worker
|
return sliceSync(settings, geometry, onProgress);
|
||||||
const slicerWorker = new SlicerWorker();
|
} else {
|
||||||
slicerWorker.onerror = reject;
|
return sliceAsync(settings, geometry, onProgress);
|
||||||
|
|
||||||
// listen to messages send from worker
|
|
||||||
slicerWorker.addEventListener('message', (event) => {
|
|
||||||
const { message, data } = event.data;
|
|
||||||
switch (message) {
|
|
||||||
case 'SLICE': {
|
|
||||||
slicerWorker.terminate();
|
|
||||||
resolve(data.gcode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'PROGRESS': {
|
|
||||||
onProgress(data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// send geometry and settings to worker to start the slicing progress
|
|
||||||
const geometry = this.geometry.toJSON();
|
|
||||||
slicerWorker.postMessage({
|
|
||||||
message: 'SLICE',
|
|
||||||
data: { geometry, settings }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sliceSync(settings, geometry, onProgress) {
|
||||||
|
return slice(settings, geometry, onProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sliceAsync(settings, geometry, onProgress) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// create the slicer worker
|
||||||
|
const slicerWorker = new SlicerWorker();
|
||||||
|
slicerWorker.onerror = reject;
|
||||||
|
|
||||||
|
// listen to messages send from worker
|
||||||
|
slicerWorker.addEventListener('message', (event) => {
|
||||||
|
const { message, data } = event.data;
|
||||||
|
switch (message) {
|
||||||
|
case 'SLICE': {
|
||||||
|
slicerWorker.terminate();
|
||||||
|
resolve(data.gcode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'PROGRESS': {
|
||||||
|
if (typeof onProgress !== 'undefined') {
|
||||||
|
onProgress(data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// send geometry and settings to worker to start the slicing progress
|
||||||
|
slicerWorker.postMessage({
|
||||||
|
message: 'SLICE',
|
||||||
|
data: {
|
||||||
|
settings,
|
||||||
|
geometry: geometry.toJSON()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Slicer from './Slicer.js';
|
import { sliceGeometry, sliceMesh } from './slicer.js';
|
||||||
import baseSettings from './settings/default.yml!text';
|
import baseSettings from './settings/default.yml!text';
|
||||||
import printerSettings from './settings/printer.yml!text';
|
import printerSettings from './settings/printer.yml!text';
|
||||||
import materialSettings from './settings/material.yml!text';
|
import materialSettings from './settings/material.yml!text';
|
||||||
@ -13,6 +13,7 @@ const defaultSettings = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Slicer,
|
sliceGeometry,
|
||||||
|
sliceMesh,
|
||||||
defaultSettings
|
defaultSettings
|
||||||
};
|
};
|
||||||
|
@ -13,12 +13,20 @@ import detectOpenClosed from './detectOpenClosed.js';
|
|||||||
import applyPrecision from './applyPrecision.js';
|
import applyPrecision from './applyPrecision.js';
|
||||||
import removePrecision from './removePrecision.js';
|
import removePrecision from './removePrecision.js';
|
||||||
|
|
||||||
export default function(geometry, settings, onProgress) {
|
export default function(settings, geometry, onProgress) {
|
||||||
const totalStages = 12;
|
const totalStages = 12;
|
||||||
let current = -1;
|
let current = -1;
|
||||||
const updateProgress = (action) => {
|
const updateProgress = (action) => {
|
||||||
current ++;
|
current ++;
|
||||||
if (onProgress) onProgress({ done: current, total: totalStages, action });
|
if (typeof onProgress !== 'undefined') {
|
||||||
|
onProgress({
|
||||||
|
progress: {
|
||||||
|
done: current,
|
||||||
|
total: totalStages,
|
||||||
|
action
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
geometry.computeFaceNormals();
|
geometry.computeFaceNormals();
|
||||||
|
@ -6,7 +6,7 @@ const loader = new THREE.JSONLoader();
|
|||||||
const onProgress = progress => {
|
const onProgress = progress => {
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
message: 'PROGRESS',
|
message: 'PROGRESS',
|
||||||
data: { progress }
|
data: progress
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,11 +14,10 @@ self.addEventListener('message', (event) => {
|
|||||||
const { message, data } = event.data;
|
const { message, data } = event.data;
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case 'SLICE': {
|
case 'SLICE': {
|
||||||
const { geometry: JSONGeometry, settings } = data;
|
const { settings, geometry: JSONGeometry } = data;
|
||||||
|
|
||||||
const { geometry } = loader.parse(JSONGeometry.data);
|
const { geometry } = loader.parse(JSONGeometry.data);
|
||||||
|
|
||||||
const gcode = slice(geometry, settings, onProgress);
|
const gcode = slice(settings, geometry, onProgress);
|
||||||
|
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
message: 'SLICE',
|
message: 'SLICE',
|
||||||
|
Loading…
Reference in New Issue
Block a user