diff --git a/index.js b/index.js index 86a95fb..54d517b 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +import 'babel-polyfill' import React from 'react'; import { Interface } from 'doodle3d-slicer'; import doodleURL from '!url-loader!./models/Doodle_2.d3sketch'; @@ -9,6 +10,8 @@ import preset from 'jss-preset-default'; import normalize from 'normalize-jss'; import JSONToSketchData from 'doodle3d-core/shape/JSONToSketchData'; import createSceneData from 'doodle3d-core/d3/createSceneData.js'; +import { generateExportMesh } from 'doodle3d-core/utils/exportUtils.js'; +import { Matrix4 } from 'three/src/math/Matrix4.js'; injectTapEventPlugin(); @@ -23,10 +26,10 @@ jss.createStyleSheet({ } }).attach(); -function init(sketch) { +function init(mesh) { render(( - + ), document.getElementById('app')); } @@ -35,4 +38,5 @@ fetch(doodleURL) .then(resonse => resonse.json()) .then(json => JSONToSketchData(json)) .then(file => createSceneData(file)) + .then(sketch => generateExportMesh(sketch, { offsetSingleWalls: false, matrix: new Matrix4() })) .then(init); diff --git a/package.json b/package.json index 0586c76..6b3e0c8 100644 --- a/package.json +++ b/package.json @@ -16,9 +16,7 @@ }, "dependencies": { "@doodle3d/clipper-js": "^1.0.7", - "@doodle3d/doodle3d-core": "github:doodle3d/doodle3d-core", "babel-plugin-transform-export-extensions": "^6.22.0", - "babel-runtime": "^6.26.0", "file-saver": "^1.3.3", "lodash": "^4.17.4", "material-ui": "^0.19.4", @@ -39,11 +37,13 @@ "babel-plugin-transform-es2015-classes": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-plugin-transform-runtime": "^6.23.0", + "babel-runtime": "^6.26.0", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.6.1", "babel-preset-es2015": "6.24.1", "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", + "@doodle3d/doodle3d-core": "github:doodle3d/doodle3d-core", "html-webpack-plugin": "^2.29.0", "html-webpack-template": "^6.0.2", "imports-loader": "^0.7.1", diff --git a/src/interface/index.js b/src/interface/index.js index 05307bc..912a083 100644 --- a/src/interface/index.js +++ b/src/interface/index.js @@ -2,6 +2,7 @@ import _ from 'lodash'; import React from 'react'; import { Quaternion } from 'three/src/math/Quaternion.js'; import { Vector3 } from 'three/src/math/Vector3.js'; +import { Mesh } from 'three/src/objects/Mesh.js'; import PropTypes from 'proptypes'; import { placeOnGround, createScene, fetchProgress, slice, TabTemplate } from './utils.js'; import injectSheet from 'react-jss'; @@ -78,10 +79,7 @@ const styles = { class Interface extends React.Component { static propTypes = { - sketch: PropTypes.shape({ - data: PropTypes.string, - appVersion: PropTypes.string - }), + mesh: PropTypes.shape({ isMesh: PropTypes.oneOf([true]) }).isRequired, classes: PropTypes.objectOf(PropTypes.string), defaultSettings: PropTypes.object.isRequired, printers: PropTypes.object.isRequired, @@ -110,7 +108,6 @@ class Interface extends React.Component { super(props); const { defaultPrinter, defaultQuality, defaultMaterial, printers, quality, material, defaultSettings } = props; this.state = { - controlMode: 'translate', showFullScreen: false, isSlicing: false, error: null, @@ -135,7 +132,10 @@ class Interface extends React.Component { } componentWillUnmount() { - if (this.state.editorControls) this.state.editorControls.dispose(); + const { editorControls, mesh: { material }, renderer } = this.state; + editorControls.dispose(); + material.dispose(); + renderer.dispose(); } resetMesh = () => { @@ -175,15 +175,18 @@ class Interface extends React.Component { }; slice = async () => { - const { mesh, settings, isSlicing, printers, quality, material } = this.state; - const { name, sketch } = this.props; + const { settings, isSlicing, printers, quality, mesh: { matrix }, material } = this.state; + const { name, mesh } = this.props; if (isSlicing) return; this.setState({ isSlicing: true, progress: { action: '', slicing: 0, uploading: 0 }, error: null }); + const exportMesh = new Mesh(mesh.geometry, mesh.material); + exportMesh.applyMatrix(matrix); + try { - await slice(name, sketch, mesh.matrix, settings, printers, quality, material, progress => { + await slice(name, exportMesh, settings, printers, quality, material, progress => { this.setState({ progress: { ...this.state.progress, ...progress } }); }); } catch (error) { @@ -220,7 +223,7 @@ class Interface extends React.Component { window.requestAnimationFrame(() => { const { setSize } = this.state; const { pixelRatio } = this.props; - setSize(width, height, pixelRatio); + if (setSize) setSize(width, height, pixelRatio); }); }; @@ -233,9 +236,10 @@ class Interface extends React.Component { const { isSlicing, progress, settings, printers, quality, material, showFullScreen, error } = this.state; const percentage = progress ? (progress.uploading + progress.slicing) / 2.0 * 100.0 : 0.0; + const style = { ...(showFullScreen ? {} : { maxWidth: 'inherit', width: '100%', height: '100%' }) }; const settingsPanel = ( -
+
{ + const matrix = new Matrix4().makeTranslation(centerY, 0, centerX).multiply(mesh.matrix); + const { gcode } = sliceGeometry(settings, mesh.geometry, mesh.material, matrix, true, false, ({ progress }) => { updateProgress({ action: progress.action, slicing: progress.done / progress.total diff --git a/src/sliceActions/generateGeometry.js b/src/sliceActions/generateGeometry.js deleted file mode 100644 index 06c17db..0000000 --- a/src/sliceActions/generateGeometry.js +++ /dev/null @@ -1,28 +0,0 @@ -import { generateExportMesh } from 'doodle3d-core/utils/exportUtils.js'; -import { Matrix4 } from 'three/src/math/Matrix4.js'; -import { Mesh } from 'three/src/objects/Mesh.js'; -import { Geometry } from 'three/src/core/Geometry.js'; -import { FrontSide, DoubleSide } from 'three/src/constants.js'; -import { BoxGeometry } from 'three/src/geometries/BoxGeometry.js'; - -export default function generateGeometry(sketch, matrix) { - const { geometry, material } = generateExportMesh(sketch, { - unionGeometry: false, - offsetSingleWalls: false, - matrix - }); - - const open = material.map(({ side }) => { - switch (side) { - case FrontSide: - return false; - case DoubleSide: - return true; - default: - return false; - } - }); - - geometry.computeFaceNormals(); - return { geometry, open }; -} diff --git a/src/sliceActions/intersectionsToShapes.js b/src/sliceActions/intersectionsToShapes.js index dd35e7e..0993d1e 100644 --- a/src/sliceActions/intersectionsToShapes.js +++ b/src/sliceActions/intersectionsToShapes.js @@ -1,9 +1,9 @@ import { subtract, normal, normalize, dot, distanceTo, clone } from './helpers/VectorUtils.js'; -export default function intersectionsToShapes(intersectionLayers, faces, open, settings) { +export default function intersectionsToShapes(intersectionLayers, faces, openObjectIndexes, settings) { const layers = []; - for (let layer = 1; layer < intersectionLayers.length; layer ++) { + for (let layer = 0; layer < intersectionLayers.length; layer ++) { const fillShapes = []; const lineShapesOpen = []; const lineShapesClosed = []; @@ -43,7 +43,7 @@ export default function intersectionsToShapes(intersectionLayers, faces, open, s for (const objectIndex in shapes) { const shape = shapes[objectIndex]; - const openShape = open[objectIndex]; + const openShape = openObjectIndexes[objectIndex]; const lines = [shape.lineSegments.pop()]; diff --git a/src/sliceActions/slice.js b/src/sliceActions/slice.js index 471c110..57b8232 100644 --- a/src/sliceActions/slice.js +++ b/src/sliceActions/slice.js @@ -1,4 +1,3 @@ -import 'babel-polyfill' import { Color } from 'three/src/math/Color.js'; import { BufferGeometry } from 'three/src/core/BufferGeometry.js'; import { BufferAttribute } from 'three/src/core/BufferAttribute.js'; @@ -16,12 +15,11 @@ import addBrim from './addBrim.js'; import optimizePaths from './optimizePaths.js'; import shapesToSlices from './shapesToSlices.js'; import slicesToGCode from './slicesToGCode.js'; -import generateGeometry from './generateGeometry.js'; import applyPrecision from './applyPrecision.js'; // // import removePrecision from './removePrecision.js'; -export default function(settings, sketch, matrix, constructLinePreview, onProgress) { - const totalStages = 12; +export default function(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) { + const totalStages = 11; let current = -1; const updateProgress = (action) => { current ++; @@ -36,9 +34,6 @@ export default function(settings, sketch, matrix, constructLinePreview, onProgre } }; - updateProgress('Generating geometry'); - const { geometry, open } = generateGeometry(sketch, matrix); - updateProgress('Constructing unique lines from geometry'); const { lines, faces } = createLines(geometry, settings); @@ -46,7 +41,7 @@ export default function(settings, sketch, matrix, constructLinePreview, onProgre const layers = calculateLayersIntersections(lines, settings); updateProgress('Constructing shapes from intersections'); - const shapes = intersectionsToShapes(layers, faces, open, settings); + const shapes = intersectionsToShapes(layers, faces, openObjectIndexes, settings); applyPrecision(shapes); diff --git a/src/slicer.js b/src/slicer.js index 54ecf01..ead168b 100644 --- a/src/slicer.js +++ b/src/slicer.js @@ -2,23 +2,63 @@ import { VertexColors } from 'three/src/constants.js'; import { BufferAttribute } from 'three/src/core/BufferAttribute.js'; import { LineBasicMaterial } from 'three/src/materials/LineBasicMaterial.js'; import { LineSegments } from 'three/src/objects/LineSegments.js'; -import _slice from './sliceActions/slice.js'; +import slice from './sliceActions/slice.js'; import SlicerWorker from './slicer.worker.js'; -import sketchDataToJSON from 'doodle3d-core/shape/sketchDataToJSON'; +import { FrontSide, DoubleSide } from 'three/src/constants.js'; +import { BufferGeometry } from 'three/src/core/BufferGeometry.js' -export function slice(settings, sketch, matrix, sync = false, constructLinePreview = false, onProgress) { - if (sync) { - return sliceSync(settings, sketch, matrix, constructLinePreview, onProgress); +export function sliceMesh(settings, mesh, sync = false, constructLinePreview = false, onProgress) { + if (!mesh || !mesh.isMesh) { + throw new Error('Provided mesh is not intance of THREE.Mesh'); + } + + mesh.updateMatrix(); + const { geometry, matrix, material } = mesh; + return sliceGeometry(settings, geometry, material, matrix, sync, constructLinePreview, onProgress); +} + +export function sliceGeometry(settings, geometry, materials, matrix, sync = false, constructLinePreview = false, onProgress) { + if (!geometry) { + throw new Error('Missing required geometry argument'); + } else if (geometry.isBufferGeometry) { + geometry = new Geometry().fromBufferGeometry(geometry); + } else if (geometry.isGeometry) { + geometry = geometry.clone(); } else { - return sliceAsync(settings, sketch, matrix, constructLinePreview, onProgress); + throw new Error('Geometry is not an instance of BufferGeometry or Geometry'); + } + + if (geometry.faces.length === 0) { + throw new Error('Geometry does not contain any data'); + } + + if (matrix && matrix.isMatrix4) { + geometry.applyMatrix(matrix); + } + + const openObjectIndexes = materials instanceof Array ? materials.map(({ side }) => { + switch (side) { + case FrontSide: + return false; + case DoubleSide: + return true; + default: + return false; + } + }) : [false]; + + if (sync) { + return sliceSync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress); + } else { + return sliceAsync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress); } } -export function sliceSync(settings, sketch, matrix, constructLinePreview, onProgress) { - return _slice(settings, sketch, matrix, constructLinePreview, onProgress); +function sliceSync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) { + return slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress); } -export function sliceAsync(settings, sketch, matrix, constructLinePreview, onProgress) { +function sliceAsync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) { return new Promise((resolve, reject) => { // create the slicer worker const slicerWorker = new SlicerWorker(); @@ -61,11 +101,10 @@ export function sliceAsync(settings, sketch, matrix, constructLinePreview, onPro }); // send geometry and settings to worker to start the slicing progress - matrix = matrix.toArray(); - sketch = sketchDataToJSON(sketch); + geometry = geometry.toJSON(); slicerWorker.postMessage({ message: 'SLICE', - data: { settings, sketch, matrix, constructLinePreview } + data: { settings, geometry, openObjectIndexes, constructLinePreview } }); }); } diff --git a/src/slicer.worker.js b/src/slicer.worker.js index 9de8a49..0168b7b 100644 --- a/src/slicer.worker.js +++ b/src/slicer.worker.js @@ -1,7 +1,7 @@ +import 'core-js'; // polyfills import slice from './sliceActions/slice.js'; import { Matrix4 } from 'three/src/math/Matrix4.js'; -import JSONToSketchData from 'doodle3d-core/shape/JSONToSketchData'; -import createSceneData from 'doodle3d-core/d3/createSceneData.js'; +import { JSONLoader } from 'three/src/loaders/JSONLoader.js'; const onProgress = progress => { self.postMessage({ @@ -10,17 +10,18 @@ const onProgress = progress => { }); } +const loader = new JSONLoader(); + self.addEventListener('message', async (event) => { const { message, data } = event.data; switch (message) { case 'SLICE': { + const { settings, geometry: JSONGeometry, constructLinePreview, openObjectIndexes } = data; + const { geometry } = loader.parse(JSONGeometry.data); + + const gcode = slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress); + const buffers = []; - const { settings, sketch: sketchData, matrix: matrixArray, constructLinePreview } = data; - const sketch = createSceneData(await JSONToSketchData(sketchData)); - const matrix = new Matrix4().fromArray(matrixArray); - - const gcode = slice(settings, sketch, matrix, constructLinePreview, onProgress); - if (gcode.linePreview) { const position = gcode.linePreview.geometry.getAttribute('position').array; const color = gcode.linePreview.geometry.getAttribute('color').array;