Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
peteruithoven 2017-07-20 16:22:11 +02:00
commit 788cd3d476
6 changed files with 133 additions and 78 deletions

View File

@ -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

View File

@ -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);
}); });

View File

@ -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()
}
});
});
}

View File

@ -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
}; };

View File

@ -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();

View File

@ -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',