mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-21 21:27:56 +01:00
Slicer now slices d3sketch files instead of stl's
Easier to differentiate between open and closed shapes
This commit is contained in:
parent
db0d82c396
commit
b85781620e
18
index.js
18
index.js
@ -1,13 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { JSONLoader } from 'three/src/loaders/JSONLoader.js';
|
|
||||||
import { Interface } from 'doodle3d-slicer';
|
import { Interface } from 'doodle3d-slicer';
|
||||||
import fileURL from '!url-loader!./models/shape.json';
|
import doodleURL from '!url-loader!./models/Doodle_2.d3sketch';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
||||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||||
import jss from 'jss';
|
import jss from 'jss';
|
||||||
import preset from 'jss-preset-default';
|
import preset from 'jss-preset-default';
|
||||||
import normalize from 'normalize-jss';
|
import normalize from 'normalize-jss';
|
||||||
|
import JSONToSketchData from 'doodle3d-core/shape/JSONToSketchData';
|
||||||
|
import createSceneData from 'doodle3d-core/d3/createSceneData.js';
|
||||||
|
|
||||||
injectTapEventPlugin();
|
injectTapEventPlugin();
|
||||||
|
|
||||||
@ -22,11 +23,16 @@ jss.createStyleSheet({
|
|||||||
}
|
}
|
||||||
}).attach();
|
}).attach();
|
||||||
|
|
||||||
const jsonLoader = new JSONLoader();
|
function init(sketch) {
|
||||||
jsonLoader.load(fileURL, geometry => {
|
|
||||||
render((
|
render((
|
||||||
<MuiThemeProvider>
|
<MuiThemeProvider>
|
||||||
<Interface geometry={geometry} name="Doodle3D"/>
|
<Interface sketch={sketch} name="doodle"/>
|
||||||
</MuiThemeProvider>
|
</MuiThemeProvider>
|
||||||
), document.getElementById('app'));
|
), document.getElementById('app'));
|
||||||
});
|
}
|
||||||
|
|
||||||
|
fetch(doodleURL)
|
||||||
|
.then(resonse => resonse.json())
|
||||||
|
.then(json => JSONToSketchData(json))
|
||||||
|
.then(file => createSceneData(file))
|
||||||
|
.then(init);
|
||||||
|
1
models/Doodle.d3sketch
Normal file
1
models/Doodle.d3sketch
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"data":"{\"spaces\":[{\"matrix\":{\"metadata\":{\"type\":\"Matrix4\",\"library\":\"three.js\"},\"elements\":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},\"objects\":[{\"height\":9.266873708001008,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,26.586102719033242,0,1,-4.229607250755304]},\"z\":10.733126291998994,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":false,\"star\":{\"rays\":5,\"innerRadius\":20.54380664652568,\"outerRadius\":40.48338368580059},\"color\":6873597,\"type\":\"STAR\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-12.688821752265852,0,1,-12.68882175226588]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":true,\"star\":{\"rays\":5,\"innerRadius\":20.54380664652568,\"outerRadius\":40.48338368580059},\"color\":6873597,\"type\":\"STAR\"}]}]}","appVersion":"0.17.4"}
|
1
models/Doodle_2.d3sketch
Normal file
1
models/Doodle_2.d3sketch
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"data":"{\"spaces\":[{\"matrix\":{\"metadata\":{\"type\":\"Matrix4\",\"library\":\"three.js\"},\"elements\":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},\"objects\":[{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-32.27848101265822,0,1,5.3797468354430436]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":false,\"solid\":true,\"star\":{\"rays\":5,\"innerRadius\":10,\"outerRadius\":25},\"color\":6873597,\"type\":\"STAR\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,47.784810126582286,0,1,0.6329113924050631]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":true,\"star\":{\"rays\":5,\"innerRadius\":22.468354430379748,\"outerRadius\":25.9493670886076},\"color\":6873597,\"type\":\"STAR\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-46.83544303797467,0,1,9.810126582278485]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":false,\"solid\":false,\"rectSize\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Vector\"},\"x\":120.8860759493671,\"y\":34.49367088607595},\"color\":6873597,\"type\":\"RECT\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-47.1518987341772,0,1,-37.341772151898724]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":false,\"rectSize\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Vector\"},\"x\":120.8860759493671,\"y\":34.49367088607595},\"color\":6873597,\"type\":\"RECT\"}]}]}","appVersion":"0.17.4"}
|
2041
package-lock.json
generated
2041
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@ -16,6 +16,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@doodle3d/clipper-js": "^1.0.7",
|
"@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",
|
"lodash": "^4.17.4",
|
||||||
"material-ui": "^0.19.4",
|
"material-ui": "^0.19.4",
|
||||||
"proptypes": "^1.1.0",
|
"proptypes": "^1.1.0",
|
||||||
@ -26,30 +30,28 @@
|
|||||||
"three": "^0.88.0"
|
"three": "^0.88.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"raw-loader": "^0.5.1",
|
"babel-cli": "6.24.1",
|
||||||
|
"babel-loader": "7.0.0",
|
||||||
|
"babel-plugin-add-module-exports": "0.2.1",
|
||||||
"babel-plugin-inline-import": "^2.0.6",
|
"babel-plugin-inline-import": "^2.0.6",
|
||||||
"babel-preset-stage-0": "^6.24.1",
|
|
||||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
"babel-plugin-transform-es2015-classes": "^6.24.1",
|
"babel-plugin-transform-es2015-classes": "^6.24.1",
|
||||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||||
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
|
"babel-polyfill": "^6.26.0",
|
||||||
"babel-preset-env": "^1.6.1",
|
"babel-preset-env": "^1.6.1",
|
||||||
"babel-preset-react": "^6.24.1",
|
|
||||||
"babel-cli": "6.24.1",
|
|
||||||
"babel-core": "6.24.1",
|
|
||||||
"babel-loader": "7.0.0",
|
|
||||||
"babel-plugin-add-module-exports": "0.2.1",
|
|
||||||
"babel-preset-es2015": "6.24.1",
|
"babel-preset-es2015": "6.24.1",
|
||||||
"babel-polyfill": "^6.23.0",
|
"babel-preset-react": "^6.24.1",
|
||||||
"file-saver": "^1.3.3",
|
"babel-preset-stage-0": "^6.24.1",
|
||||||
|
"html-webpack-plugin": "^2.29.0",
|
||||||
"html-webpack-template": "^6.0.2",
|
"html-webpack-template": "^6.0.2",
|
||||||
"imports-loader": "^0.7.1",
|
"imports-loader": "^0.7.1",
|
||||||
"normalize-jss": "^4.0.0",
|
|
||||||
"material-ui": "^0.19.4",
|
"material-ui": "^0.19.4",
|
||||||
|
"normalize-jss": "^4.0.0",
|
||||||
|
"raw-loader": "^0.5.1",
|
||||||
"react-tap-event-plugin": "^3.0.2",
|
"react-tap-event-plugin": "^3.0.2",
|
||||||
"url-loader": "^0.5.9",
|
"url-loader": "^0.5.9",
|
||||||
"babel-plugin-transform-runtime": "^6.23.0",
|
|
||||||
"html-webpack-plugin": "^2.29.0",
|
|
||||||
"webpack": "^3.3.0",
|
"webpack": "^3.3.0",
|
||||||
"webpack-dev-server": "^2.5.1",
|
"webpack-dev-server": "^2.5.1",
|
||||||
"worker-loader": "^0.8.1",
|
"worker-loader": "^0.8.1",
|
||||||
|
@ -78,11 +78,10 @@ const styles = {
|
|||||||
|
|
||||||
class Interface extends React.Component {
|
class Interface extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
geometry(props, propName) {
|
sketch: PropTypes.shape({
|
||||||
if (!(props[propName].isGeometry || props[propName].isBufferGeometry)) {
|
data: PropTypes.string,
|
||||||
throw new Error('invalid prop, is not geometry');
|
appVersion: PropTypes.string
|
||||||
}
|
}),
|
||||||
},
|
|
||||||
classes: PropTypes.objectOf(PropTypes.string),
|
classes: PropTypes.objectOf(PropTypes.string),
|
||||||
defaultSettings: PropTypes.object.isRequired,
|
defaultSettings: PropTypes.object.isRequired,
|
||||||
printers: PropTypes.object.isRequired,
|
printers: PropTypes.object.isRequired,
|
||||||
@ -130,6 +129,7 @@ class Interface extends React.Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { canvas } = this.refs;
|
const { canvas } = this.refs;
|
||||||
|
|
||||||
const scene = createScene(canvas, this.props, this.state);
|
const scene = createScene(canvas, this.props, this.state);
|
||||||
this.setState({ ...scene });
|
this.setState({ ...scene });
|
||||||
}
|
}
|
||||||
@ -176,21 +176,22 @@ class Interface extends React.Component {
|
|||||||
|
|
||||||
slice = async () => {
|
slice = async () => {
|
||||||
const { mesh, settings, isSlicing, printers, quality, material } = this.state;
|
const { mesh, settings, isSlicing, printers, quality, material } = this.state;
|
||||||
const { name } = this.props;
|
const { name, sketch } = this.props;
|
||||||
|
|
||||||
if (isSlicing) return;
|
if (isSlicing) return;
|
||||||
|
|
||||||
this.setState({ isSlicing: true, progress: { action: '', slicing: 0, uploading: 0 }, error: null });
|
this.setState({ isSlicing: true, progress: { action: '', slicing: 0, uploading: 0 }, error: null });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await slice(name, mesh, settings, printers, quality, material, progress => {
|
await slice(name, sketch, mesh.matrix, settings, printers, quality, material, progress => {
|
||||||
this.setState({ progress: { ...this.state.progress, ...progress } });
|
this.setState({ progress: { ...this.state.progress, ...progress } });
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.setState({ error: error.message });
|
this.setState({ error: error.message });
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.setState({ isSlicing: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ isSlicing: false });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onChangeSettings = (settings) => {
|
onChangeSettings = (settings) => {
|
||||||
|
@ -15,9 +15,12 @@ import 'three/examples/js/controls/EditorControls';
|
|||||||
import printerSettings from '../settings/printer.yml';
|
import printerSettings from '../settings/printer.yml';
|
||||||
import materialSettings from '../settings/material.yml';
|
import materialSettings from '../settings/material.yml';
|
||||||
import qualitySettings from '../settings/quality.yml';
|
import qualitySettings from '../settings/quality.yml';
|
||||||
import { sliceGeometry } from '../slicer.js';
|
import { sliceAsync } from '../slicer.js';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import fileSaver from 'file-saver';
|
||||||
|
import { generateExportMesh } from 'doodle3d-core/utils/exportUtils.js';
|
||||||
|
import ShapesManager from 'doodle3d-core/d3/ShapesManager.js';
|
||||||
|
|
||||||
export function placeOnGround(mesh) {
|
export function placeOnGround(mesh) {
|
||||||
const boundingBox = new Box3().setFromObject(mesh);
|
const boundingBox = new Box3().setFromObject(mesh);
|
||||||
@ -27,8 +30,9 @@ export function placeOnGround(mesh) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createScene(canvas, props, state) {
|
export function createScene(canvas, props, state) {
|
||||||
const { geometry, pixelRatio } = props;
|
const { sketch, pixelRatio } = props;
|
||||||
const { controlMode, settings } = state;
|
const { settings } = state;
|
||||||
|
const { geometry } = generateExportMesh(sketch, { offsetSingleWalls: false, matrix: new THREE.Matrix4() });
|
||||||
|
|
||||||
// center geometry
|
// center geometry
|
||||||
geometry.computeBoundingBox();
|
geometry.computeBoundingBox();
|
||||||
@ -117,24 +121,24 @@ export function fetchProgress(url, { method = 'get', headers = {}, body = {} } =
|
|||||||
const GCODE_SERVER_URL = 'https://gcodeserver.doodle3d.com';
|
const GCODE_SERVER_URL = 'https://gcodeserver.doodle3d.com';
|
||||||
const CONNECT_URL = 'http://connect.doodle3d.com/';
|
const CONNECT_URL = 'http://connect.doodle3d.com/';
|
||||||
|
|
||||||
export async function slice(name, mesh, settings, printers, quality, material, updateProgress) {
|
export async function slice(name, sketch, matrix, settings, printers, quality, material, updateProgress) {
|
||||||
if (!printers) throw new Error('Please select a printer');
|
if (!printers) throw new Error('Please select a printer');
|
||||||
|
|
||||||
const { dimensions } = settings;
|
const { dimensions } = settings;
|
||||||
const centerX = dimensions.x / 2;
|
const centerX = dimensions.x / 2;
|
||||||
const centerY = dimensions.y / 2;
|
const centerY = dimensions.y / 2;
|
||||||
|
|
||||||
const geometry = mesh.geometry.clone();
|
matrix = new Matrix4().makeTranslation(centerY, 0, centerX).multiply(matrix);
|
||||||
mesh.updateMatrix();
|
const { gcode } = await sliceAsync(settings, sketch, matrix, false, ({ progress }) => {
|
||||||
|
|
||||||
const matrix = new Matrix4().makeTranslation(centerY, 0, centerX).multiply(mesh.matrix);
|
|
||||||
const { gcode } = await sliceGeometry(settings, geometry, matrix, false, false, ({ progress }) => {
|
|
||||||
updateProgress({
|
updateProgress({
|
||||||
action: progress.action,
|
action: progress.action,
|
||||||
slicing: progress.done / progress.total
|
slicing: progress.done / progress.total
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// const blob = new File([gcode], `${name}.gcode`, { type: 'text/plain;charset=utf-8' });
|
||||||
|
// fileSaver.saveAs(blob);
|
||||||
|
|
||||||
// upload G-code file to AWS S3
|
// upload G-code file to AWS S3
|
||||||
const { data: { reservation, id } } = await fetch(`${GCODE_SERVER_URL}/upload`, { method: 'POST' })
|
const { data: { reservation, id } } = await fetch(`${GCODE_SERVER_URL}/upload`, { method: 'POST' })
|
||||||
.then(response => response.json());
|
.then(response => response.json());
|
||||||
@ -174,19 +178,18 @@ export async function slice(name, mesh, settings, printers, quality, material, u
|
|||||||
if (!popup) throw new Error('popup was blocked by browser');
|
if (!popup) throw new Error('popup was blocked by browser');
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = {
|
export const TabTemplate = ({ children, selected, style }) => {
|
||||||
width: '100%',
|
const templateStyle = {
|
||||||
position: 'relative',
|
width: '100%',
|
||||||
textAlign: 'initial',
|
position: 'relative',
|
||||||
};
|
textAlign: 'initial',
|
||||||
|
...style,
|
||||||
export const TabTemplate = ({children, selected, style}) => {
|
...(selected ? {} : {
|
||||||
const templateStyle = Object.assign({}, styles, style);
|
height: 0,
|
||||||
if (!selected) {
|
width: 0,
|
||||||
templateStyle.height = 0;
|
overflow: 'hidden'
|
||||||
templateStyle.width = 0;
|
})
|
||||||
templateStyle.overflow = 'hidden';
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={templateStyle}>
|
<div style={templateStyle}>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
import { devide } from './helpers/VectorUtils.js';
|
||||||
import { PRECISION } from '../constants.js'
|
import { PRECISION } from '../constants.js'
|
||||||
|
|
||||||
export default function applyPrecision(shapes) {
|
export default function applyPrecision(layers) {
|
||||||
for (let i = 0; i < shapes.length; i ++) {
|
for (let layer = 0; layer < layers.length; layer ++) {
|
||||||
const { fillShapes, lineShapesOpen, lineShapesClosed } = shapes[i];
|
const { fillShapes, lineShapesOpen, lineShapesClosed } = layers[layer];
|
||||||
|
|
||||||
scaleUpShape(fillShapes);
|
scaleUpShape(fillShapes);
|
||||||
scaleUpShape(lineShapesOpen);
|
scaleUpShape(lineShapesOpen);
|
||||||
@ -15,9 +16,7 @@ function scaleUpShape(shape) {
|
|||||||
const path = shape[i];
|
const path = shape[i];
|
||||||
|
|
||||||
for (let i = 0; i < path.length; i ++) {
|
for (let i = 0; i < path.length; i ++) {
|
||||||
const point = path[i];
|
path[i] = devide(path[i], PRECISION);
|
||||||
|
|
||||||
point.copy(point.divideScalar(PRECISION));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { Vector2 } from 'three/src/math/Vector2.js';
|
|
||||||
|
|
||||||
export default function calculateLayersIntersections(lines, settings) {
|
export default function calculateLayersIntersections(lines, settings) {
|
||||||
const {
|
const {
|
||||||
dimensions: { z: dimensionsZ },
|
dimensions: { z: dimensionsZ },
|
||||||
@ -9,22 +7,19 @@ export default function calculateLayersIntersections(lines, settings) {
|
|||||||
|
|
||||||
const numLayers = Math.floor((dimensionsZ - zOffset) / layerHeight);
|
const numLayers = Math.floor((dimensionsZ - zOffset) / layerHeight);
|
||||||
|
|
||||||
const layerIntersectionIndexes = Array.from(Array(numLayers)).map(() => []);
|
const layers = Array.from(Array(numLayers)).map(() => ({
|
||||||
const layerIntersectionPoints = Array.from(Array(numLayers)).map(() => []);
|
points: {},
|
||||||
|
faceIndexes: []
|
||||||
|
}));
|
||||||
|
|
||||||
for (let lineIndex = 0; lineIndex < lines.length; lineIndex ++) {
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex ++) {
|
||||||
const { line, isFlat } = lines[lineIndex];
|
const { line, faces } = lines[lineIndex];
|
||||||
|
|
||||||
if (isFlat) continue;
|
|
||||||
|
|
||||||
const min = Math.ceil((Math.min(line.start.y, line.end.y) - zOffset) / layerHeight);
|
const min = Math.ceil((Math.min(line.start.y, line.end.y) - zOffset) / layerHeight);
|
||||||
const max = Math.floor((Math.max(line.start.y, line.end.y) - zOffset) / layerHeight);
|
const max = Math.floor((Math.max(line.start.y, line.end.y) - zOffset) / layerHeight);
|
||||||
|
|
||||||
for (let layerIndex = min; layerIndex <= max; layerIndex ++) {
|
for (let layerIndex = min; layerIndex <= max; layerIndex ++) {
|
||||||
if (layerIndex >= 0 && layerIndex < numLayers) {
|
if (layerIndex >= 0 && layerIndex < numLayers) {
|
||||||
|
|
||||||
layerIntersectionIndexes[layerIndex].push(lineIndex);
|
|
||||||
|
|
||||||
const y = layerIndex * layerHeight + zOffset;
|
const y = layerIndex * layerHeight + zOffset;
|
||||||
|
|
||||||
let x, z;
|
let x, z;
|
||||||
@ -38,10 +33,20 @@ export default function calculateLayersIntersections(lines, settings) {
|
|||||||
z = line.end.z * alpha + line.start.z * alpha1;
|
z = line.end.z * alpha + line.start.z * alpha1;
|
||||||
}
|
}
|
||||||
|
|
||||||
layerIntersectionPoints[layerIndex][lineIndex] = new Vector2(z, x);
|
layers[layerIndex].points[lineIndex] = { x: z, y: x };
|
||||||
|
layers[layerIndex].faceIndexes.push(...faces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { layerIntersectionIndexes, layerIntersectionPoints };
|
for (let i = 0; i < layers.length; i ++) {
|
||||||
|
const layer = layers[i];
|
||||||
|
|
||||||
|
layer.faceIndexes = layer.faceIndexes.reduce((result, faceIndex) => {
|
||||||
|
if (!result.includes(faceIndex)) result.push(faceIndex);
|
||||||
|
return result;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return layers;
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
import { Line3 } from 'three/src/math/Line3.js';
|
import { Line3 } from 'three/src/math/Line3.js';
|
||||||
import { Vector2 } from 'three/src/math/Vector2.js';
|
import { normalize } from './helpers/VectorUtils.js';
|
||||||
|
|
||||||
function addLine(geometry, lineLookup, lines, a, b, isFlat) {
|
function addLine(geometry, lineLookup, lines, a, b, faceIndex) {
|
||||||
const index = lines.length;
|
let index;
|
||||||
lineLookup[`${a}_${b}`] = index;
|
if (typeof lineLookup[`${b}_${a}`] !== 'undefined') {
|
||||||
|
index = lineLookup[`${b}_${a}`];
|
||||||
|
} else {
|
||||||
|
index = lines.length;
|
||||||
|
lineLookup[`${a}_${b}`] = index;
|
||||||
|
|
||||||
lines.push({
|
const line = new Line3(geometry.vertices[a], geometry.vertices[b]);
|
||||||
line: new Line3(geometry.vertices[a], geometry.vertices[b]),
|
lines.push({ line, faces: [] });
|
||||||
connects: [],
|
}
|
||||||
normals: [],
|
|
||||||
isFlat
|
const { faces } = lines[index];
|
||||||
});
|
faces.push(faceIndex);
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
@ -19,31 +23,20 @@ export default function createLines(geometry, settings) {
|
|||||||
const lines = [];
|
const lines = [];
|
||||||
const lineLookup = {};
|
const lineLookup = {};
|
||||||
|
|
||||||
for (let i = 0; i < geometry.faces.length; i ++) {
|
const faces = geometry.faces.map((face, i) => {
|
||||||
const face = geometry.faces[i];
|
const { normal, materialIndex: objectIndex, a, b, c } = geometry.faces[i];
|
||||||
|
|
||||||
const lookupA = lineLookup[`${face.b}_${face.a}`];
|
// skip faces that point up or down
|
||||||
const lookupB = lineLookup[`${face.c}_${face.b}`];
|
if (normal.y > .999 || normal.y < -.999) return;
|
||||||
const lookupC = lineLookup[`${face.a}_${face.c}`];
|
|
||||||
|
|
||||||
const isFlat = face.normal.y > 0.999 || face.normal.y < -0.999;
|
const indexA = addLine(geometry, lineLookup, lines, a, b, i);
|
||||||
|
const indexB = addLine(geometry, lineLookup, lines, b, c, i);
|
||||||
|
const indexC = addLine(geometry, lineLookup, lines, c, a, i);
|
||||||
|
|
||||||
// only add unique lines
|
const flatNormal = normalize({ x: normal.z, y: normal.x });
|
||||||
// returns index of said line
|
const lineIndexes = [indexA, indexB, indexC];
|
||||||
const lineIndexA = typeof lookupA !== 'undefined' ? lookupA : addLine(geometry, lineLookup, lines, face.a, face.b, isFlat);
|
return { lineIndexes, flatNormal, objectIndex };
|
||||||
const lineIndexB = typeof lookupB !== 'undefined' ? lookupB : addLine(geometry, lineLookup, lines, face.b, face.c, isFlat);
|
});
|
||||||
const lineIndexC = typeof lookupC !== 'undefined' ? lookupC : addLine(geometry, lineLookup, lines, face.c, face.a, isFlat);
|
|
||||||
|
|
||||||
// set connecting lines (based on face)
|
return { lines, faces };
|
||||||
lines[lineIndexA].connects.push(lineIndexB, lineIndexC);
|
|
||||||
lines[lineIndexB].connects.push(lineIndexC, lineIndexA);
|
|
||||||
lines[lineIndexC].connects.push(lineIndexA, lineIndexB);
|
|
||||||
|
|
||||||
const normal = new Vector2(face.normal.z, face.normal.x).normalize();
|
|
||||||
lines[lineIndexA].normals.push(normal);
|
|
||||||
lines[lineIndexB].normals.push(normal);
|
|
||||||
lines[lineIndexC].normals.push(normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines;
|
|
||||||
}
|
}
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
export default function detectOpenClosed(lines) {
|
|
||||||
const pools = getPools(lines);
|
|
||||||
const openLines = lines.map(line => line.connects.length === 2);
|
|
||||||
|
|
||||||
for (let i = 0; i < pools.length; i ++) {
|
|
||||||
const pool = pools[i];
|
|
||||||
|
|
||||||
const isOpenGeometry = pool.some(lineIndex => openLines[lineIndex]);
|
|
||||||
|
|
||||||
for (let j = 0; j < pool.length; j ++) {
|
|
||||||
const lineIndex = pool[j];
|
|
||||||
const line = lines[lineIndex];
|
|
||||||
line.openGeometry = isOpenGeometry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function findPool(pools, lines, lineIndex) {
|
|
||||||
const { connects } = lines[lineIndex];
|
|
||||||
for (let i = 0; i < pools.length; i ++) {
|
|
||||||
const pool = pools[i];
|
|
||||||
|
|
||||||
if (pool.find(lineIndex => connects.includes(lineIndex))) {
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no pool found
|
|
||||||
// create new pool
|
|
||||||
const pool = [];
|
|
||||||
pools.push(pool);
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPools(lines) {
|
|
||||||
const pools = [];
|
|
||||||
|
|
||||||
for (let lineIndex = 0; lineIndex < lines.length; lineIndex ++) {
|
|
||||||
const pool = findPool(pools, lines, lineIndex);
|
|
||||||
pool.push(lineIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < pools.length; i ++) {
|
|
||||||
const poolA = pools[i];
|
|
||||||
|
|
||||||
for (let j = i + 1; j < pools.length; j ++) {
|
|
||||||
const poolB = pools[j];
|
|
||||||
|
|
||||||
for (let k = 0; k < poolA.length; k ++) {
|
|
||||||
const { connects } = lines[poolA[k]];
|
|
||||||
|
|
||||||
if (poolB.find(lineIndex => connects.includes(lineIndex))) {
|
|
||||||
poolA.splice(poolA.length, 0, ...poolB);
|
|
||||||
poolB.splice(0, poolB.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pools.filter(pool => pool.length > 0);
|
|
||||||
}
|
|
28
src/sliceActions/generateGeometry.js
Normal file
28
src/sliceActions/generateGeometry.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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 };
|
||||||
|
}
|
@ -10,18 +10,23 @@ export const scale = (a, factor) => ({
|
|||||||
x: a.x * factor,
|
x: a.x * factor,
|
||||||
y: a.y * factor
|
y: a.y * factor
|
||||||
});
|
});
|
||||||
|
export const devide = (a, factor) => ({
|
||||||
|
x: a.x / factor,
|
||||||
|
y: a.y / factor
|
||||||
|
});
|
||||||
export const normal = (a) => ({
|
export const normal = (a) => ({
|
||||||
x: -a.y,
|
x: -a.y,
|
||||||
y: a.x
|
y: a.x
|
||||||
});
|
});
|
||||||
export const dot = (a, b) => a.x * b.x + a.y * b.y;
|
export const dot = (a, b) => a.x * b.x + a.y * b.y;
|
||||||
export const length = (a) => Math.sqrt(a.x * a.x + a.y * a.y);
|
export const length = (v) => Math.sqrt(v.x * v.x + v.y * v.y);
|
||||||
export const distanceTo = (a, b) => length(subtract(a, b));
|
export const distanceTo = (a, b) => length(subtract(a, b));
|
||||||
export const normalize = (a) => {
|
export const normalize = (v) => {
|
||||||
const l = length(a);
|
const l = length(v);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: a.x / l,
|
x: v.x / l,
|
||||||
y: a.y / l
|
y: v.y / l
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
export const clone = (v) => ({ x: v.x, y: v.y });
|
||||||
|
@ -1,120 +1,136 @@
|
|||||||
import { Vector2 } from 'three/src/math/Vector2.js';
|
import { subtract, normal, normalize, dot, distanceTo, clone } from './helpers/VectorUtils.js';
|
||||||
import Shape from 'clipper-js';
|
|
||||||
|
|
||||||
export default function intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings) {
|
export default function intersectionsToShapes(intersectionLayers, faces, open, settings) {
|
||||||
const layers = [];
|
const layers = [];
|
||||||
|
|
||||||
for (let layer = 1; layer < layerIntersectionIndexes.length; layer ++) {
|
for (let layer = 1; layer < intersectionLayers.length; layer ++) {
|
||||||
const intersectionIndexes = layerIntersectionIndexes[layer];
|
|
||||||
const intersectionPoints = layerIntersectionPoints[layer];
|
|
||||||
|
|
||||||
if (intersectionIndexes.length === 0) continue;
|
|
||||||
|
|
||||||
const fillShapes = [];
|
const fillShapes = [];
|
||||||
const lineShapesOpen = [];
|
const lineShapesOpen = [];
|
||||||
const lineShapesClosed = [];
|
const lineShapesClosed = [];
|
||||||
for (let i = 0; i < intersectionIndexes.length; i ++) {
|
|
||||||
let index = intersectionIndexes[i];
|
|
||||||
|
|
||||||
if (typeof intersectionPoints[index] === 'undefined') continue;
|
const { points, faceIndexes } = intersectionLayers[layer];
|
||||||
|
|
||||||
const shape = [];
|
if (faceIndexes.length === 0) continue;
|
||||||
|
|
||||||
const firstPoints = [index];
|
const shapes = {};
|
||||||
const { openGeometry } = lines[index];
|
|
||||||
let isFirstPoint = true;
|
|
||||||
let openShape = true;
|
|
||||||
|
|
||||||
while (index !== -1) {
|
for (let i = 0; i < faceIndexes.length; i ++) {
|
||||||
const intersection = intersectionPoints[index];
|
const { lineIndexes, objectIndex, flatNormal } = faces[faceIndexes[i]];
|
||||||
// uppercase X and Y because clipper vector
|
|
||||||
shape.push(intersection);
|
|
||||||
|
|
||||||
delete intersectionPoints[index];
|
const a = points[lineIndexes[0]];
|
||||||
|
const b = points[lineIndexes[1]];
|
||||||
|
const c = points[lineIndexes[2]];
|
||||||
|
|
||||||
const connects = lines[index].connects;
|
const lineSegment = [];
|
||||||
const faceNormals = lines[index].normals;
|
if (a && b) {
|
||||||
|
lineSegment.push(a, b);
|
||||||
|
} else if (b && c) {
|
||||||
|
lineSegment.push(b, c);
|
||||||
|
} else if (c && a) {
|
||||||
|
lineSegment.push(c, a);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < connects.length; i ++) {
|
const segmentNormal = normalize(normal(subtract(lineSegment[1], lineSegment[0])));
|
||||||
index = connects[i];
|
if (dot(segmentNormal, flatNormal) < 0) lineSegment.reverse();
|
||||||
|
|
||||||
if (firstPoints.includes(index) && shape.length > 2) {
|
if (!shapes[objectIndex]) shapes[objectIndex] = { lineSegments: [] };
|
||||||
openShape = false;
|
const shape = shapes[objectIndex];
|
||||||
index = -1;
|
|
||||||
break;
|
shape.lineSegments.push(lineSegment)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const objectIndex in shapes) {
|
||||||
|
const shape = shapes[objectIndex];
|
||||||
|
const openShape = open[objectIndex];
|
||||||
|
|
||||||
|
const lines = [shape.lineSegments.pop()];
|
||||||
|
|
||||||
|
loop: while (shape.lineSegments.length !== 0) {
|
||||||
|
for (let i = 0; i < lines.length; i ++) {
|
||||||
|
const line = lines[i];
|
||||||
|
|
||||||
|
const lastPoint = line[line.length - 1];
|
||||||
|
|
||||||
|
let closestSegmentEnd;
|
||||||
|
let endHit = false;
|
||||||
|
const distanceEnd = new WeakMap();
|
||||||
|
for (let i = 0; i < shape.lineSegments.length; i ++) {
|
||||||
|
const lineSegment = shape.lineSegments[i];
|
||||||
|
if (lastPoint === lineSegment[0]) {
|
||||||
|
closestSegmentEnd = lineSegment;
|
||||||
|
endHit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const distance = distanceTo(lastPoint, lineSegment[0]);
|
||||||
|
distanceEnd.set(lineSegment, distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if index has an intersection or is already used
|
if (!endHit) {
|
||||||
if (typeof intersectionPoints[index] !== 'undefined') {
|
closestSegmentEnd = shape.lineSegments.sort((a, b) => {
|
||||||
const faceNormal = faceNormals[Math.floor(i / 2)];
|
const distanceA = distanceEnd.get(a);
|
||||||
|
const distanceB = distanceEnd.get(b);
|
||||||
|
if (distanceA === distanceB) return distanceTo(a[0], a[1]) - distanceTo(b[0], b[1]);
|
||||||
|
return distanceA - distanceB;
|
||||||
|
})[0];
|
||||||
|
|
||||||
const a = new Vector2(intersection.x, intersection.y);
|
if (distanceTo(closestSegmentEnd[0], lastPoint) < .001) endHit = true;
|
||||||
const b = new Vector2(intersectionPoints[index].x, intersectionPoints[index].y);
|
}
|
||||||
|
|
||||||
// can't calculate normal between points if distance is smaller as 0.0001
|
if (endHit) {
|
||||||
if ((faceNormal.x === 0 && faceNormal.y === 0) || a.distanceTo(b) < 0.0001) {
|
shape.lineSegments.splice(shape.lineSegments.indexOf(closestSegmentEnd), 1);
|
||||||
if (isFirstPoint) {
|
line.splice(line.length, 0, closestSegmentEnd[1]);
|
||||||
firstPoints.push(index);
|
continue loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete intersectionPoints[index];
|
const firstPoint = line[0];
|
||||||
|
|
||||||
connects.push(...lines[index].connects);
|
let closestSegmentStart;
|
||||||
faceNormals.push(...lines[index].normals);
|
let hitStart = false;
|
||||||
index = -1;
|
const distanceStart = new WeakMap();
|
||||||
} else {
|
for (let i = 0; i < shape.lineSegments.length; i ++) {
|
||||||
// make sure the path goes the right direction
|
const lineSegment = shape.lineSegments[i];
|
||||||
// Vector2.normal is not yet implimented
|
if (firstPoint === lineSegment[1]) {
|
||||||
// const normal = a.sub(b).normal().normalize();
|
closestSegmentStart = lineSegment;
|
||||||
const normal = a.sub(b);
|
hitStart = true;
|
||||||
normal.set(-normal.y, normal.x).normalize();
|
break;
|
||||||
|
|
||||||
if (normal.dot(faceNormal) > 0) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
index = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
const distance = distanceTo(firstPoint, lineSegment[1]);
|
||||||
index = -1;
|
distanceStart.set(lineSegment, distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hitStart) {
|
||||||
|
closestSegmentStart = shape.lineSegments.sort((a, b) => {
|
||||||
|
const distanceA = distanceStart.get(a);
|
||||||
|
const distanceB = distanceStart.get(b);
|
||||||
|
if (distanceA === distanceB) return distanceTo(a[0], a[1]) - distanceTo(b[0], b[1]);
|
||||||
|
return distanceA - distanceB;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
if (distanceTo(closestSegmentStart[1], firstPoint) < .001) hitStart = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hitStart) {
|
||||||
|
shape.lineSegments.splice(shape.lineSegments.indexOf(closestSegmentStart), 1);
|
||||||
|
line.splice(0, 0, closestSegmentStart[0]);
|
||||||
|
continue loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isFirstPoint = false;
|
lines.push(shape.lineSegments.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openShape) {
|
if (openShape) {
|
||||||
index = firstPoints[0];
|
for (const line of lines) {
|
||||||
|
const closed = distanceTo(line[0], line[line.length - 1]) < .001;
|
||||||
while (index !== -1) {
|
if (closed) {
|
||||||
if (!firstPoints.includes(index)) {
|
lineShapesClosed.push(line);
|
||||||
const intersection = intersectionPoints[index];
|
} else {
|
||||||
shape.unshift(intersection);
|
lineShapesOpen.push(line);
|
||||||
|
|
||||||
delete intersectionPoints[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const connects = lines[index].connects;
|
|
||||||
|
|
||||||
for (let i = 0; i < connects.length; i ++) {
|
|
||||||
index = connects[i];
|
|
||||||
|
|
||||||
if (typeof intersectionPoints[index] !== 'undefined') {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
index = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (openGeometry) {
|
|
||||||
if (openShape) {
|
|
||||||
lineShapesOpen.push(shape);
|
|
||||||
} else {
|
|
||||||
lineShapesClosed.push(shape);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fillShapes.push(shape);
|
fillShapes.push(...lines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'babel-polyfill'
|
||||||
import { Color } from 'three/src/math/Color.js';
|
import { Color } from 'three/src/math/Color.js';
|
||||||
import { BufferGeometry } from 'three/src/core/BufferGeometry.js';
|
import { BufferGeometry } from 'three/src/core/BufferGeometry.js';
|
||||||
import { BufferAttribute } from 'three/src/core/BufferAttribute.js';
|
import { BufferAttribute } from 'three/src/core/BufferAttribute.js';
|
||||||
@ -15,11 +16,11 @@ import addBrim from './addBrim.js';
|
|||||||
import optimizePaths from './optimizePaths.js';
|
import optimizePaths from './optimizePaths.js';
|
||||||
import shapesToSlices from './shapesToSlices.js';
|
import shapesToSlices from './shapesToSlices.js';
|
||||||
import slicesToGCode from './slicesToGCode.js';
|
import slicesToGCode from './slicesToGCode.js';
|
||||||
import detectOpenClosed from './detectOpenClosed.js';
|
import generateGeometry from './generateGeometry.js';
|
||||||
import applyPrecision from './applyPrecision.js';
|
import applyPrecision from './applyPrecision.js';
|
||||||
// import removePrecision from './removePrecision.js';
|
// // import removePrecision from './removePrecision.js';
|
||||||
|
|
||||||
export default function(settings, geometry, constructLinePreview, onProgress) {
|
export default function(settings, sketch, matrix, constructLinePreview, onProgress) {
|
||||||
const totalStages = 12;
|
const totalStages = 12;
|
||||||
let current = -1;
|
let current = -1;
|
||||||
const updateProgress = (action) => {
|
const updateProgress = (action) => {
|
||||||
@ -35,23 +36,17 @@ export default function(settings, geometry, constructLinePreview, onProgress) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
geometry.computeFaceNormals();
|
updateProgress('Generating geometry');
|
||||||
|
const { geometry, open } = generateGeometry(sketch, matrix);
|
||||||
|
|
||||||
// get unique lines from geometry;
|
|
||||||
updateProgress('Constructing unique lines from geometry');
|
updateProgress('Constructing unique lines from geometry');
|
||||||
const lines = createLines(geometry, settings);
|
const { lines, faces } = createLines(geometry, settings);
|
||||||
|
|
||||||
updateProgress('Detecting open vs closed shapes');
|
|
||||||
detectOpenClosed(lines);
|
|
||||||
|
|
||||||
updateProgress('Calculating layer intersections');
|
updateProgress('Calculating layer intersections');
|
||||||
const {
|
const layers = calculateLayersIntersections(lines, settings);
|
||||||
layerIntersectionIndexes,
|
|
||||||
layerIntersectionPoints
|
|
||||||
} = calculateLayersIntersections(lines, settings);
|
|
||||||
|
|
||||||
updateProgress('Constructing shapes from intersections');
|
updateProgress('Constructing shapes from intersections');
|
||||||
const shapes = intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings);
|
const shapes = intersectionsToShapes(layers, faces, open, settings);
|
||||||
|
|
||||||
applyPrecision(shapes);
|
applyPrecision(shapes);
|
||||||
|
|
||||||
@ -91,7 +86,7 @@ function gcodeToString(gcode) {
|
|||||||
const value = command[action];
|
const value = command[action];
|
||||||
const currentValue = currentValues[action];
|
const currentValue = currentValues[action];
|
||||||
if (first) {
|
if (first) {
|
||||||
string += action + value;
|
string += `${action}${value}`;
|
||||||
first = false;
|
first = false;
|
||||||
} else if (currentValue !== value) {
|
} else if (currentValue !== value) {
|
||||||
string += ` ${action}${value}`;
|
string += ` ${action}${value}`;
|
||||||
|
@ -1,53 +1,24 @@
|
|||||||
import { Geometry } from 'three/src/core/Geometry.js';
|
|
||||||
import { BufferGeometry } from 'three/src/core/BufferGeometry.js';
|
|
||||||
import { VertexColors } from 'three/src/constants.js';
|
import { VertexColors } from 'three/src/constants.js';
|
||||||
import { BufferAttribute } from 'three/src/core/BufferAttribute.js';
|
import { BufferAttribute } from 'three/src/core/BufferAttribute.js';
|
||||||
import { LineBasicMaterial } from 'three/src/materials/LineBasicMaterial.js';
|
import { LineBasicMaterial } from 'three/src/materials/LineBasicMaterial.js';
|
||||||
import { LineSegments } from 'three/src/objects/LineSegments.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 SlicerWorker from './slicer.worker.js';
|
||||||
|
import sketchDataToJSON from 'doodle3d-core/shape/sketchDataToJSON';
|
||||||
|
|
||||||
export function sliceMesh(settings, mesh, sync = false, constructLinePreview = false, onProgress) {
|
export function slice(settings, sketch, matrix, 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 } = mesh;
|
|
||||||
return sliceGeometry(settings, geometry, matrix, sync, onProgress);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sliceGeometry(settings, geometry, 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 {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sync) {
|
if (sync) {
|
||||||
return sliceSync(settings, geometry, constructLinePreview, onProgress);
|
return sliceSync(settings, sketch, matrix, constructLinePreview, onProgress);
|
||||||
} else {
|
} else {
|
||||||
return sliceAsync(settings, geometry, constructLinePreview, onProgress);
|
return sliceAsync(settings, sketch, matrix, constructLinePreview, onProgress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sliceSync(settings, geometry, constructLinePreview, onProgress) {
|
export function sliceSync(settings, sketch, matrix, constructLinePreview, onProgress) {
|
||||||
return slice(settings, geometry, constructLinePreview, onProgress);
|
return _slice(settings, sketch, matrix, constructLinePreview, onProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sliceAsync(settings, geometry, constructLinePreview, onProgress) {
|
export function sliceAsync(settings, sketch, matrix, constructLinePreview, onProgress) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// create the slicer worker
|
// create the slicer worker
|
||||||
const slicerWorker = new SlicerWorker();
|
const slicerWorker = new SlicerWorker();
|
||||||
@ -90,14 +61,11 @@ function sliceAsync(settings, geometry, constructLinePreview, onProgress) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// send geometry and settings to worker to start the slicing progress
|
// send geometry and settings to worker to start the slicing progress
|
||||||
geometry = geometry.toJSON();
|
matrix = matrix.toArray();
|
||||||
|
sketch = sketchDataToJSON(sketch);
|
||||||
slicerWorker.postMessage({
|
slicerWorker.postMessage({
|
||||||
message: 'SLICE',
|
message: 'SLICE',
|
||||||
data: {
|
data: { settings, sketch, matrix, constructLinePreview }
|
||||||
settings,
|
|
||||||
geometry,
|
|
||||||
constructLinePreview
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import 'core-js'; // polyfills
|
|
||||||
import slice from './sliceActions/slice.js';
|
import slice from './sliceActions/slice.js';
|
||||||
import { JSONLoader } from 'three/src/loaders/JSONLoader.js';
|
import { Matrix4 } from 'three/src/math/Matrix4.js';
|
||||||
|
import JSONToSketchData from 'doodle3d-core/shape/JSONToSketchData';
|
||||||
const loader = new JSONLoader();
|
import createSceneData from 'doodle3d-core/d3/createSceneData.js';
|
||||||
|
|
||||||
const onProgress = progress => {
|
const onProgress = progress => {
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
@ -11,15 +10,16 @@ const onProgress = progress => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addEventListener('message', (event) => {
|
self.addEventListener('message', async (event) => {
|
||||||
const { message, data } = event.data;
|
const { message, data } = event.data;
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case 'SLICE': {
|
case 'SLICE': {
|
||||||
const buffers = [];
|
const buffers = [];
|
||||||
const { settings, geometry: JSONGeometry, constructLinePreview } = data;
|
const { settings, sketch: sketchData, matrix: matrixArray, constructLinePreview } = data;
|
||||||
const { geometry } = loader.parse(JSONGeometry.data);
|
const sketch = createSceneData(await JSONToSketchData(sketchData));
|
||||||
|
const matrix = new Matrix4().fromArray(matrixArray);
|
||||||
|
|
||||||
const gcode = slice(settings, geometry, constructLinePreview, onProgress);
|
const gcode = slice(settings, sketch, matrix, constructLinePreview, onProgress);
|
||||||
|
|
||||||
if (gcode.linePreview) {
|
if (gcode.linePreview) {
|
||||||
const position = gcode.linePreview.geometry.getAttribute('position').array;
|
const position = gcode.linePreview.geometry.getAttribute('position').array;
|
||||||
|
@ -2,6 +2,8 @@ const path = require('path');
|
|||||||
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||||
const HTMLWebpackPlugin = require('html-webpack-plugin');
|
const HTMLWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
|
||||||
|
const devMode = true;
|
||||||
|
|
||||||
const babelLoader = {
|
const babelLoader = {
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
@ -28,7 +30,9 @@ module.exports = {
|
|||||||
alias: {
|
alias: {
|
||||||
'doodle3d-slicer': path.resolve(__dirname, 'src/'),
|
'doodle3d-slicer': path.resolve(__dirname, 'src/'),
|
||||||
'clipper-lib': '@doodle3d/clipper-lib',
|
'clipper-lib': '@doodle3d/clipper-lib',
|
||||||
'clipper-js': '@doodle3d/clipper-js'
|
'clipper-js': '@doodle3d/clipper-js',
|
||||||
|
'doodle3d-core': `@doodle3d/doodle3d-core/${devMode ? 'module' : 'lib'}`,
|
||||||
|
'cal': '@doodle3d/cal'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
@ -46,6 +50,9 @@ module.exports = {
|
|||||||
}, {
|
}, {
|
||||||
test: /\.worker\.js$/,
|
test: /\.worker\.js$/,
|
||||||
use: ['worker-loader', babelLoader]
|
use: ['worker-loader', babelLoader]
|
||||||
|
}, {
|
||||||
|
test: /\.(png|jpg|gif)$/,
|
||||||
|
use: ['url-loader?name=images/[name].[ext]']
|
||||||
}, {
|
}, {
|
||||||
test: /\.glsl$/,
|
test: /\.glsl$/,
|
||||||
use: ['raw-loader']
|
use: ['raw-loader']
|
||||||
|
Loading…
Reference in New Issue
Block a user