mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-22 13:37:58 +01:00
Merge branch 'develop'
This commit is contained in:
commit
d60b8d1ddb
18
.babelrc
18
.babelrc
@ -1,19 +1,25 @@
|
||||
{
|
||||
"env": {
|
||||
// transpile to common node & browser compatible js, keeping modules
|
||||
"module": {
|
||||
"presets": [
|
||||
["latest", {
|
||||
["env", {
|
||||
"targets": { "node": "6" },
|
||||
"modules": false
|
||||
}]
|
||||
}],
|
||||
"stage-0",
|
||||
"react"
|
||||
]
|
||||
},
|
||||
// transpile to common node & browser compatible js, using commonjs
|
||||
"main": {
|
||||
"presets": ["latest"]
|
||||
"presets": ["env", "stage-0", "react"]
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"babel-plugin-transform-object-rest-spread"
|
||||
"babel-plugin-transform-regenerator",
|
||||
"babel-plugin-transform-object-rest-spread",
|
||||
"babel-plugin-inline-import",
|
||||
"babel-plugin-transform-class-properties",
|
||||
"babel-plugin-transform-es2015-classes",
|
||||
"babel-plugin-syntax-dynamic-import"
|
||||
]
|
||||
}
|
||||
|
@ -1,128 +0,0 @@
|
||||
import React from 'react';
|
||||
import { PRECISION } from '../src/constants.js';
|
||||
|
||||
export default class SlicerViewer extends React.Component {
|
||||
state = {
|
||||
layer: 0,
|
||||
render: {
|
||||
renderIntersectionPoints: false,
|
||||
renderShape1: false,
|
||||
renderShape2: true,
|
||||
renderOuterLine: true,
|
||||
renderInnerLine: true,
|
||||
renderFill: true
|
||||
}
|
||||
};
|
||||
|
||||
changeSlider = (event) => {
|
||||
this.setState({
|
||||
layer: parseInt(event.target.value)
|
||||
});
|
||||
};
|
||||
|
||||
onControl = (event) => {
|
||||
const section = event.target.value;
|
||||
this.setState({
|
||||
render: {
|
||||
...this.state.render,
|
||||
[section]: !this.state.render[section]
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { layer, render } = this.state;
|
||||
const { layerIntersectionPoints, settings, layerShapes, slices } = this.props;
|
||||
|
||||
const numLayers = settings.dimensionsZ / settings.layerHeight;
|
||||
|
||||
const intersectionPoints = layerIntersectionPoints[layer + 1];
|
||||
const shape = layerShapes[layer];
|
||||
const slice = slices[layer];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<svg viewBox={`-20 -20 ${settings.dimensionsX + 40} ${settings.dimensionsX + 40}`}>
|
||||
<rect
|
||||
width={settings.dimensionsX}
|
||||
height={settings.dimensionsY}
|
||||
fill="lightGrey"
|
||||
/>
|
||||
{render.renderIntersectionPoints && intersectionPoints.map(({ x, y }, i) => (
|
||||
<circle key={i} cx={x} cy={y} r="0.3"/>
|
||||
))}
|
||||
{render.renderShape1 && shape && shape.closedShapes.map((closedShape, i) => (
|
||||
<polygon
|
||||
key={i}
|
||||
points={closedShape.map(({ x, y }) => `${x} ${y}`).join(' ')}
|
||||
fill="rgba(255, 0, 0, 0.5)"
|
||||
/>
|
||||
))}
|
||||
{slice && slice.parts.map((slicePart, i) => (
|
||||
<g key={i}>
|
||||
{render.renderShape2 && <ClipperShapeSVG
|
||||
shape={slicePart.shape}
|
||||
scale={PRECISION}
|
||||
color="rgba(0, 0, 0, 0.5)"
|
||||
fill
|
||||
/>}
|
||||
{render.renderOuterLine && <ClipperShapeSVG
|
||||
shape={slicePart.outerLine}
|
||||
scale={1.0}
|
||||
color="blue"
|
||||
strokeWidth={settings.nozzleDiameter * 0.9}
|
||||
/>}
|
||||
{render.renderInnerLine && slicePart.innerLines.map((innerLine, i) => (
|
||||
<ClipperShapeSVG
|
||||
key={i}
|
||||
shape={innerLine}
|
||||
scale={1.0}
|
||||
color="red"
|
||||
strokeWidth={settings.nozzleDiameter * 0.9}
|
||||
/>
|
||||
))}
|
||||
{render.renderFill && <ClipperShapeSVG
|
||||
shape={slicePart.fill}
|
||||
scale={1.0}
|
||||
color="yellow"
|
||||
strokeWidth={settings.nozzleDiameter * 0.9}
|
||||
/>}
|
||||
</g>
|
||||
))}
|
||||
</svg>
|
||||
<div id="controls">
|
||||
<input onChange={this.changeSlider} value={layer} type="range" min="0" max={numLayers} />
|
||||
<p>Layer: {layer}</p>
|
||||
<p><label><input type="checkbox" value="renderIntersectionPoints" onChange={this.onControl} checked={render.renderIntersectionPoints} />Render Intersection Points</label></p>
|
||||
<p><label><input type="checkbox" value="renderShape1" onChange={this.onControl} checked={render.renderShape1} />Render Shape 1</label></p>
|
||||
<p><label><input type="checkbox" value="renderShape2" onChange={this.onControl} checked={render.renderShape2} />Render Shape 2</label></p>
|
||||
<p><label><input type="checkbox" value="renderOuterLine" onChange={this.onControl} checked={render.renderOuterLine} />Render Out Line</label></p>
|
||||
<p><label><input type="checkbox" value="renderInnerLine" onChange={this.onControl} checked={render.renderInnerLine} />Render Inner Lines</label></p>
|
||||
<p><label><input type="checkbox" value="renderFill" onChange={this.onControl} checked={render.renderFill} />Render Fill</label></p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ClipperShapeSVG extends React.Component {
|
||||
render() {
|
||||
const { shape, color = 'black', strokeWidth, scale, fill } = this.props;
|
||||
|
||||
const data = shape.paths.map(path => {
|
||||
const pathData = path.map(({ X, Y }, i) => `${i === 0 ? 'M' : 'L '}${X * scale} ${Y * scale}`);
|
||||
if (shape.closed) pathData.push('Z');
|
||||
return pathData.join(' ');
|
||||
}).join(' ');
|
||||
|
||||
return (
|
||||
<path
|
||||
d={data}
|
||||
strokeWidth={typeof strokeWidth === 'number' ? strokeWidth : 1.0}
|
||||
vectorEffect={typeof strokeWidth === 'number' ? 'none' : 'non-scaling-stroke'}
|
||||
fill={fill ? color : 'none'}
|
||||
stroke={!fill ? color : 'none'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
import calculateLayersIntersections from 'src/sliceActions/calculateLayersIntersections.js';
|
||||
import createLines from 'src/sliceActions/createLines.js';
|
||||
import generateInfills from 'src/sliceActions/generateInfills.js';
|
||||
import generateInnerLines from 'src/sliceActions/generateInnerLines.js';
|
||||
import generateSupport from 'src/sliceActions/generateSupport.js';
|
||||
import intersectionsToShapes from 'src/sliceActions/intersectionsToShapes.js';
|
||||
import addBrim from 'src/sliceActions/addBrim.js';
|
||||
import optimizePaths from 'src/sliceActions/optimizePaths.js';
|
||||
import shapesToSlices from 'src/sliceActions/shapesToSlices.js';
|
||||
import slicesToGCode from 'src/sliceActions/slicesToGCode.js';
|
||||
import applyPrecision from 'src/sliceActions/applyPrecision.js';
|
||||
import removePrecision from 'src/sliceActions/removePrecision.js';
|
||||
|
||||
export default function generateRawData(geometry, settings) {
|
||||
const rawData = {};
|
||||
|
||||
const lines = createLines(geometry, settings);
|
||||
|
||||
const {
|
||||
layerIntersectionIndexes,
|
||||
layerIntersectionPoints
|
||||
} = calculateLayersIntersections(lines, settings);
|
||||
|
||||
rawData.layerIntersectionPoints = layerIntersectionPoints
|
||||
.map(intersectionPoints => intersectionPoints.map(intersectionPoint => intersectionPoint.clone()));
|
||||
|
||||
const layerShapes = intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings);
|
||||
|
||||
rawData.layerShapes = layerShapes
|
||||
.map(({ closedShapes, openShapes }) => ({
|
||||
closedShapes: closedShapes.map(closedShape => closedShape.map(vector => vector.clone())),
|
||||
openShapes: openShapes.map(openShape => openShape.map(vector => vector.clone()))
|
||||
}));
|
||||
|
||||
|
||||
applyPrecision(layerShapes);
|
||||
|
||||
const slices = shapesToSlices(layerShapes, settings);
|
||||
|
||||
generateInnerLines(slices, settings);
|
||||
generateInfills(slices, settings);
|
||||
generateSupport(slices, settings);
|
||||
addBrim(slices, settings);
|
||||
optimizePaths(slices, settings);
|
||||
removePrecision(slices);
|
||||
|
||||
rawData.slices = slices;
|
||||
|
||||
const gcode = slicesToGCode(slices, settings);
|
||||
|
||||
rawData.gcode = gcode;
|
||||
|
||||
return rawData;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<!DOCTYPE>
|
||||
<html>
|
||||
<head>
|
||||
<title>Doodle3D Slicer</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p><a href="./viewer.html">Viewer</a></p>
|
||||
<p><a href="./save.html">Save</a></p>
|
||||
</body>
|
||||
</html>
|
33
example/index.js
Normal file
33
example/index.js
Normal file
@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { Interface } from 'doodle3d-slicer';
|
||||
import fileURL from '!url-loader!./models/shape.json';
|
||||
import { render } from 'react-dom';
|
||||
import fileSaver from 'file-saver';
|
||||
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
|
||||
injectTapEventPlugin();
|
||||
|
||||
document.body.style.margin = 0;
|
||||
document.body.style.padding = 0;
|
||||
document.body.style.height = '100%';
|
||||
document.documentElement.style.height = '100%'
|
||||
document.getElementById('app').style.height = '100%';
|
||||
|
||||
const downloadGCode = gcode => {
|
||||
const file = new File([gcode], 'gcode.gcode', { type: 'text/plain' });
|
||||
fileSaver.saveAs(file);
|
||||
};
|
||||
|
||||
const jsonLoader = new THREE.JSONLoader();
|
||||
jsonLoader.load(fileURL, geometry => {
|
||||
render((
|
||||
<MuiThemeProvider>
|
||||
<Interface
|
||||
geometry={geometry}
|
||||
onCompleteActions={[{ title: 'Download', callback: downloadGCode }]}
|
||||
/>
|
||||
</MuiThemeProvider>
|
||||
), document.getElementById('app'));
|
||||
});
|
@ -1,21 +0,0 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#container, svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
svg, #controls {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
width: 100%;
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -157,6 +157,11 @@
|
||||
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
|
||||
"dev": true
|
||||
},
|
||||
"asap": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
|
||||
},
|
||||
"asn1.js": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz",
|
||||
@ -711,6 +716,15 @@
|
||||
"regenerator-transform": "0.9.11"
|
||||
}
|
||||
},
|
||||
"babel-plugin-transform-runtime": {
|
||||
"version": "6.23.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz",
|
||||
"integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-runtime": "6.23.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-transform-strict-mode": {
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
|
||||
@ -926,6 +940,11 @@
|
||||
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
|
||||
"dev": true
|
||||
},
|
||||
"bowser": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/bowser/-/bowser-1.8.1.tgz",
|
||||
"integrity": "sha512-NMPaR8ILtdLSWzxQtEs16XbxMcY8ohWGQ5V+TZSJS3fNUt/PBAGkF6YWO9B/4qWE23bK3o0moQKq8UyFEosYkA=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
|
||||
@ -1107,6 +1126,11 @@
|
||||
"lazy-cache": "1.0.4"
|
||||
}
|
||||
},
|
||||
"chain-function": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.0.tgz",
|
||||
"integrity": "sha1-DUqzfn4Y6tC9xHuSB2QRjOWHM9w="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
@ -1120,6 +1144,11 @@
|
||||
"supports-color": "2.0.0"
|
||||
}
|
||||
},
|
||||
"change-emitter": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
|
||||
"integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
|
||||
@ -1340,6 +1369,14 @@
|
||||
"randombytes": "2.0.5"
|
||||
}
|
||||
},
|
||||
"css-in-js-utils": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-yuWmPMD9FLi50Xf3k8W8oO3WM1eVnxEGCldCLyfusQ+CgivFk0s23yst4ooW6tfxMuSa03S6uUEga9UhX6GRrA==",
|
||||
"requires": {
|
||||
"hyphenate-style-name": "1.0.2"
|
||||
}
|
||||
},
|
||||
"css-select": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
|
||||
@ -1515,6 +1552,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dom-helpers": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.2.1.tgz",
|
||||
"integrity": "sha1-MgPgf+0he9H0JLAZc1WC/Deyglo="
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
|
||||
@ -1596,6 +1638,14 @@
|
||||
"integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=",
|
||||
"dev": true
|
||||
},
|
||||
"encoding": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
|
||||
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
|
||||
"requires": {
|
||||
"iconv-lite": "0.4.19"
|
||||
}
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.3.0.tgz",
|
||||
@ -1895,6 +1945,27 @@
|
||||
"websocket-driver": "0.6.5"
|
||||
}
|
||||
},
|
||||
"fbjs": {
|
||||
"version": "0.8.16",
|
||||
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz",
|
||||
"integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=",
|
||||
"requires": {
|
||||
"core-js": "1.2.7",
|
||||
"isomorphic-fetch": "2.2.1",
|
||||
"loose-envify": "1.3.1",
|
||||
"object-assign": "4.1.1",
|
||||
"promise": "7.3.1",
|
||||
"setimmediate": "1.0.5",
|
||||
"ua-parser-js": "0.7.17"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
|
||||
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-saver": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.3.tgz",
|
||||
@ -3030,6 +3101,11 @@
|
||||
"minimalistic-crypto-utils": "1.0.1"
|
||||
}
|
||||
},
|
||||
"hoist-non-react-statics": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz",
|
||||
"integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA="
|
||||
},
|
||||
"home-or-tmp": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
|
||||
@ -3120,6 +3196,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"html-webpack-template": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-webpack-template/-/html-webpack-template-6.0.2.tgz",
|
||||
"integrity": "sha512-ekYCkU5t41wOu4kgGWvojVrREHap1qvZ8cbuy8ogH7EmscY4B0ElOEGQFFKpvig4GhhlVCK4mWaIik3dgz92SQ=="
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz",
|
||||
@ -3230,12 +3311,31 @@
|
||||
"integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=",
|
||||
"dev": true
|
||||
},
|
||||
"hyphenate-style-name": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz",
|
||||
"integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es="
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
|
||||
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
|
||||
"integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=",
|
||||
"dev": true
|
||||
},
|
||||
"imports-loader": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/imports-loader/-/imports-loader-0.7.1.tgz",
|
||||
"integrity": "sha1-8gS180cCoywdt9SNidXoZ6BEElM=",
|
||||
"requires": {
|
||||
"loader-utils": "1.1.0",
|
||||
"source-map": "0.5.6"
|
||||
}
|
||||
},
|
||||
"indent-string": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
|
||||
@ -3267,6 +3367,15 @@
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
},
|
||||
"inline-style-prefixer": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz",
|
||||
"integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=",
|
||||
"requires": {
|
||||
"bowser": "1.8.1",
|
||||
"css-in-js-utils": "2.0.0"
|
||||
}
|
||||
},
|
||||
"internal-ip": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz",
|
||||
@ -3438,6 +3547,11 @@
|
||||
"integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
|
||||
"dev": true
|
||||
},
|
||||
"is-stream": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
|
||||
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
|
||||
},
|
||||
"is-utf8": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
|
||||
@ -3459,11 +3573,19 @@
|
||||
"isarray": "1.0.0"
|
||||
}
|
||||
},
|
||||
"isomorphic-fetch": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
|
||||
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
|
||||
"requires": {
|
||||
"node-fetch": "1.7.3",
|
||||
"whatwg-fetch": "2.0.3"
|
||||
}
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
|
||||
"dev": true
|
||||
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.9.0",
|
||||
@ -3519,6 +3641,11 @@
|
||||
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
|
||||
"dev": true
|
||||
},
|
||||
"keycode": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.1.9.tgz",
|
||||
"integrity": "sha1-lkojxU5IiUBbSGGlyfBIDUUUHfo="
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||
@ -3588,6 +3715,16 @@
|
||||
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.merge": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.0.tgz",
|
||||
"integrity": "sha1-aYhLoUSsM/5plzemCG3v+t0PicU="
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"longest": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
|
||||
@ -3598,7 +3735,6 @@
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
|
||||
"integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-tokens": "3.0.2"
|
||||
}
|
||||
@ -3634,6 +3770,24 @@
|
||||
"integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
|
||||
"dev": true
|
||||
},
|
||||
"material-ui": {
|
||||
"version": "0.19.4",
|
||||
"resolved": "https://registry.npmjs.org/material-ui/-/material-ui-0.19.4.tgz",
|
||||
"integrity": "sha1-ypzcqKqLtZTfrF2zjsn/BFoyNYc=",
|
||||
"requires": {
|
||||
"babel-runtime": "6.23.0",
|
||||
"inline-style-prefixer": "3.0.8",
|
||||
"keycode": "2.1.9",
|
||||
"lodash.merge": "4.6.0",
|
||||
"lodash.throttle": "4.1.1",
|
||||
"prop-types": "15.6.0",
|
||||
"react-event-listener": "0.5.1",
|
||||
"react-transition-group": "1.2.1",
|
||||
"recompose": "0.26.0",
|
||||
"simple-assign": "0.1.0",
|
||||
"warning": "3.0.0"
|
||||
}
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@ -3828,6 +3982,15 @@
|
||||
"lower-case": "1.1.4"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
|
||||
"requires": {
|
||||
"encoding": "0.1.12",
|
||||
"is-stream": "1.1.0"
|
||||
}
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.6.33",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.6.33.tgz",
|
||||
@ -3912,8 +4075,7 @@
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||
"dev": true
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||
},
|
||||
"object.omit": {
|
||||
"version": "2.0.1",
|
||||
@ -4226,6 +4388,24 @@
|
||||
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
|
||||
"dev": true
|
||||
},
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
||||
"requires": {
|
||||
"asap": "2.0.6"
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.6.0",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz",
|
||||
"integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=",
|
||||
"requires": {
|
||||
"fbjs": "0.8.16",
|
||||
"loose-envify": "1.3.1",
|
||||
"object-assign": "4.1.1"
|
||||
}
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz",
|
||||
@ -4341,6 +4521,172 @@
|
||||
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
|
||||
"dev": true
|
||||
},
|
||||
"react": {
|
||||
"version": "16.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.1.0.tgz",
|
||||
"integrity": "sha512-hvKYlKqde2JNnNiEzORvSA0J1L7uSZ43l+J89ZNoP4EXxQrVNH0CFj8vorfPou3w+1ou1BNMBir2VVsuXtETRA==",
|
||||
"requires": {
|
||||
"fbjs": "0.8.16",
|
||||
"loose-envify": "1.3.1",
|
||||
"object-assign": "4.1.1",
|
||||
"prop-types": "15.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"asap": {
|
||||
"version": "2.0.6",
|
||||
"bundled": true
|
||||
},
|
||||
"core-js": {
|
||||
"version": "1.2.7",
|
||||
"bundled": true
|
||||
},
|
||||
"encoding": {
|
||||
"version": "0.1.12",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"iconv-lite": "0.4.19"
|
||||
}
|
||||
},
|
||||
"fbjs": {
|
||||
"version": "0.8.16",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"core-js": "1.2.7",
|
||||
"isomorphic-fetch": "2.2.1",
|
||||
"loose-envify": "1.3.1",
|
||||
"object-assign": "4.1.1",
|
||||
"promise": "7.3.1",
|
||||
"setimmediate": "1.0.5",
|
||||
"ua-parser-js": "0.7.17"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.19",
|
||||
"bundled": true
|
||||
},
|
||||
"is-stream": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
},
|
||||
"isomorphic-fetch": {
|
||||
"version": "2.2.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"node-fetch": "1.7.3",
|
||||
"whatwg-fetch": "2.0.3"
|
||||
}
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.3.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"js-tokens": "3.0.2"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "1.7.3",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"encoding": "0.1.12",
|
||||
"is-stream": "1.1.0"
|
||||
}
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"bundled": true
|
||||
},
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"asap": "2.0.6"
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.6.0",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"fbjs": "0.8.16",
|
||||
"loose-envify": "1.3.1",
|
||||
"object-assign": "4.1.1"
|
||||
}
|
||||
},
|
||||
"setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"bundled": true
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.17",
|
||||
"bundled": true
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "16.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.1.0.tgz",
|
||||
"integrity": "sha512-i9in5qW3H2PDinUPD9bnQK7tLAD8LhjYQ+fXi3nJOvVnxOO3ErHq6RNEnKY7pbjTPt155e74q7al8eBUuyLtew==",
|
||||
"requires": {
|
||||
"fbjs": "0.8.16",
|
||||
"loose-envify": "1.3.1",
|
||||
"object-assign": "4.1.1",
|
||||
"prop-types": "15.6.0"
|
||||
}
|
||||
},
|
||||
"react-event-listener": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.5.1.tgz",
|
||||
"integrity": "sha1-ujYHbke8N8Wmf/XM1Kn/DxViEEA=",
|
||||
"requires": {
|
||||
"babel-runtime": "6.26.0",
|
||||
"fbjs": "0.8.16",
|
||||
"prop-types": "15.6.0",
|
||||
"warning": "3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-runtime": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
||||
"requires": {
|
||||
"core-js": "2.4.1",
|
||||
"regenerator-runtime": "0.11.0"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
|
||||
"integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-tap-event-plugin": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-tap-event-plugin/-/react-tap-event-plugin-3.0.2.tgz",
|
||||
"integrity": "sha1-KANxZ3uIHDE3bgAnoLhtLG3gOe4=",
|
||||
"requires": {
|
||||
"fbjs": "0.8.16"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz",
|
||||
"integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==",
|
||||
"requires": {
|
||||
"chain-function": "1.0.0",
|
||||
"dom-helpers": "3.2.1",
|
||||
"loose-envify": "1.3.1",
|
||||
"prop-types": "15.6.0",
|
||||
"warning": "3.0.0"
|
||||
}
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||
@ -4410,6 +4756,17 @@
|
||||
"set-immediate-shim": "1.0.1"
|
||||
}
|
||||
},
|
||||
"recompose": {
|
||||
"version": "0.26.0",
|
||||
"resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz",
|
||||
"integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==",
|
||||
"requires": {
|
||||
"change-emitter": "0.1.6",
|
||||
"fbjs": "0.8.16",
|
||||
"hoist-non-react-statics": "2.3.1",
|
||||
"symbol-observable": "1.0.4"
|
||||
}
|
||||
},
|
||||
"redent": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
|
||||
@ -4696,8 +5053,7 @@
|
||||
"setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.0.3",
|
||||
@ -4720,6 +5076,11 @@
|
||||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
|
||||
"dev": true
|
||||
},
|
||||
"simple-assign": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-assign/-/simple-assign-0.1.0.tgz",
|
||||
"integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o="
|
||||
},
|
||||
"slash": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
|
||||
@ -4770,8 +5131,7 @@
|
||||
"source-map": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
|
||||
"integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
|
||||
"dev": true
|
||||
"integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI="
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.4.15",
|
||||
@ -4920,6 +5280,11 @@
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
},
|
||||
"symbol-observable": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz",
|
||||
"integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0="
|
||||
},
|
||||
"tapable": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.6.tgz",
|
||||
@ -4992,6 +5357,11 @@
|
||||
"mime-types": "2.1.15"
|
||||
}
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.17",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",
|
||||
"integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g=="
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "2.8.29",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
|
||||
@ -5158,6 +5528,14 @@
|
||||
"indexof": "0.0.1"
|
||||
}
|
||||
},
|
||||
"warning": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
|
||||
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
|
||||
"requires": {
|
||||
"loose-envify": "1.3.1"
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz",
|
||||
@ -5296,6 +5674,11 @@
|
||||
"integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
|
||||
"integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ="
|
||||
},
|
||||
"which-module": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
|
@ -12,10 +12,17 @@
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"file-saver": "^1.3.3",
|
||||
"html-webpack-template": "^6.0.2",
|
||||
"imports-loader": "^0.7.1",
|
||||
"material-ui": "^0.19.4",
|
||||
"react": "^16.1.0",
|
||||
"react-dom": "^16.1.0",
|
||||
"react-tap-event-plugin": "^3.0.2",
|
||||
"three": "^0.83.0",
|
||||
"url-loader": "^0.5.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-loader": "^7.1.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
@ -1,19 +0,0 @@
|
||||
<!DOCTYPE>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<title>Doodle3D Slicer - Save</title>
|
||||
|
||||
<script type="text/javascript" src="../jspm_packages/system.js"></script>
|
||||
<script type="text/javascript" src="../jspm.config.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
System.import('./save.js');
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,27 +0,0 @@
|
||||
import * as THREE from 'three';
|
||||
import { defaultSettings, sliceGeometry } from 'src/index.js';
|
||||
import fileSaver from 'file-saver';
|
||||
|
||||
const settings = {
|
||||
...defaultSettings.base,
|
||||
...defaultSettings.material.pla,
|
||||
...defaultSettings.printer.ultimaker2go,
|
||||
...defaultSettings.quality.high
|
||||
};
|
||||
|
||||
const jsonLoader = new THREE.JSONLoader();
|
||||
jsonLoader.load('models/airplane.json', async geometry => {
|
||||
geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / -2));
|
||||
geometry.applyMatrix(new THREE.Matrix4().setPosition(new THREE.Vector3(50, -0.0, 50)));
|
||||
geometry.computeFaceNormals();
|
||||
|
||||
const onProgress = ({ progress: { done, total, action } }) => {
|
||||
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' });
|
||||
fileSaver.saveAs(file);
|
||||
});
|
55582
example/stl/Airplane.stl
55582
example/stl/Airplane.stl
File diff suppressed because it is too large
Load Diff
94222
example/stl/Rocket.stl
94222
example/stl/Rocket.stl
File diff suppressed because it is too large
Load Diff
Binary file not shown.
36906
example/stl/traktor.stl
36906
example/stl/traktor.stl
File diff suppressed because it is too large
Load Diff
@ -1,29 +0,0 @@
|
||||
<!DOCTYPE>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<title>Doodle3D Slicer - Viewer</title>
|
||||
|
||||
<style>
|
||||
#gcode {
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript" src="../jspm_packages/system.js"></script>
|
||||
<script type="text/javascript" src="../jspm.config.js"></script>
|
||||
|
||||
<link href="main.css" rel="stylesheet"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
System.import('example/viewer.js');
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="container"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,35 +0,0 @@
|
||||
import 'three.js';
|
||||
import 'three.js/loaders/STLLoader';
|
||||
import React from 'react';
|
||||
import ReactDOM, { render } from 'react-dom';
|
||||
import * as SLICER from 'src/index.js';
|
||||
import generateRawData from './generateRawData.js';
|
||||
import SlicerViewer from './SlicerViewer.js';
|
||||
|
||||
const settings = new SLICER.Settings({
|
||||
...SLICER.printerSettings['ultimaker2go'],
|
||||
...SLICER.userSettings
|
||||
});
|
||||
|
||||
const stlLoader = new THREE.STLLoader();
|
||||
stlLoader.load('stl/Airplane.stl', (geometry) => {
|
||||
geometry = new THREE.Geometry().fromBufferGeometry(geometry);
|
||||
|
||||
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().scale(0.8));
|
||||
geometry.mergeVertices();
|
||||
geometry.computeFaceNormals();
|
||||
|
||||
const rawData = generateRawData(geometry, settings);
|
||||
|
||||
render(
|
||||
<SlicerViewer
|
||||
layerIntersectionPoints={rawData.layerIntersectionPoints}
|
||||
layerShapes={rawData.layerShapes}
|
||||
slices={rawData.slices}
|
||||
settings={settings}
|
||||
/>,
|
||||
document.getElementById('container')
|
||||
);
|
||||
});
|
@ -6,15 +6,17 @@ const babelLoader = {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
['latest', {
|
||||
'modules': false,
|
||||
'loose': true
|
||||
}]
|
||||
require('babel-preset-env'),
|
||||
require('babel-preset-react')
|
||||
],
|
||||
plugins: [
|
||||
require('babel-plugin-transform-object-rest-spread'),
|
||||
require('babel-plugin-transform-class-properties'),
|
||||
require('babel-plugin-transform-runtime')
|
||||
],
|
||||
plugins: [require('babel-plugin-transform-object-rest-spread')],
|
||||
babelrc: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
entry: './index.js',
|
||||
@ -35,12 +37,13 @@ module.exports = {
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: babelLoader
|
||||
},
|
||||
{
|
||||
}, { // make THREE global available to three.js examples
|
||||
test: /three\/examples\/.+\.js/,
|
||||
use: 'imports-loader?THREE=three'
|
||||
}, {
|
||||
test: /\.yml$/,
|
||||
use: 'yml-loader'
|
||||
},
|
||||
{
|
||||
}, {
|
||||
test: /\.worker\.js$/,
|
||||
use: ['worker-loader', babelLoader]
|
||||
}
|
||||
@ -48,7 +51,10 @@ module.exports = {
|
||||
},
|
||||
plugins: [
|
||||
new HTMLWebpackPlugin({
|
||||
title: 'Doodle3D Slicer - Simple example'
|
||||
title: 'Doodle3D Slicer - Simple example',
|
||||
template: require('html-webpack-template'),
|
||||
inject: false,
|
||||
appMountId: 'app'
|
||||
}),
|
||||
],
|
||||
devtool: "source-map",
|
1336
package-lock.json
generated
1336
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@ -15,12 +15,29 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@doodle3d/clipper-js": "^1.0.7",
|
||||
"lodash": "^4.17.4",
|
||||
"material-ui": "^0.19.4",
|
||||
"proptypes": "^1.1.0",
|
||||
"react": "^16.1.0",
|
||||
"react-dom": "^16.1.0",
|
||||
"react-jss": "^7.2.0",
|
||||
"react-resize-detector": "^1.1.0",
|
||||
"three": "^0.83.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
||||
"babel-preset-latest": "^6.24.1"
|
||||
"babel-plugin-inline-import": "^2.0.6",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-es2015-classes": "^6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"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"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -1,29 +0,0 @@
|
||||
import * as THREE from 'three';
|
||||
import { defaultSettings, sliceGeometry } from 'doodle3d-slicer';
|
||||
import fileURL from '!url-loader!./models/combingtest.json';
|
||||
import fileSaver from 'file-saver';
|
||||
|
||||
const settings = {
|
||||
...defaultSettings.base,
|
||||
...defaultSettings.material.pla,
|
||||
...defaultSettings.printer.ultimaker2go,
|
||||
...defaultSettings.quality.high
|
||||
};
|
||||
|
||||
const jsonLoader = new THREE.JSONLoader();
|
||||
jsonLoader.load(fileURL, geometry => {
|
||||
geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / -2));
|
||||
geometry.applyMatrix(new THREE.Matrix4().setPosition(new THREE.Vector3(50, -0.0, 50)));
|
||||
|
||||
const onProgress = ({ progress: { done, total, action } }) => {
|
||||
const percentage = `${(done / total * 100).toFixed()}%`
|
||||
document.write(`<p>${action}, ${percentage}</p>`);
|
||||
};
|
||||
|
||||
const { filament, duration, gcode } = sliceGeometry(settings, geometry, null, true, onProgress);
|
||||
// console.log('filament: ', filament);
|
||||
// console.log('duration: ', duration);
|
||||
// document.body.innerHTML = gcode.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
||||
const file = new File([gcode], 'gcode.gcode', { type: 'text/plain' });
|
||||
fileSaver.saveAs(file);
|
||||
});
|
@ -1,4 +1,5 @@
|
||||
import { sliceGeometry, sliceMesh } from './slicer.js';
|
||||
import Interface from './interface/index.js';
|
||||
import baseSettings from './settings/default.yml';
|
||||
import printerSettings from './settings/printer.yml';
|
||||
import materialSettings from './settings/material.yml';
|
||||
@ -14,5 +15,6 @@ const defaultSettings = {
|
||||
export {
|
||||
sliceGeometry,
|
||||
sliceMesh,
|
||||
Interface,
|
||||
defaultSettings
|
||||
};
|
||||
|
60
src/interface/FormComponents.js
Normal file
60
src/interface/FormComponents.js
Normal file
@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'proptypes';
|
||||
import _ from 'lodash';
|
||||
import injectSheet from 'react-jss';
|
||||
import MaterialUISelectField from 'material-ui/SelectField'
|
||||
import MaterialUICheckbox from 'material-ui/Checkbox';
|
||||
import MaterialUITextField from 'material-ui/TextField';
|
||||
import { grey100, grey300, grey500 } from 'material-ui/styles/colors';
|
||||
|
||||
const styles = {
|
||||
fieldSet: {
|
||||
border: 'none',
|
||||
backgroundColor: grey100,
|
||||
marginTop: '20px',
|
||||
'& legend': {
|
||||
border: `1px solid ${grey300}`,
|
||||
backgroundColor: 'white',
|
||||
padding: '3px 13px',
|
||||
color: grey500
|
||||
}
|
||||
}
|
||||
};
|
||||
export const SettingsGroup = injectSheet(styles)(({ name, classes, children }) => (
|
||||
<fieldset className={classes.fieldSet}>
|
||||
<legend>{name}</legend>
|
||||
{children}
|
||||
</fieldset>
|
||||
));
|
||||
SettingsGroup.propTypes = {
|
||||
classes: PropTypes.objectOf(PropTypes.string),
|
||||
name: PropTypes.string.isRequired,
|
||||
children: PropTypes.node
|
||||
};
|
||||
|
||||
export const SelectField = (props, context) => (
|
||||
<MaterialUISelectField
|
||||
{ ...props }
|
||||
value={context.state[props.name]}
|
||||
onChange={(event, index, value) => context.onChange(props.name, value)}
|
||||
/>
|
||||
);
|
||||
SelectField.contextTypes = { state: PropTypes.object, onChange: PropTypes.func };
|
||||
|
||||
export const TextField = (props, context) => (
|
||||
<MaterialUITextField
|
||||
{ ...props }
|
||||
value={_.get(context.state, props.name)}
|
||||
onChange={(event, value) => context.onChange(props.name, value)}
|
||||
/>
|
||||
);
|
||||
TextField.contextTypes = { state: PropTypes.object, onChange: PropTypes.func };
|
||||
|
||||
export const Checkbox = (props, context) => (
|
||||
<MaterialUICheckbox
|
||||
{ ...props }
|
||||
checked={_.get(context.state, props.name)}
|
||||
onCheck={(event, value) => context.onChange(props.name, value)}
|
||||
/>
|
||||
);
|
||||
Checkbox.contextTypes = { state: PropTypes.object, onChange: PropTypes.func };
|
157
src/interface/Settings.js
Normal file
157
src/interface/Settings.js
Normal file
@ -0,0 +1,157 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'proptypes';
|
||||
import _ from 'lodash';
|
||||
import { Tabs, Tab } from 'material-ui/Tabs';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import injectSheet from 'react-jss';
|
||||
import { SettingsGroup, SelectField, TextField, Checkbox } from './FormComponents.js';
|
||||
import { grey500 } from 'material-ui/styles/colors';
|
||||
|
||||
const styles = {
|
||||
textFieldRow: {
|
||||
display: 'flex'
|
||||
}
|
||||
};
|
||||
|
||||
class Settings extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
settings: props.initialSettings,
|
||||
printers: props.defaultPrinter,
|
||||
quality: props.defaultQuality,
|
||||
material: props.defaultMaterial
|
||||
};
|
||||
}
|
||||
|
||||
changeSettings = (fieldName, value) => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
let state;
|
||||
switch (fieldName) {
|
||||
case 'printers':
|
||||
case 'quality':
|
||||
case 'material':
|
||||
state = {
|
||||
[fieldName]: value,
|
||||
settings: _.merge({}, this.state.settings, this.props[fieldName][value])
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
state = _.set(_.cloneDeep(this.state), fieldName, value);
|
||||
break;
|
||||
}
|
||||
if (onChange) onChange(state.settings);
|
||||
if (state) this.setState(state);
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return { state: this.state, onChange: this.changeSettings };
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, printers, quality, material } = this.props;
|
||||
|
||||
return (
|
||||
<Tabs>
|
||||
<Tab label="basic">
|
||||
<div>
|
||||
<SelectField name="printers" floatingLabelText="Printer" fullWidth>
|
||||
{Object.entries(printers).map(([value, { title }]) => (
|
||||
<MenuItem key={value} value={value} primaryText={title} />
|
||||
))}
|
||||
</SelectField>
|
||||
<SelectField name="quality" floatingLabelText="Quality" fullWidth>
|
||||
{Object.entries(quality).map(([value, { title }]) => (
|
||||
<MenuItem key={value} value={value} primaryText={title} />
|
||||
))}
|
||||
</SelectField>
|
||||
<SelectField name="material" floatingLabelText="Material" fullWidth>
|
||||
{Object.entries(material).map(([value, { title }]) => (
|
||||
<MenuItem key={value} value={value} primaryText={title} />
|
||||
))}
|
||||
</SelectField>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab label="advanced">
|
||||
<div>
|
||||
<SettingsGroup name="Printer dimensions">
|
||||
<div className={classes.textFieldRow}>
|
||||
<TextField name="settings.dimensions.x" fullWidth floatingLabelText="X" type="number" />
|
||||
<TextField name="settings.dimensions.y" fullWidth floatingLabelText="Y" type="number" />
|
||||
<TextField name="settings.dimensions.z" fullWidth floatingLabelText="Z" type="number" />
|
||||
</div>
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Nozzle">
|
||||
<TextField name="settings.nozzleDiameter" fullWidth floatingLabelText="Diameter" type="number" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Bed">
|
||||
<TextField name="settings.bedTemperature" fullWidth floatingLabelText="Temperature" type="number" />
|
||||
<Checkbox name="settings.heatedBed" label="Heated" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Material">
|
||||
<TextField name="settings.filamentThickness" fullWidth floatingLabelText="Thickness" type="number" />
|
||||
<TextField name="settings.temperature" fullWidth floatingLabelText="Temperature" type="number" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Thickness">
|
||||
<TextField name="settings.thickness.top" fullWidth floatingLabelText="top" type="number" />
|
||||
<TextField name="settings.thickness.bottom" fullWidth floatingLabelText="bottom" type="number" />
|
||||
<TextField name="settings.thickness.shell" fullWidth floatingLabelText="shell" type="number" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Retraction">
|
||||
<Checkbox name="settings.retraction.enabled" label="Enabled" />
|
||||
<TextField name="settings.retraction.amount" fullWidth floatingLabelText="Amount" type="number" />
|
||||
<TextField name="settings.retraction.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||
<TextField name="settings.retraction.minDistance" fullWidth floatingLabelText="Min distance" type="number" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Travel">
|
||||
<TextField name="settings.travel.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||
<Checkbox name="settings.combing" label="Combing" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Inner shell">
|
||||
<TextField name="settings.innerShell.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||
<TextField name="settings.innerShell.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Outer shell">
|
||||
<TextField name="settings.outerShell.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||
<TextField name="settings.outerShell.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Inner infill">
|
||||
<TextField name="settings.innerInfill.gridSize" fullWidth floatingLabelText="Grid size" type="number" />
|
||||
<TextField name="settings.innerInfill.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||
<TextField name="settings.innerInfill.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Outer infill">
|
||||
<TextField name="settings.outerInfill.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||
<TextField name="settings.outerInfill.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="Brim">
|
||||
<TextField name="settings.brim.offset" fullWidth floatingLabelText="Offset" type="number" />
|
||||
<TextField name="settings.brim.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||
<TextField name="settings.brim.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||
</SettingsGroup>
|
||||
<SettingsGroup name="First layer">
|
||||
<TextField name="settings.firstLayer.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||
<TextField name="settings.firstLayer.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||
</SettingsGroup>
|
||||
</div>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
}
|
||||
Settings.childContextTypes = { state: PropTypes.object, onChange: PropTypes.func };
|
||||
Settings.propTypes = {
|
||||
classes: PropTypes.objectOf(PropTypes.string),
|
||||
onChange: PropTypes.func,
|
||||
printers: PropTypes.object.isRequired,
|
||||
defaultPrinter: PropTypes.string.isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
defaultQuality: PropTypes.string.isRequired,
|
||||
material: PropTypes.object.isRequired,
|
||||
defaultMaterial: PropTypes.string.isRequired,
|
||||
initialSettings: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default injectSheet(styles)(Settings);
|
272
src/interface/index.js
Normal file
272
src/interface/index.js
Normal file
@ -0,0 +1,272 @@
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import * as THREE from 'three';
|
||||
import PropTypes from 'proptypes';
|
||||
import { placeOnGround, createScene, createGcodeGeometry } from './utils.js';
|
||||
import injectSheet from 'react-jss';
|
||||
import { sliceGeometry } from '../slicer.js';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import Slider from 'material-ui/Slider';
|
||||
import { grey100, grey300 } from 'material-ui/styles/colors';
|
||||
import Settings from './Settings.js';
|
||||
import baseSettings from '../settings/default.yml';
|
||||
import printerSettings from '../settings/printer.yml';
|
||||
import materialSettings from '../settings/material.yml';
|
||||
import qualitySettings from '../settings/quality.yml';
|
||||
import ReactResizeDetector from 'react-resize-detector';
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
backgroundColor: grey100,
|
||||
overflow: 'hidden'
|
||||
},
|
||||
controlBar: {
|
||||
position: 'absolute',
|
||||
bottom: '10px',
|
||||
left: '10px'
|
||||
},
|
||||
d3View: {
|
||||
flexGrow: 1
|
||||
},
|
||||
canvas: {
|
||||
position: 'absolute'
|
||||
},
|
||||
sliceBar: {
|
||||
width: '240px',
|
||||
padding: '0 10px',
|
||||
overflowY: 'auto',
|
||||
backgroundColor: 'white',
|
||||
borderLeft: `1px solid ${grey300}`
|
||||
},
|
||||
overlay: {
|
||||
position: 'absolute',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
color: 'white',
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0
|
||||
},
|
||||
sliceActions: {
|
||||
listStyleType: 'none'
|
||||
},
|
||||
button: {
|
||||
margin: '5px 0'
|
||||
},
|
||||
controlButton: {
|
||||
marginRight: '2px'
|
||||
}
|
||||
};
|
||||
|
||||
class Interface extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { defaultPrinter, defaultQuality, defaultMaterial, printers, quality, material, defaultSettings } = props;
|
||||
this.state = {
|
||||
controlMode: 'translate',
|
||||
isSlicing: false,
|
||||
sliced: false,
|
||||
settings: _.merge(
|
||||
{},
|
||||
defaultSettings,
|
||||
printers[defaultPrinter],
|
||||
quality[defaultQuality],
|
||||
material[defaultMaterial]
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { canvas } = this.refs;
|
||||
const scene = createScene(canvas, this.props, this.state);
|
||||
this.setState(scene);
|
||||
}
|
||||
|
||||
resetMesh = () => {
|
||||
const { mesh, render } = this.state;
|
||||
if (mesh) {
|
||||
mesh.position.set(0, 0, 0);
|
||||
mesh.scale.set(1, 1, 1);
|
||||
mesh.rotation.set(0, 0, 0);
|
||||
mesh.updateMatrix();
|
||||
placeOnGround(mesh);
|
||||
render();
|
||||
}
|
||||
};
|
||||
|
||||
reset = () => {
|
||||
const { control, mesh, render, gcode, scene } = this.state;
|
||||
control.enabled = true;
|
||||
control.setSize(1);
|
||||
control.visible = true;
|
||||
mesh.visible = true;
|
||||
|
||||
scene.remove(gcode.linePreview);
|
||||
gcode.linePreview.geometry.dispose();
|
||||
|
||||
this.setState({ sliced: false, gcode: null });
|
||||
render();
|
||||
};
|
||||
|
||||
slice = async () => {
|
||||
const { mesh, render, scene, control, settings } = this.state;
|
||||
|
||||
const { dimensions } = settings;
|
||||
const centerX = dimensions.x / 2;
|
||||
const centerY = dimensions.y / 2;
|
||||
|
||||
const geometry = mesh.geometry.clone();
|
||||
mesh.updateMatrix();
|
||||
|
||||
this.setState({ isSlicing: true, progress: { actions: [], percentage: 0 } });
|
||||
|
||||
const matrix = new THREE.Matrix4().makeTranslation(centerY, 0, centerX).multiply(mesh.matrix);
|
||||
const gcode = await sliceGeometry(settings, geometry, matrix, false, true, ({ progress }) => {
|
||||
this.setState({ progress: {
|
||||
actions: [...this.state.progress.actions, progress.action],
|
||||
percentage: progress.done / progress.total
|
||||
} });
|
||||
});
|
||||
|
||||
this.setState({ isSlicing: false });
|
||||
|
||||
// TODO
|
||||
// can't disable control ui still interacts with mouse input
|
||||
control.enabled = false;
|
||||
// hack to disable control
|
||||
control.setSize(0);
|
||||
control.visible = false;
|
||||
mesh.visible = false;
|
||||
|
||||
gcode.linePreview.position.x = -centerY;
|
||||
gcode.linePreview.position.z = -centerX;
|
||||
scene.add(gcode.linePreview);
|
||||
|
||||
this.setState({ sliced: true, gcode });
|
||||
render();
|
||||
};
|
||||
|
||||
onChangeSettings = (settings) => {
|
||||
this.setState({ settings });
|
||||
};
|
||||
|
||||
updateDrawRange = (event, value) => {
|
||||
const { gcode, render } = this.state;
|
||||
gcode.linePreview.geometry.setDrawRange(0, value);
|
||||
render();
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.state.editorControls) this.state.editorControls.dispose();
|
||||
if (this.state.control) this.state.control.dispose();
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
const { control, box, render, setSize } = this.state;
|
||||
if (control && nextState.controlMode !== this.state.controlMode) control.setMode(nextState.controlMode);
|
||||
if (box && nextState.settings.dimensions !== this.state.settings.dimensions) {
|
||||
const { dimensions } = nextState.settings;
|
||||
box.scale.set(dimensions.y, dimensions.z, dimensions.x);
|
||||
render();
|
||||
}
|
||||
if (setSize && nextProps.width !== this.props.width || nextProps.height !== this.props.height || nextProps.pixelRatio !== this.props.pixelRatio) {
|
||||
setSize(nextProps.width, nextProps.height, nextProps.pixelRatio);
|
||||
}
|
||||
}
|
||||
|
||||
onResize = (width, height) => {
|
||||
window.requestAnimationFrame(() => {
|
||||
const { setSize } = this.state;
|
||||
const { pixelRatio } = this.props;
|
||||
setSize(width, height, pixelRatio);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { width, height, classes, onCompleteActions, defaultPrinter, defaultQuality, defaultMaterial } = this.props;
|
||||
const { sliced, isSlicing, progress, gcode, controlMode, settings } = this.state;
|
||||
|
||||
return (
|
||||
<div className={classes.container}>
|
||||
<div className={classes.d3View}>
|
||||
<ReactResizeDetector handleWidth handleHeight onResize={this.onResize} />
|
||||
<canvas className={classes.canvas} ref="canvas" width={width} height={height} />
|
||||
{!sliced && <div className={classes.controlBar}>
|
||||
<RaisedButton className={classes.controlButton} onTouchTap={this.resetMesh} primary label="reset" />
|
||||
<RaisedButton className={classes.controlButton} disabled={controlMode === 'translate'} onTouchTap={() => this.setState({ controlMode: 'translate' })} primary label="translate" />
|
||||
<RaisedButton className={classes.controlButton} disabled={controlMode === 'rotate'} onTouchTap={() => this.setState({ controlMode: 'rotate' })} primary label="rotate" />
|
||||
<RaisedButton className={classes.controlButton} disabled={controlMode === 'scale'} onTouchTap={() => this.setState({ controlMode: 'scale' })} primary label="scale" />
|
||||
</div>}
|
||||
</div>
|
||||
{sliced && <div className={classes.controlBar}>
|
||||
<Slider
|
||||
axis="y"
|
||||
style={{ height: '300px' }}
|
||||
step={2}
|
||||
min={1}
|
||||
max={gcode.linePreview.geometry.getAttribute('position').count}
|
||||
defaultValue={gcode.linePreview.geometry.getAttribute('position').count}
|
||||
onChange={this.updateDrawRange}
|
||||
/>
|
||||
</div>}
|
||||
{!sliced && <div className={classes.sliceBar}>
|
||||
<Settings
|
||||
printers={printerSettings}
|
||||
defaultPrinter={defaultPrinter}
|
||||
quality={qualitySettings}
|
||||
defaultQuality={defaultQuality}
|
||||
material={materialSettings}
|
||||
defaultMaterial={defaultMaterial}
|
||||
initialSettings={settings}
|
||||
onChange={this.onChangeSettings}
|
||||
/>
|
||||
<RaisedButton className={classes.button} fullWidth disabled={isSlicing} onTouchTap={this.slice} primary label="slice" />
|
||||
</div>}
|
||||
{sliced && <div className={classes.sliceBar}>
|
||||
<RaisedButton className={classes.button} fullWidth onTouchTap={this.reset} primary label="slice again" />
|
||||
{onCompleteActions.map(({ title, callback }, i) => (
|
||||
<RaisedButton className={classes.button} key={i} fullWidth onTouchTap={() => callback(gcode.gcode, settings)} primary label={title} />
|
||||
))}
|
||||
</div>}
|
||||
{isSlicing && <div className={classes.overlay}>
|
||||
<p>Slicing: {progress.percentage.toLocaleString(navigator.language, { style: 'percent' })}</p>
|
||||
<ul className={classes.sliceActions}>
|
||||
{progress.actions.map((action, i) => <li key={i}>{action}</li>)}
|
||||
</ul>
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Interface.propTypes = {
|
||||
geometry(props, propName) {
|
||||
if (!(props[propName].isGeometry || props[propName].isBufferGeometry)) {
|
||||
throw new Error('invalid prop, is not geometry');
|
||||
}
|
||||
},
|
||||
classes: PropTypes.objectOf(PropTypes.string),
|
||||
onCompleteActions: PropTypes.arrayOf(PropTypes.shape({ title: PropTypes.string, callback: PropTypes.func })).isRequired,
|
||||
defaultSettings: PropTypes.object.isRequired,
|
||||
printers: PropTypes.object.isRequired,
|
||||
defaultPrinter: PropTypes.string.isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
defaultQuality: PropTypes.string.isRequired,
|
||||
material: PropTypes.object.isRequired,
|
||||
defaultMaterial: PropTypes.string.isRequired,
|
||||
pixelRatio: PropTypes.number.isRequired
|
||||
};
|
||||
Interface.defaultProps = {
|
||||
defaultSettings: baseSettings,
|
||||
printers: printerSettings,
|
||||
defaultPrinter: 'ultimaker2',
|
||||
quality: qualitySettings,
|
||||
defaultQuality: 'medium',
|
||||
material: materialSettings,
|
||||
defaultMaterial: 'pla',
|
||||
pixelRatio: 1
|
||||
};
|
||||
|
||||
export default injectSheet(styles)(Interface);
|
82
src/interface/utils.js
Normal file
82
src/interface/utils.js
Normal file
@ -0,0 +1,82 @@
|
||||
import * as THREE from 'three';
|
||||
import 'three/examples/js/controls/EditorControls';
|
||||
import 'three/examples/js/controls/TransformControls';
|
||||
|
||||
export function placeOnGround(mesh) {
|
||||
const boundingBox = new THREE.Box3().setFromObject(mesh);
|
||||
|
||||
mesh.position.y -= boundingBox.min.y;
|
||||
mesh.updateMatrix();
|
||||
}
|
||||
|
||||
export function createScene(canvas, props, state) {
|
||||
const { geometry, pixelRatio } = props;
|
||||
const { controlMode, settings } = state;
|
||||
|
||||
// center geometry
|
||||
geometry.computeBoundingBox();
|
||||
const centerX = (geometry.boundingBox.max.x + geometry.boundingBox.min.x) / 2;
|
||||
const centerY = (geometry.boundingBox.max.y + geometry.boundingBox.min.y) / 2;
|
||||
const centerZ = (geometry.boundingBox.max.z + geometry.boundingBox.min.z) / 2;
|
||||
geometry.applyMatrix(new THREE.Matrix4().makeTranslation(-centerX, -centerY, -centerZ));
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true });
|
||||
renderer.setClearColor(0xffffff, 0);
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(50, 1, 1, 10000);
|
||||
camera.position.set(0, 400, 300);
|
||||
|
||||
const setSize = (width, height, pixelRatio = 1) => {
|
||||
renderer.setSize(width, height);
|
||||
renderer.setPixelRatio(pixelRatio);
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
render();
|
||||
};
|
||||
|
||||
const directionalLight = new THREE.DirectionalLight(0xd5d5d5);
|
||||
directionalLight.position.set(1, 1, 1);
|
||||
scene.add(directionalLight);
|
||||
|
||||
const light = new THREE.AmbientLight(0x808080);
|
||||
scene.add(light);
|
||||
|
||||
const mesh = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({ color: 0x2194ce }));
|
||||
placeOnGround(mesh);
|
||||
scene.add(mesh);
|
||||
|
||||
const editorControls = new THREE.EditorControls(camera, canvas);
|
||||
editorControls.focus(mesh);
|
||||
|
||||
const control = new THREE.TransformControls(camera, canvas);
|
||||
control.setMode(controlMode);
|
||||
control.setRotationSnap(THREE.Math.degToRad(45));
|
||||
control.addEventListener('mouseDown', () => editorControls.enabled = false);
|
||||
control.addEventListener('mouseUp', () => {
|
||||
editorControls.enabled = true;
|
||||
placeOnGround(mesh);
|
||||
});
|
||||
|
||||
control.attach(mesh);
|
||||
scene.add(control);
|
||||
|
||||
const render = () => {
|
||||
control.update();
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
control.addEventListener('change', render);
|
||||
editorControls.addEventListener('change', render);
|
||||
|
||||
const box = new THREE.BoxHelper();
|
||||
box.update(new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1).applyMatrix(new THREE.Matrix4().makeTranslation(0, 0.5, 0))));
|
||||
box.material.color.setHex(0x72bcd4);
|
||||
scene.add(box);
|
||||
|
||||
const { dimensions } = settings;
|
||||
box.scale.set(dimensions.y, dimensions.z, dimensions.x);
|
||||
|
||||
return { control, editorControls, scene, mesh, camera, renderer, render, box, setSize };
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
import * as THREE from 'three';
|
||||
import { PRECISION } from '../../constants.js';
|
||||
|
||||
const MOVE = 'G';
|
||||
const M_COMMAND = 'M';
|
||||
const FAN_SPEED = 'S';
|
||||
const SPEED = 'F';
|
||||
const EXTRUDER = 'E';
|
||||
const POSITION_X = 'X';
|
||||
const POSITION_Y = 'Y';
|
||||
const POSITION_Z = 'Z';
|
||||
export const MOVE = 'G';
|
||||
export const M_COMMAND = 'M';
|
||||
export const FAN_SPEED = 'S';
|
||||
export const SPEED = 'F';
|
||||
export const EXTRUDER = 'E';
|
||||
export const POSITION_X = 'X';
|
||||
export const POSITION_Y = 'Y';
|
||||
export const POSITION_Z = 'Z';
|
||||
|
||||
export default class {
|
||||
constructor(nozzleToFilamentRatio) {
|
||||
this._nozzleToFilamentRatio = nozzleToFilamentRatio;
|
||||
|
||||
this._gcode = '';
|
||||
this._gcode = [];
|
||||
this._currentValues = {};
|
||||
this._nozzlePosition = new THREE.Vector2(0, 0);
|
||||
this._extruder = 0.0;
|
||||
@ -24,24 +24,7 @@ export default class {
|
||||
}
|
||||
|
||||
_addGCode(command) {
|
||||
let str = '';
|
||||
|
||||
let first = true;
|
||||
for (const action in command) {
|
||||
const value = command[action];
|
||||
const currentValue = this._currentValues[action];
|
||||
if (first) {
|
||||
str = action + value;
|
||||
|
||||
first = false;
|
||||
} else if (currentValue !== value) {
|
||||
str += ` ${action}${value}`;
|
||||
|
||||
this._currentValues[action] = value;
|
||||
}
|
||||
}
|
||||
|
||||
this._gcode += `${str}\n`;
|
||||
this._gcode.push(command);
|
||||
}
|
||||
|
||||
turnFanOn(fanSpeed) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import * as THREE from 'three';
|
||||
import calculateLayersIntersections from './calculateLayersIntersections.js';
|
||||
import createLines from './createLines.js';
|
||||
import generateInfills from './generateInfills.js';
|
||||
@ -13,7 +14,7 @@ import detectOpenClosed from './detectOpenClosed.js';
|
||||
import applyPrecision from './applyPrecision.js';
|
||||
// import removePrecision from './removePrecision.js';
|
||||
|
||||
export default function(settings, geometry, onProgress) {
|
||||
export default function(settings, geometry, constructLinePreview, onProgress) {
|
||||
const totalStages = 12;
|
||||
let current = -1;
|
||||
const updateProgress = (action) => {
|
||||
@ -72,5 +73,67 @@ export default function(settings, geometry, onProgress) {
|
||||
|
||||
updateProgress('Finished');
|
||||
|
||||
if (constructLinePreview) gcode.linePreview = createGcodeGeometry(gcode.gcode);
|
||||
gcode.gcode = gcodeToString(gcode.gcode);
|
||||
return gcode;
|
||||
}
|
||||
|
||||
function gcodeToString(gcode) {
|
||||
const currentValues = {};
|
||||
return gcode.reduce((string, command) => {
|
||||
let first = true;
|
||||
for (const action in command) {
|
||||
const value = command[action];
|
||||
const currentValue = currentValues[action];
|
||||
if (first) {
|
||||
string += action + value;
|
||||
first = false;
|
||||
} else if (currentValue !== value) {
|
||||
string += ` ${action}${value}`;
|
||||
currentValues[action] = value;
|
||||
}
|
||||
}
|
||||
string += '\n';
|
||||
return string;
|
||||
}, '');
|
||||
}
|
||||
|
||||
const MAX_SPEED = 100 * 60;
|
||||
function createGcodeGeometry(gcode) {
|
||||
const positions = [];
|
||||
const colors = [];
|
||||
|
||||
let lastPoint
|
||||
for (let i = 0; i < gcode.length; i ++) {
|
||||
const { G, F, X, Y, Z } = gcode[i];
|
||||
|
||||
if (X || Y || Z) {
|
||||
let color;
|
||||
if (G === 0) {
|
||||
color = new THREE.Color(0x00ff00);
|
||||
} else if (G === 1) {
|
||||
color = new THREE.Color().setHSL(F / MAX_SPEED, 0.5, 0.5);
|
||||
}
|
||||
|
||||
if (G === 1) {
|
||||
if (lastPoint) positions.push(lastPoint[0], lastPoint[1], lastPoint[2]);
|
||||
positions.push(Y, Z, X);
|
||||
|
||||
colors.push(color.r, color.g, color.b);
|
||||
colors.push(color.r, color.g, color.b);
|
||||
}
|
||||
|
||||
lastPoint = [Y, Z, X];
|
||||
}
|
||||
}
|
||||
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
|
||||
geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3));
|
||||
geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3));
|
||||
|
||||
const material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });
|
||||
const linePreview = new THREE.LineSegments(geometry, material);
|
||||
|
||||
return linePreview;
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ import * as THREE from 'three';
|
||||
import slice from './sliceActions/slice.js';
|
||||
import SlicerWorker from './slicer.worker.js';
|
||||
|
||||
export function sliceMesh(settings, mesh, sync = false, onProgress) {
|
||||
if (typeof mesh === 'undefined' || !mesh.isMesh) {
|
||||
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');
|
||||
}
|
||||
|
||||
@ -12,8 +12,8 @@ export function sliceMesh(settings, mesh, sync = false, onProgress) {
|
||||
return sliceGeometry(settings, geometry, matrix, sync, onProgress);
|
||||
}
|
||||
|
||||
export function sliceGeometry(settings, geometry, matrix, sync = false, onProgress) {
|
||||
if (typeof geometry === 'undefined') {
|
||||
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 THREE.Geometry().fromBufferGeometry(geometry);
|
||||
@ -27,22 +27,22 @@ export function sliceGeometry(settings, geometry, matrix, sync = false, onProgre
|
||||
throw new Error('Geometry does not contain any data');
|
||||
}
|
||||
|
||||
if (matrix) {
|
||||
if (matrix && matrix.isMatrix4) {
|
||||
geometry.applyMatrix(matrix);
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
return sliceSync(settings, geometry, onProgress);
|
||||
return sliceSync(settings, geometry, constructLinePreview, onProgress);
|
||||
} else {
|
||||
return sliceAsync(settings, geometry, onProgress);
|
||||
return sliceAsync(settings, geometry, constructLinePreview, onProgress);
|
||||
}
|
||||
}
|
||||
|
||||
function sliceSync(settings, geometry, onProgress) {
|
||||
return slice(settings, geometry, onProgress);
|
||||
function sliceSync(settings, geometry, constructLinePreview, onProgress) {
|
||||
return slice(settings, geometry, constructLinePreview, onProgress);
|
||||
}
|
||||
|
||||
function sliceAsync(settings, geometry, onProgress) {
|
||||
function sliceAsync(settings, geometry, constructLinePreview, onProgress) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// create the slicer worker
|
||||
const slicerWorker = new SlicerWorker();
|
||||
@ -58,6 +58,20 @@ function sliceAsync(settings, geometry, onProgress) {
|
||||
switch (message) {
|
||||
case 'SLICE': {
|
||||
slicerWorker.terminate();
|
||||
|
||||
if (data.gcode.linePreview) {
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
|
||||
const { position, color } = data.gcode.linePreview;
|
||||
geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(position), 3));
|
||||
geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(color), 3));
|
||||
|
||||
const material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });
|
||||
const linePreview = new THREE.LineSegments(geometry, material);
|
||||
|
||||
data.gcode.linePreview = linePreview;
|
||||
}
|
||||
|
||||
resolve(data.gcode);
|
||||
break;
|
||||
}
|
||||
@ -76,7 +90,8 @@ function sliceAsync(settings, geometry, onProgress) {
|
||||
message: 'SLICE',
|
||||
data: {
|
||||
settings,
|
||||
geometry
|
||||
geometry,
|
||||
constructLinePreview
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -15,15 +15,23 @@ self.addEventListener('message', (event) => {
|
||||
const { message, data } = event.data;
|
||||
switch (message) {
|
||||
case 'SLICE': {
|
||||
const { settings, geometry: JSONGeometry } = data;
|
||||
const buffers = [];
|
||||
const { settings, geometry: JSONGeometry, constructLinePreview } = data;
|
||||
const { geometry } = loader.parse(JSONGeometry.data);
|
||||
|
||||
const gcode = slice(settings, geometry, onProgress);
|
||||
const gcode = slice(settings, geometry, constructLinePreview, onProgress);
|
||||
|
||||
if (gcode.linePreview) {
|
||||
const position = gcode.linePreview.geometry.getAttribute('position').array;
|
||||
const color = gcode.linePreview.geometry.getAttribute('color').array;
|
||||
buffers.push(position.buffer, color.buffer);
|
||||
gcode.linePreview = { position, color };
|
||||
}
|
||||
|
||||
self.postMessage({
|
||||
message: 'SLICE',
|
||||
data: { gcode }
|
||||
});
|
||||
}, buffers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user