mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-22 13:37:58 +01:00
Merge branch 'master' into develop
This commit is contained in:
commit
6f735b5de4
15
.babelrc
15
.babelrc
@ -3,7 +3,10 @@
|
|||||||
"module": {
|
"module": {
|
||||||
"presets": [
|
"presets": [
|
||||||
["env", {
|
["env", {
|
||||||
"targets": { "node": "6" },
|
"targets": {
|
||||||
|
"node": "6",
|
||||||
|
"browsers": ["last 2 versions", "safari >= 7", "not ie < 11"]
|
||||||
|
},
|
||||||
"modules": false
|
"modules": false
|
||||||
}],
|
}],
|
||||||
"stage-0",
|
"stage-0",
|
||||||
@ -15,11 +18,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"babel-plugin-transform-regenerator",
|
"transform-class-properties",
|
||||||
"babel-plugin-transform-object-rest-spread",
|
"transform-object-rest-spread",
|
||||||
"babel-plugin-inline-import",
|
"transform-runtime",
|
||||||
"babel-plugin-transform-class-properties",
|
"transform-es2015-classes"
|
||||||
"babel-plugin-transform-es2015-classes",
|
|
||||||
"babel-plugin-syntax-dynamic-import"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
|
|
||||||
jspm_package
|
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
lib
|
lib
|
||||||
|
@ -1,4 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
jspm_packages
|
jspm_packages
|
||||||
example
|
|
||||||
simpleExample
|
|
@ -9,7 +9,7 @@ import * as THREE from 'three';
|
|||||||
import { defaultSettings, sliceGeometry } from 'Doodle3D/Doodle3D-Slicer';
|
import { defaultSettings, sliceGeometry } from 'Doodle3D/Doodle3D-Slicer';
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
...defaultSettings.base,
|
...defaultSettings.default,
|
||||||
...defaultSettings.material.pla,
|
...defaultSettings.material.pla,
|
||||||
...defaultSettings.printer.ultimaker2go,
|
...defaultSettings.printer.ultimaker2go,
|
||||||
...defaultSettings.quality.high
|
...defaultSettings.quality.high
|
||||||
@ -27,7 +27,7 @@ const gcode = await sliceGeometry(settings, geometry);
|
|||||||
import { defaultSettings } from 'Doodle3D/Doodle3D-Slicer';
|
import { defaultSettings } from 'Doodle3D/Doodle3D-Slicer';
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
...defaultSettings.base,
|
...defaultSettings.default,
|
||||||
...defaultSettings.material.pla,
|
...defaultSettings.material.pla,
|
||||||
...defaultSettings.printer.ultimaker2go,
|
...defaultSettings.printer.ultimaker2go,
|
||||||
...defaultSettings.quality.high
|
...defaultSettings.quality.high
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
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'));
|
|
||||||
});
|
|
5812
example/package-lock.json
generated
5812
example/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "doodle3d-simple-example",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "webpack -p",
|
|
||||||
"start": "webpack-dev-server -w"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"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",
|
|
||||||
"babel-preset-latest": "^6.24.1",
|
|
||||||
"html-webpack-plugin": "^2.29.0",
|
|
||||||
"webpack": "^3.3.0",
|
|
||||||
"webpack-dev-server": "^2.5.1",
|
|
||||||
"worker-loader": "^0.8.1",
|
|
||||||
"yml-loader": "^2.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
42
index.js
Normal file
42
index.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import 'babel-polyfill'
|
||||||
|
import React from 'react';
|
||||||
|
import { Interface } from 'doodle3d-slicer';
|
||||||
|
import doodleURL from '!url-loader!./models/Doodle_2.d3sketch';
|
||||||
|
import { render } from 'react-dom';
|
||||||
|
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
||||||
|
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||||
|
import jss from 'jss';
|
||||||
|
import preset from 'jss-preset-default';
|
||||||
|
import normalize from 'normalize-jss';
|
||||||
|
import JSONToSketchData from 'doodle3d-core/shape/JSONToSketchData';
|
||||||
|
import createSceneData from 'doodle3d-core/d3/createSceneData.js';
|
||||||
|
import { generateExportMesh } from 'doodle3d-core/utils/exportUtils.js';
|
||||||
|
import { Matrix4 } from 'three/src/math/Matrix4.js';
|
||||||
|
|
||||||
|
injectTapEventPlugin();
|
||||||
|
|
||||||
|
jss.setup(preset());
|
||||||
|
jss.createStyleSheet(normalize).attach();
|
||||||
|
jss.createStyleSheet({
|
||||||
|
'@global': {
|
||||||
|
'*': { margin: 0, padding: 0 },
|
||||||
|
'#app, body, html': { height: '100%', fontFamily: 'sans-serif' },
|
||||||
|
body: { overflow: 'auto' },
|
||||||
|
html: { overflow: 'hidden' }
|
||||||
|
}
|
||||||
|
}).attach();
|
||||||
|
|
||||||
|
function init(mesh) {
|
||||||
|
render((
|
||||||
|
<MuiThemeProvider>
|
||||||
|
<Interface mesh={mesh} name="doodle"/>
|
||||||
|
</MuiThemeProvider>
|
||||||
|
), document.getElementById('app'));
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(doodleURL)
|
||||||
|
.then(resonse => resonse.json())
|
||||||
|
.then(json => JSONToSketchData(json))
|
||||||
|
.then(file => createSceneData(file))
|
||||||
|
.then(sketch => generateExportMesh(sketch, { offsetSingleWalls: false, matrix: new Matrix4() }))
|
||||||
|
.then(init);
|
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"}
|
5710
package-lock.json
generated
5710
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
40
package.json
40
package.json
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@doodle3d/doodle3d-slicer",
|
"name": "@doodle3d/doodle3d-slicer",
|
||||||
"version": "0.0.13",
|
"version": "0.0.18",
|
||||||
"description": "JavaScript gcode slicer, Intended to use with the Doodle3D WiFi-Box # Usage",
|
"description": "JavaScript gcode slicer, Intended to use with the Doodle3D WiFi-Box # Usage",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"module": "module/index.js",
|
"module": "module/index.js",
|
||||||
"esnext": "src/index.js",
|
"esnext": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"start": "webpack-dev-server -w",
|
||||||
"prepare": "npm run build",
|
"prepare": "npm run build",
|
||||||
"build": "npm run build:main && npm run build:main:settings && npm run build:module && npm run build:module:settings ",
|
"build": "npm run build:main && npm run build:main:settings && npm run build:module && npm run build:module:settings ",
|
||||||
"build:main": "BABEL_ENV=main babel src -s -d lib",
|
"build:main": "BABEL_ENV=main babel src -s -d lib",
|
||||||
@ -15,29 +16,42 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@doodle3d/clipper-js": "^1.0.7",
|
"@doodle3d/clipper-js": "^1.0.7",
|
||||||
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
|
"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",
|
||||||
"react": "^16.1.0",
|
"react": "^16.0.0",
|
||||||
"react-dom": "^16.1.0",
|
"react-dom": "^16.0.0",
|
||||||
"react-jss": "^7.2.0",
|
"react-jss": "^7.2.0",
|
||||||
"react-resize-detector": "^1.1.0",
|
"react-resize-detector": "^1.1.0",
|
||||||
"three": "^0.83.0"
|
"three": "^0.88.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-plugin-inline-import": "^2.0.6",
|
"@doodle3d/doodle3d-core": "github:doodle3d/doodle3d-core",
|
||||||
"babel-preset-stage-0": "^6.24.1",
|
"babel-cli": "6.24.1",
|
||||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
"babel-loader": "7.0.0",
|
||||||
"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-es2015": "6.24.1",
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
"babel-cli": "6.24.1",
|
"babel-preset-stage-0": "^6.24.1",
|
||||||
"babel-core": "6.24.1",
|
"babel-runtime": "^6.26.0",
|
||||||
"babel-loader": "7.0.0",
|
"html-webpack-plugin": "^2.29.0",
|
||||||
"babel-plugin-add-module-exports": "0.2.1",
|
"html-webpack-template": "^6.0.2",
|
||||||
"babel-preset-es2015": "6.24.1"
|
"imports-loader": "^0.7.1",
|
||||||
|
"material-ui": "^0.19.4",
|
||||||
|
"normalize-jss": "^4.0.0",
|
||||||
|
"raw-loader": "^0.5.1",
|
||||||
|
"react-tap-event-plugin": "^3.0.2",
|
||||||
|
"url-loader": "^0.5.9",
|
||||||
|
"webpack": "^3.3.0",
|
||||||
|
"webpack-dev-server": "^2.5.1",
|
||||||
|
"worker-loader": "^0.8.1",
|
||||||
|
"yml-loader": "^2.1.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { sliceGeometry, sliceMesh } from './slicer.js';
|
import { sliceGeometry, sliceMesh } from './slicer.js';
|
||||||
import Interface from './interface/index.js';
|
import Interface from './interface/index.js';
|
||||||
import baseSettings from './settings/default.yml';
|
import _defaultSettings from './settings/default.yml';
|
||||||
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';
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
base: baseSettings,
|
default: _defaultSettings,
|
||||||
printer: printerSettings,
|
printer: printerSettings,
|
||||||
material: materialSettings,
|
material: materialSettings,
|
||||||
quality: qualitySettings
|
quality: qualitySettings
|
||||||
|
@ -5,56 +5,35 @@ import injectSheet from 'react-jss';
|
|||||||
import MaterialUISelectField from 'material-ui/SelectField'
|
import MaterialUISelectField from 'material-ui/SelectField'
|
||||||
import MaterialUICheckbox from 'material-ui/Checkbox';
|
import MaterialUICheckbox from 'material-ui/Checkbox';
|
||||||
import MaterialUITextField from 'material-ui/TextField';
|
import MaterialUITextField from 'material-ui/TextField';
|
||||||
import { grey100, grey300, grey500 } from 'material-ui/styles/colors';
|
|
||||||
|
|
||||||
const styles = {
|
const contextTypes = { state: PropTypes.object, onChange: PropTypes.func, disabled: PropTypes.bool };
|
||||||
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) => (
|
export const SelectField = (props, context) => (
|
||||||
<MaterialUISelectField
|
<MaterialUISelectField
|
||||||
{ ...props }
|
{ ...props }
|
||||||
value={context.state[props.name]}
|
disabled={context.disabled}
|
||||||
|
value={_.get(context.state, props.name)}
|
||||||
onChange={(event, index, value) => context.onChange(props.name, value)}
|
onChange={(event, index, value) => context.onChange(props.name, value)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
SelectField.contextTypes = { state: PropTypes.object, onChange: PropTypes.func };
|
SelectField.contextTypes = contextTypes;
|
||||||
|
|
||||||
export const TextField = (props, context) => (
|
export const TextField = (props, context) => (
|
||||||
<MaterialUITextField
|
<MaterialUITextField
|
||||||
{ ...props }
|
{ ...props }
|
||||||
|
disabled={context.disabled}
|
||||||
value={_.get(context.state, props.name)}
|
value={_.get(context.state, props.name)}
|
||||||
onChange={(event, value) => context.onChange(props.name, value)}
|
onChange={(event, value) => context.onChange(props.name, value)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
TextField.contextTypes = { state: PropTypes.object, onChange: PropTypes.func };
|
TextField.contextTypes = contextTypes;
|
||||||
|
|
||||||
export const Checkbox = (props, context) => (
|
export const Checkbox = (props, context) => (
|
||||||
<MaterialUICheckbox
|
<MaterialUICheckbox
|
||||||
{ ...props }
|
{ ...props }
|
||||||
|
disabled={context.disabled}
|
||||||
checked={_.get(context.state, props.name)}
|
checked={_.get(context.state, props.name)}
|
||||||
onCheck={(event, value) => context.onChange(props.name, value)}
|
onCheck={(event, value) => context.onChange(props.name, value)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
Checkbox.contextTypes = { state: PropTypes.object, onChange: PropTypes.func };
|
Checkbox.contextTypes = contextTypes;
|
||||||
|
@ -4,18 +4,43 @@ import _ from 'lodash';
|
|||||||
import { Tabs, Tab } from 'material-ui/Tabs';
|
import { Tabs, Tab } from 'material-ui/Tabs';
|
||||||
import MenuItem from 'material-ui/MenuItem';
|
import MenuItem from 'material-ui/MenuItem';
|
||||||
import injectSheet from 'react-jss';
|
import injectSheet from 'react-jss';
|
||||||
import { SettingsGroup, SelectField, TextField, Checkbox } from './FormComponents.js';
|
import { SelectField, TextField, Checkbox } from './FormComponents.js';
|
||||||
import { grey500 } from 'material-ui/styles/colors';
|
import { grey800, cyan500 } from 'material-ui/styles/colors';
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
textFieldRow: {
|
textFieldRow: {
|
||||||
display: 'flex'
|
display: 'flex'
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
width: '100%',
|
||||||
|
flexGrow: 1,
|
||||||
|
overflowY: 'auto',
|
||||||
|
'& p, h3': {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
margin: '30px 0 0 0'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Settings extends React.Component {
|
class Settings extends React.Component {
|
||||||
|
static childContextTypes = { state: PropTypes.object, onChange: PropTypes.func, disabled: PropTypes.bool };
|
||||||
|
static defaultProps: {
|
||||||
|
disabled: false
|
||||||
|
};
|
||||||
|
static propTypes = {
|
||||||
|
classes: PropTypes.objectOf(PropTypes.string),
|
||||||
|
onChange: PropTypes.func,
|
||||||
|
printers: PropTypes.object.isRequired,
|
||||||
|
defaultPrinter: PropTypes.string,
|
||||||
|
quality: PropTypes.object.isRequired,
|
||||||
|
defaultQuality: PropTypes.string.isRequired,
|
||||||
|
material: PropTypes.object.isRequired,
|
||||||
|
defaultMaterial: PropTypes.string.isRequired,
|
||||||
|
initialSettings: PropTypes.object.isRequired,
|
||||||
|
disabled: PropTypes.bool.isRequired
|
||||||
|
};
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super();
|
||||||
this.state = {
|
this.state = {
|
||||||
settings: props.initialSettings,
|
settings: props.initialSettings,
|
||||||
printers: props.defaultPrinter,
|
printers: props.defaultPrinter,
|
||||||
@ -42,116 +67,96 @@ class Settings extends React.Component {
|
|||||||
state = _.set(_.cloneDeep(this.state), fieldName, value);
|
state = _.set(_.cloneDeep(this.state), fieldName, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (onChange) onChange(state.settings);
|
if (onChange) onChange(state);
|
||||||
if (state) this.setState(state);
|
if (state) this.setState(state);
|
||||||
};
|
};
|
||||||
|
|
||||||
getChildContext() {
|
getChildContext() {
|
||||||
return { state: this.state, onChange: this.changeSettings };
|
return { state: this.state, onChange: this.changeSettings, disabled: this.props.disabled };
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { classes, printers, quality, material } = this.props;
|
const { classes, printers, quality, material, disabled } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs>
|
<div className={classes.container}>
|
||||||
<Tab label="basic">
|
<SelectField name="printers" floatingLabelText="Printer" fullWidth>
|
||||||
<div>
|
{Object.entries(printers).map(([value, { title }]) => (
|
||||||
<SelectField name="printers" floatingLabelText="Printer" fullWidth>
|
<MenuItem key={value} value={value} primaryText={title} />
|
||||||
{Object.entries(printers).map(([value, { title }]) => (
|
))}
|
||||||
<MenuItem key={value} value={value} primaryText={title} />
|
</SelectField>
|
||||||
))}
|
<SelectField name="material" floatingLabelText="Material" fullWidth>
|
||||||
</SelectField>
|
{Object.entries(material).map(([value, { title }]) => (
|
||||||
<SelectField name="quality" floatingLabelText="Quality" fullWidth>
|
<MenuItem key={value} value={value} primaryText={title} />
|
||||||
{Object.entries(quality).map(([value, { title }]) => (
|
))}
|
||||||
<MenuItem key={value} value={value} primaryText={title} />
|
</SelectField>
|
||||||
))}
|
<h3>Printer Setup</h3>
|
||||||
</SelectField>
|
<Tabs inkBarStyle={{ backgroundColor: cyan500 }}>
|
||||||
<SelectField name="material" floatingLabelText="Material" fullWidth>
|
<Tab buttonStyle={{ color: grey800, backgroundColor: 'white' }} label="Basic">
|
||||||
{Object.entries(material).map(([value, { title }]) => (
|
<div>
|
||||||
<MenuItem key={value} value={value} primaryText={title} />
|
<SelectField name="quality" floatingLabelText="Quality" fullWidth>
|
||||||
))}
|
{Object.entries(quality).map(([value, { title }]) => (
|
||||||
</SelectField>
|
<MenuItem key={value} value={value} primaryText={title} />
|
||||||
</div>
|
))}
|
||||||
</Tab>
|
</SelectField>
|
||||||
<Tab label="advanced">
|
</div>
|
||||||
<div>
|
</Tab>
|
||||||
<SettingsGroup name="Printer dimensions">
|
<Tab buttonStyle={{ color: grey800, backgroundColor: 'white' }} label="Advanced">
|
||||||
|
<div>
|
||||||
|
<p>Layer</p>
|
||||||
|
<TextField name="settings.layerHeight" fullWidth floatingLabelText="Height" type="number" />
|
||||||
|
<p>Printer dimensions</p>
|
||||||
<div className={classes.textFieldRow}>
|
<div className={classes.textFieldRow}>
|
||||||
<TextField name="settings.dimensions.x" fullWidth floatingLabelText="X" type="number" />
|
<TextField name="settings.dimensions.x" fullWidth floatingLabelText="X" type="number" />
|
||||||
<TextField name="settings.dimensions.y" fullWidth floatingLabelText="Y" type="number" />
|
<TextField name="settings.dimensions.y" fullWidth floatingLabelText="Y" type="number" />
|
||||||
<TextField name="settings.dimensions.z" fullWidth floatingLabelText="Z" type="number" />
|
<TextField name="settings.dimensions.z" fullWidth floatingLabelText="Z" type="number" />
|
||||||
</div>
|
</div>
|
||||||
</SettingsGroup>
|
<p>Nozzle</p>
|
||||||
<SettingsGroup name="Nozzle">
|
|
||||||
<TextField name="settings.nozzleDiameter" fullWidth floatingLabelText="Diameter" type="number" />
|
<TextField name="settings.nozzleDiameter" fullWidth floatingLabelText="Diameter" type="number" />
|
||||||
</SettingsGroup>
|
<p>Bed</p>
|
||||||
<SettingsGroup name="Bed">
|
|
||||||
<TextField name="settings.bedTemperature" fullWidth floatingLabelText="Temperature" type="number" />
|
<TextField name="settings.bedTemperature" fullWidth floatingLabelText="Temperature" type="number" />
|
||||||
<Checkbox name="settings.heatedBed" label="Heated" />
|
<Checkbox name="settings.heatedBed" label="Heated" />
|
||||||
</SettingsGroup>
|
<p>Material</p>
|
||||||
<SettingsGroup name="Material">
|
|
||||||
<TextField name="settings.filamentThickness" fullWidth floatingLabelText="Thickness" type="number" />
|
<TextField name="settings.filamentThickness" fullWidth floatingLabelText="Thickness" type="number" />
|
||||||
<TextField name="settings.temperature" fullWidth floatingLabelText="Temperature" type="number" />
|
<TextField name="settings.temperature" fullWidth floatingLabelText="Temperature" type="number" />
|
||||||
</SettingsGroup>
|
<p>Thickness</p>
|
||||||
<SettingsGroup name="Thickness">
|
|
||||||
<TextField name="settings.thickness.top" fullWidth floatingLabelText="top" type="number" />
|
<TextField name="settings.thickness.top" fullWidth floatingLabelText="top" type="number" />
|
||||||
<TextField name="settings.thickness.bottom" fullWidth floatingLabelText="bottom" type="number" />
|
<TextField name="settings.thickness.bottom" fullWidth floatingLabelText="bottom" type="number" />
|
||||||
<TextField name="settings.thickness.shell" fullWidth floatingLabelText="shell" type="number" />
|
<TextField name="settings.thickness.shell" fullWidth floatingLabelText="shell" type="number" />
|
||||||
</SettingsGroup>
|
<p>Retraction</p>
|
||||||
<SettingsGroup name="Retraction">
|
|
||||||
<Checkbox name="settings.retraction.enabled" label="Enabled" />
|
<Checkbox name="settings.retraction.enabled" label="Enabled" />
|
||||||
<TextField name="settings.retraction.amount" fullWidth floatingLabelText="Amount" type="number" />
|
<TextField name="settings.retraction.amount" fullWidth floatingLabelText="Amount" type="number" />
|
||||||
<TextField name="settings.retraction.speed" fullWidth floatingLabelText="Speed" type="number" />
|
<TextField name="settings.retraction.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||||
<TextField name="settings.retraction.minDistance" fullWidth floatingLabelText="Min distance" type="number" />
|
<TextField name="settings.retraction.minDistance" fullWidth floatingLabelText="Min distance" type="number" />
|
||||||
</SettingsGroup>
|
<p>Travel</p>
|
||||||
<SettingsGroup name="Travel">
|
|
||||||
<TextField name="settings.travel.speed" fullWidth floatingLabelText="Speed" type="number" />
|
<TextField name="settings.travel.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||||
<Checkbox name="settings.combing" label="Combing" />
|
<Checkbox name="settings.combing" label="Combing" />
|
||||||
</SettingsGroup>
|
<p>Inner shell</p>
|
||||||
<SettingsGroup name="Inner shell">
|
|
||||||
<TextField name="settings.innerShell.speed" fullWidth floatingLabelText="Speed" type="number" />
|
<TextField name="settings.innerShell.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||||
<TextField name="settings.innerShell.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
<TextField name="settings.innerShell.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||||
</SettingsGroup>
|
<p>Outer shell</p>
|
||||||
<SettingsGroup name="Outer shell">
|
|
||||||
<TextField name="settings.outerShell.speed" fullWidth floatingLabelText="Speed" type="number" />
|
<TextField name="settings.outerShell.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||||
<TextField name="settings.outerShell.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
<TextField name="settings.outerShell.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||||
</SettingsGroup>
|
<p>Inner infill</p>
|
||||||
<SettingsGroup name="Inner infill">
|
|
||||||
<TextField name="settings.innerInfill.gridSize" fullWidth floatingLabelText="Grid size" type="number" />
|
<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.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||||
<TextField name="settings.innerInfill.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
<TextField name="settings.innerInfill.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||||
</SettingsGroup>
|
<p>Outer infill</p>
|
||||||
<SettingsGroup name="Outer infill">
|
|
||||||
<TextField name="settings.outerInfill.speed" fullWidth floatingLabelText="Speed" type="number" />
|
<TextField name="settings.outerInfill.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||||
<TextField name="settings.outerInfill.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
<TextField name="settings.outerInfill.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||||
</SettingsGroup>
|
<p>Brim</p>
|
||||||
<SettingsGroup name="Brim">
|
|
||||||
<TextField name="settings.brim.offset" fullWidth floatingLabelText="Offset" type="number" />
|
<TextField name="settings.brim.offset" fullWidth floatingLabelText="Offset" type="number" />
|
||||||
<TextField name="settings.brim.speed" fullWidth floatingLabelText="Speed" type="number" />
|
<TextField name="settings.brim.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||||
<TextField name="settings.brim.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
<TextField name="settings.brim.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||||
</SettingsGroup>
|
<p>First layer</p>
|
||||||
<SettingsGroup name="First layer">
|
|
||||||
<TextField name="settings.firstLayer.speed" fullWidth floatingLabelText="Speed" type="number" />
|
<TextField name="settings.firstLayer.speed" fullWidth floatingLabelText="Speed" type="number" />
|
||||||
<TextField name="settings.firstLayer.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
<TextField name="settings.firstLayer.flowRate" fullWidth floatingLabelText="Flow rate" type="number" />
|
||||||
</SettingsGroup>
|
</div>
|
||||||
</div>
|
</Tab>
|
||||||
</Tab>
|
</Tabs>
|
||||||
</Tabs>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
export default injectSheet(styles)(Settings);
|
||||||
|
@ -1,27 +1,38 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import * as THREE from 'three';
|
import { Quaternion } from 'three/src/math/Quaternion.js';
|
||||||
|
import { Vector3 } from 'three/src/math/Vector3.js';
|
||||||
|
import { Mesh } from 'three/src/objects/Mesh.js';
|
||||||
import PropTypes from 'proptypes';
|
import PropTypes from 'proptypes';
|
||||||
import { placeOnGround, createScene, createGcodeGeometry } from './utils.js';
|
import { placeOnGround, createScene, fetchProgress, slice, TabTemplate } from './utils.js';
|
||||||
import injectSheet from 'react-jss';
|
import injectSheet from 'react-jss';
|
||||||
import { sliceGeometry } from '../slicer.js';
|
|
||||||
import RaisedButton from 'material-ui/RaisedButton';
|
import RaisedButton from 'material-ui/RaisedButton';
|
||||||
|
import FlatButton from 'material-ui/FlatButton';
|
||||||
import Slider from 'material-ui/Slider';
|
import Slider from 'material-ui/Slider';
|
||||||
import { grey100, grey300 } from 'material-ui/styles/colors';
|
import LinearProgress from 'material-ui/LinearProgress';
|
||||||
|
import { grey50, grey300, grey800, red500 } from 'material-ui/styles/colors';
|
||||||
|
import Popover from 'material-ui/Popover/Popover';
|
||||||
|
import Menu from 'material-ui/Menu';
|
||||||
|
import MenuItem from 'material-ui/MenuItem';
|
||||||
|
import { Tabs, Tab } from 'material-ui/Tabs';
|
||||||
import Settings from './Settings.js';
|
import Settings from './Settings.js';
|
||||||
import baseSettings from '../settings/default.yml';
|
import defaultSettings from '../settings/default.yml';
|
||||||
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 ReactResizeDetector from 'react-resize-detector';
|
import ReactResizeDetector from 'react-resize-detector';
|
||||||
|
|
||||||
|
const MAX_FULLSCREEN_WIDTH = 720;
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
container: {
|
container: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
backgroundColor: grey100,
|
backgroundColor: grey50,
|
||||||
overflow: 'hidden'
|
color: grey800,
|
||||||
|
overflow: 'hidden',
|
||||||
|
fontFamily: 'roboto, sans-serif'
|
||||||
},
|
},
|
||||||
controlBar: {
|
controlBar: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -29,46 +40,87 @@ const styles = {
|
|||||||
left: '10px'
|
left: '10px'
|
||||||
},
|
},
|
||||||
d3View: {
|
d3View: {
|
||||||
flexGrow: 1
|
flexGrow: 1,
|
||||||
|
flexBasis: 0
|
||||||
},
|
},
|
||||||
canvas: {
|
canvas: {
|
||||||
position: 'absolute'
|
position: 'absolute'
|
||||||
},
|
},
|
||||||
sliceBar: {
|
settingsBar: {
|
||||||
width: '240px',
|
display: 'flex',
|
||||||
padding: '0 10px',
|
flexDirection: 'column',
|
||||||
overflowY: 'auto',
|
maxWidth: '380px',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
padding: '10px',
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
borderLeft: `1px solid ${grey300}`
|
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: {
|
sliceActions: {
|
||||||
listStyleType: 'none'
|
flexShrink: 0,
|
||||||
|
},
|
||||||
|
sliceButtons: {
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
display: 'flex'
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
margin: '5px 0'
|
margin: '5px 0 5px 5px'
|
||||||
},
|
},
|
||||||
controlButton: {
|
controlButton: {
|
||||||
marginRight: '2px'
|
marginRight: '2px'
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
width: '100%',
|
||||||
|
padding: '10px'
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
color: red500
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
position: 'absolute'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Interface extends React.Component {
|
class Interface extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
mesh: PropTypes.shape({ isMesh: PropTypes.oneOf([true]) }).isRequired,
|
||||||
|
classes: PropTypes.objectOf(PropTypes.string),
|
||||||
|
defaultSettings: PropTypes.object.isRequired,
|
||||||
|
printers: PropTypes.object.isRequired,
|
||||||
|
defaultPrinter: PropTypes.string,
|
||||||
|
quality: PropTypes.object.isRequired,
|
||||||
|
defaultQuality: PropTypes.string.isRequired,
|
||||||
|
material: PropTypes.object.isRequired,
|
||||||
|
defaultMaterial: PropTypes.string.isRequired,
|
||||||
|
pixelRatio: PropTypes.number.isRequired,
|
||||||
|
onCancel: PropTypes.func,
|
||||||
|
name: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
defaultSettings: defaultSettings,
|
||||||
|
printers: printerSettings,
|
||||||
|
quality: qualitySettings,
|
||||||
|
defaultQuality: 'medium',
|
||||||
|
material: materialSettings,
|
||||||
|
defaultMaterial: 'pla',
|
||||||
|
pixelRatio: 1,
|
||||||
|
name: 'Doodle3D'
|
||||||
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const { defaultPrinter, defaultQuality, defaultMaterial, printers, quality, material, defaultSettings } = props;
|
const { defaultPrinter, defaultQuality, defaultMaterial, printers, quality, material, defaultSettings } = props;
|
||||||
this.state = {
|
this.state = {
|
||||||
controlMode: 'translate',
|
showFullScreen: false,
|
||||||
isSlicing: false,
|
isSlicing: false,
|
||||||
sliced: false,
|
error: null,
|
||||||
|
printers: defaultPrinter,
|
||||||
|
quality: defaultQuality,
|
||||||
|
material: defaultMaterial,
|
||||||
|
popover: {
|
||||||
|
element: null,
|
||||||
|
open: false
|
||||||
|
},
|
||||||
settings: _.merge(
|
settings: _.merge(
|
||||||
{},
|
{},
|
||||||
defaultSettings,
|
defaultSettings,
|
||||||
@ -81,12 +133,21 @@ 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 });
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { editorControls, mesh: { material }, renderer } = this.state;
|
||||||
|
editorControls.dispose();
|
||||||
|
material.dispose();
|
||||||
|
renderer.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
resetMesh = () => {
|
resetMesh = () => {
|
||||||
const { mesh, render } = this.state;
|
const { mesh, render, isSlicing } = this.state;
|
||||||
|
if (isSlicing) return;
|
||||||
if (mesh) {
|
if (mesh) {
|
||||||
mesh.position.set(0, 0, 0);
|
mesh.position.set(0, 0, 0);
|
||||||
mesh.scale.set(1, 1, 1);
|
mesh.scale.set(1, 1, 1);
|
||||||
@ -97,176 +158,210 @@ class Interface extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
reset = () => {
|
scaleUp = () => this.scaleMesh(0.9);
|
||||||
const { control, mesh, render, gcode, scene } = this.state;
|
scaleDown = () => this.scaleMesh(1.0 / 0.9);
|
||||||
control.enabled = true;
|
scaleMesh = (factor) => {
|
||||||
control.setSize(1);
|
const { mesh, render, isSlicing } = this.state;
|
||||||
control.visible = true;
|
if (isSlicing) return;
|
||||||
mesh.visible = true;
|
if (mesh) {
|
||||||
|
mesh.scale.multiplyScalar(factor);
|
||||||
scene.remove(gcode.linePreview);
|
mesh.updateMatrix();
|
||||||
gcode.linePreview.geometry.dispose();
|
placeOnGround(mesh);
|
||||||
|
render();
|
||||||
this.setState({ sliced: false, gcode: null });
|
}
|
||||||
render();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
slice = async () => {
|
rotateX = () => this.rotate(new Vector3(0, 0, 1), Math.PI / 2.0);
|
||||||
const { mesh, render, scene, control, settings } = this.state;
|
rotateY = () => this.rotate(new Vector3(1, 0, 0), Math.PI / 2.0);
|
||||||
|
rotateZ = () => this.rotate(new Vector3(0, 1, 0), Math.PI / 2.0);
|
||||||
|
rotate = (axis, angle) => {
|
||||||
|
const { mesh, render, isSlicing } = this.state;
|
||||||
|
if (isSlicing) return;
|
||||||
|
if (mesh) {
|
||||||
|
mesh.rotateOnWorldAxis(axis, angle);
|
||||||
|
placeOnGround(mesh);
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const { dimensions } = settings;
|
slice = async (target) => {
|
||||||
const centerX = dimensions.x / 2;
|
const { isSlicing, settings, printers, quality, material, mesh: { matrix } } = this.state;
|
||||||
const centerY = dimensions.y / 2;
|
const { name, mesh } = this.props;
|
||||||
|
|
||||||
const geometry = mesh.geometry.clone();
|
if (isSlicing) return;
|
||||||
mesh.updateMatrix();
|
|
||||||
|
|
||||||
this.setState({ isSlicing: true, progress: { actions: [], percentage: 0 } });
|
this.closePopover();
|
||||||
|
|
||||||
const matrix = new THREE.Matrix4().makeTranslation(centerY, 0, centerX).multiply(mesh.matrix);
|
this.setState({ isSlicing: true, progress: { action: '', percentage: 0, step: 0 }, error: null });
|
||||||
const gcode = await sliceGeometry(settings, geometry, matrix, false, true, ({ progress }) => {
|
|
||||||
this.setState({ progress: {
|
const exportMesh = new Mesh(mesh.geometry, mesh.material);
|
||||||
actions: [...this.state.progress.actions, progress.action],
|
exportMesh.applyMatrix(matrix);
|
||||||
percentage: progress.done / progress.total
|
|
||||||
} });
|
try {
|
||||||
|
await slice(target, name, exportMesh, settings, printers, quality, material, progress => {
|
||||||
|
this.setState({ progress: { ...this.state.progress, ...progress } });
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
this.setState({ error: error.message });
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.setState({ isSlicing: false });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
openPopover = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
popover: {
|
||||||
|
element: event.currentTarget,
|
||||||
|
open: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
closePopover = () => {
|
||||||
|
this.setState({
|
||||||
|
popover: {
|
||||||
|
element: null,
|
||||||
|
open: false
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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) => {
|
onChangeSettings = (settings) => {
|
||||||
this.setState({ 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) {
|
componentWillUpdate(nextProps, nextState) {
|
||||||
const { control, box, render, setSize } = this.state;
|
const { box, render, setSize } = this.state;
|
||||||
if (control && nextState.controlMode !== this.state.controlMode) control.setMode(nextState.controlMode);
|
let changed = false;
|
||||||
if (box && nextState.settings.dimensions !== this.state.settings.dimensions) {
|
if (box && nextState.settings.dimensions !== this.state.settings.dimensions) {
|
||||||
const { dimensions } = nextState.settings;
|
const { dimensions } = nextState.settings;
|
||||||
box.scale.set(dimensions.y, dimensions.z, dimensions.x);
|
box.scale.set(dimensions.y, dimensions.z, dimensions.x);
|
||||||
render();
|
box.updateMatrix();
|
||||||
}
|
changed = true;
|
||||||
if (setSize && nextProps.width !== this.props.width || nextProps.height !== this.props.height || nextProps.pixelRatio !== this.props.pixelRatio) {
|
|
||||||
setSize(nextProps.width, nextProps.height, nextProps.pixelRatio);
|
|
||||||
}
|
}
|
||||||
|
if (changed) render();
|
||||||
}
|
}
|
||||||
|
|
||||||
onResize = (width, height) => {
|
componentDidUpdate() {
|
||||||
|
const { updateCanvas } = this.state;
|
||||||
|
const { canvas } = this.refs;
|
||||||
|
if (updateCanvas && canvas) updateCanvas(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
onResize3dView = (width, height) => {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
const { setSize } = this.state;
|
const { setSize } = this.state;
|
||||||
const { pixelRatio } = this.props;
|
const { pixelRatio } = this.props;
|
||||||
setSize(width, height, pixelRatio);
|
if (setSize) setSize(width, height, pixelRatio);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
onResizeContainer = (width) => {
|
||||||
const { width, height, classes, onCompleteActions, defaultPrinter, defaultQuality, defaultMaterial } = this.props;
|
this.setState({ showFullScreen: width > MAX_FULLSCREEN_WIDTH });
|
||||||
const { sliced, isSlicing, progress, gcode, controlMode, settings } = this.state;
|
};
|
||||||
|
|
||||||
return (
|
render() {
|
||||||
<div className={classes.container}>
|
const { classes, defaultPrinter, defaultQuality, defaultMaterial, onCancel } = this.props;
|
||||||
<div className={classes.d3View}>
|
const { isSlicing, progress, settings, printers, quality, material, showFullScreen, error } = this.state;
|
||||||
<ReactResizeDetector handleWidth handleHeight onResize={this.onResize} />
|
|
||||||
<canvas className={classes.canvas} ref="canvas" width={width} height={height} />
|
const style = { ...(showFullScreen ? {} : { maxWidth: 'inherit', width: '100%', height: '100%' }) };
|
||||||
{!sliced && <div className={classes.controlBar}>
|
|
||||||
<RaisedButton className={classes.controlButton} onTouchTap={this.resetMesh} primary label="reset" />
|
const settingsPanel = (
|
||||||
<RaisedButton className={classes.controlButton} disabled={controlMode === 'translate'} onTouchTap={() => this.setState({ controlMode: 'translate' })} primary label="translate" />
|
<div className={classes.settingsBar} style={style}>
|
||||||
<RaisedButton className={classes.controlButton} disabled={controlMode === 'rotate'} onTouchTap={() => this.setState({ controlMode: 'rotate' })} primary label="rotate" />
|
<Settings
|
||||||
<RaisedButton className={classes.controlButton} disabled={controlMode === 'scale'} onTouchTap={() => this.setState({ controlMode: 'scale' })} primary label="scale" />
|
disabled={isSlicing}
|
||||||
</div>}
|
printers={printerSettings}
|
||||||
|
defaultPrinter={defaultPrinter}
|
||||||
|
quality={qualitySettings}
|
||||||
|
defaultQuality={defaultQuality}
|
||||||
|
material={materialSettings}
|
||||||
|
defaultMaterial={defaultMaterial}
|
||||||
|
initialSettings={settings}
|
||||||
|
onChange={this.onChangeSettings}
|
||||||
|
/>
|
||||||
|
<div className={classes.sliceActions}>
|
||||||
|
{error && <p className={classes.error}>{error}</p>}
|
||||||
|
{isSlicing && <p>{progress.action}</p>}
|
||||||
|
{isSlicing && <LinearProgress mode="determinate" value={progress.percentage * 100.0} />}
|
||||||
|
<div className={classes.sliceButtons}>
|
||||||
|
{onCancel && <RaisedButton
|
||||||
|
label="Cancel"
|
||||||
|
className={`${classes.button}`}
|
||||||
|
onTouchTap={onCancel}
|
||||||
|
/>}
|
||||||
|
<RaisedButton
|
||||||
|
label="Print"
|
||||||
|
ref="button"
|
||||||
|
primary
|
||||||
|
className={`${classes.button}`}
|
||||||
|
onTouchTap={this.openPopover}
|
||||||
|
disabled={isSlicing}
|
||||||
|
/>
|
||||||
|
<Popover
|
||||||
|
open={this.state.popover.open}
|
||||||
|
anchorEl={this.state.popover.element}
|
||||||
|
anchorOrigin={{horizontal: 'left', vertical: 'bottom'}}
|
||||||
|
targetOrigin={{horizontal: 'left', vertical: 'bottom'}}
|
||||||
|
onRequestClose={this.closePopover}
|
||||||
|
>
|
||||||
|
<Menu>
|
||||||
|
<MenuItem primaryText="Send over WiFi" onTouchTap={() => this.slice('WIFI')} />
|
||||||
|
<MenuItem primaryText="Download GCode" onTouchTap={() => this.slice('DOWNLOAD')} />
|
||||||
|
</Menu>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</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>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const d3Panel = (
|
||||||
|
<div className={classes.d3View}>
|
||||||
|
<ReactResizeDetector handleWidth handleHeight onResize={this.onResize3dView} />
|
||||||
|
<canvas className={classes.canvas} ref="canvas" />
|
||||||
|
<div className={classes.controlBar}>
|
||||||
|
<RaisedButton disabled={isSlicing} className={classes.controlButton} onTouchTap={this.resetMesh} label="reset" />
|
||||||
|
<RaisedButton disabled={isSlicing} className={classes.controlButton} onTouchTap={this.scaleUp} label="scale down" />
|
||||||
|
<RaisedButton disabled={isSlicing} className={classes.controlButton} onTouchTap={this.scaleDown} label="scale up" />
|
||||||
|
<RaisedButton disabled={isSlicing} className={classes.controlButton} onTouchTap={this.rotateX} label="rotate x" />
|
||||||
|
<RaisedButton disabled={isSlicing} className={classes.controlButton} onTouchTap={this.rotateY} label="rotate y" />
|
||||||
|
<RaisedButton disabled={isSlicing} className={classes.controlButton} onTouchTap={this.rotateZ} label="rotate z" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (showFullScreen) {
|
||||||
|
return (
|
||||||
|
<div className={classes.container}>
|
||||||
|
<ReactResizeDetector handleWidth handleHeight onResize={this.onResizeContainer} />
|
||||||
|
<h1 className={classes.title}>Print</h1>
|
||||||
|
{d3Panel}
|
||||||
|
{settingsPanel}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className={classes.container}>
|
||||||
|
<ReactResizeDetector handleWidth handleHeight onResize={this.onResizeContainer} />
|
||||||
|
<Tabs
|
||||||
|
style={{ width: '100%', display: 'flex', flexDirection: 'column' }}
|
||||||
|
tabItemContainerStyle={{ flexShrink: 0 }}
|
||||||
|
contentContainerStyle={{ flexGrow: 1, display: 'flex' }}
|
||||||
|
tabTemplateStyle={{ display: 'flex' }}
|
||||||
|
tabTemplate={TabTemplate}
|
||||||
|
>
|
||||||
|
<Tab label="Settings">
|
||||||
|
{settingsPanel}
|
||||||
|
</Tab>
|
||||||
|
<Tab label="Edit Model">
|
||||||
|
{d3Panel}
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
</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);
|
export default injectSheet(styles)(Interface);
|
||||||
|
@ -1,33 +1,71 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
|
import { Box3 } from 'three/src/math/Box3.js';
|
||||||
|
import { Matrix4 } from 'three/src/math/Matrix4.js';
|
||||||
|
import { Scene } from 'three/src/scenes/Scene.js';
|
||||||
|
import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js';
|
||||||
|
import { AmbientLight } from 'three/src/lights/AmbientLight.js';
|
||||||
|
import { DirectionalLight } from 'three/src/lights/DirectionalLight.js';
|
||||||
|
import { MeshPhongMaterial } from 'three/src/materials/MeshPhongMaterial.js';
|
||||||
|
import { BoxGeometry } from 'three/src/geometries/BoxGeometry.js';
|
||||||
|
import { Mesh } from 'three/src/objects/Mesh.js';
|
||||||
|
import { BoxHelper } from 'three/src/helpers/BoxHelper.js';
|
||||||
|
import { WebGLRenderer } from 'three/src/renderers/WebGLRenderer.js';
|
||||||
|
import { DoubleSide } from 'three/src/constants.js';
|
||||||
import 'three/examples/js/controls/EditorControls';
|
import 'three/examples/js/controls/EditorControls';
|
||||||
import 'three/examples/js/controls/TransformControls';
|
import printerSettings from '../settings/printer.yml';
|
||||||
|
import materialSettings from '../settings/material.yml';
|
||||||
|
import qualitySettings from '../settings/quality.yml';
|
||||||
|
import { sliceGeometry } from '../slicer.js';
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import fileSaver from 'file-saver';
|
||||||
|
|
||||||
export function placeOnGround(mesh) {
|
export function placeOnGround(mesh) {
|
||||||
const boundingBox = new THREE.Box3().setFromObject(mesh);
|
const boundingBox = new Box3().setFromObject(mesh);
|
||||||
|
|
||||||
mesh.position.y -= boundingBox.min.y;
|
mesh.position.y -= boundingBox.min.y;
|
||||||
mesh.updateMatrix();
|
mesh.updateMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createScene(canvas, props, state) {
|
export function createScene(canvas, props, state) {
|
||||||
const { geometry, pixelRatio } = props;
|
const { pixelRatio, mesh: { geometry } } = props;
|
||||||
const { controlMode, settings } = state;
|
const { settings } = state;
|
||||||
|
|
||||||
// center geometry
|
// center geometry
|
||||||
geometry.computeBoundingBox();
|
geometry.computeBoundingBox();
|
||||||
const centerX = (geometry.boundingBox.max.x + geometry.boundingBox.min.x) / 2;
|
const center = geometry.boundingBox.getCenter();
|
||||||
const centerY = (geometry.boundingBox.max.y + geometry.boundingBox.min.y) / 2;
|
geometry.applyMatrix(new Matrix4().makeTranslation(-center.x, -center.y, -center.z));
|
||||||
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 });
|
const scene = new Scene();
|
||||||
renderer.setClearColor(0xffffff, 0);
|
|
||||||
|
|
||||||
const scene = new THREE.Scene();
|
const camera = new PerspectiveCamera(50, 1, 1, 10000);
|
||||||
|
|
||||||
const camera = new THREE.PerspectiveCamera(50, 1, 1, 10000);
|
|
||||||
camera.position.set(0, 400, 300);
|
camera.position.set(0, 400, 300);
|
||||||
|
|
||||||
|
const directionalLightA = new DirectionalLight(0xa2a2a2);
|
||||||
|
directionalLightA.position.set(1, 1, 1);
|
||||||
|
scene.add(directionalLightA);
|
||||||
|
|
||||||
|
const directionalLightB = new DirectionalLight(0xa2a2a2);
|
||||||
|
directionalLightB.position.set(-1, 1, -1);
|
||||||
|
scene.add(directionalLightB);
|
||||||
|
|
||||||
|
const light = new AmbientLight(0x656565);
|
||||||
|
scene.add(light);
|
||||||
|
|
||||||
|
const material = new MeshPhongMaterial({ color: 0x2194ce, side: DoubleSide, specular: 0xc5c5c5, shininess: 5 });
|
||||||
|
const mesh = new Mesh(geometry, material);
|
||||||
|
placeOnGround(mesh);
|
||||||
|
scene.add(mesh);
|
||||||
|
|
||||||
|
const box = new BoxHelper(new Mesh(new BoxGeometry(1, 1, 1).applyMatrix(new Matrix4().makeTranslation(0, 0.5, 0))), 0x72bcd4);
|
||||||
|
scene.add(box);
|
||||||
|
|
||||||
|
const { dimensions } = settings;
|
||||||
|
box.scale.set(dimensions.y, dimensions.z, dimensions.x);
|
||||||
|
box.updateMatrix();
|
||||||
|
|
||||||
|
const render = () => renderer.render(scene, camera);
|
||||||
|
|
||||||
const setSize = (width, height, pixelRatio = 1) => {
|
const setSize = (width, height, pixelRatio = 1) => {
|
||||||
renderer.setSize(width, height);
|
renderer.setSize(width, height);
|
||||||
renderer.setPixelRatio(pixelRatio);
|
renderer.setPixelRatio(pixelRatio);
|
||||||
@ -36,47 +74,155 @@ export function createScene(canvas, props, state) {
|
|||||||
render();
|
render();
|
||||||
};
|
};
|
||||||
|
|
||||||
const directionalLight = new THREE.DirectionalLight(0xd5d5d5);
|
let editorControls;
|
||||||
directionalLight.position.set(1, 1, 1);
|
let renderer;
|
||||||
scene.add(directionalLight);
|
const updateCanvas = (canvas) => {
|
||||||
|
if (!renderer || renderer.domElement !== canvas) {
|
||||||
|
if (renderer) renderer.dispose();
|
||||||
|
renderer = new WebGLRenderer({ canvas, alpha: true, antialias: true });
|
||||||
|
renderer.setClearColor(0xffffff, 0);
|
||||||
|
}
|
||||||
|
if (!editorControls || editorControls.domElement !== canvas) {
|
||||||
|
if (editorControls) editorControls.dispose();
|
||||||
|
editorControls = new THREE.EditorControls(camera, canvas);
|
||||||
|
editorControls.focus(mesh);
|
||||||
|
editorControls.addEventListener('change', render);
|
||||||
|
}
|
||||||
|
|
||||||
const light = new THREE.AmbientLight(0x808080);
|
render();
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
updateCanvas(canvas);
|
||||||
|
|
||||||
control.addEventListener('change', render);
|
const focus = () => editorControls.focus(mesh);
|
||||||
editorControls.addEventListener('change', render);
|
|
||||||
|
|
||||||
const box = new THREE.BoxHelper();
|
return { editorControls, scene, mesh, camera, renderer, render, box, setSize, updateCanvas, focus };
|
||||||
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);
|
export function fetchProgress(url, { method = 'get', headers = {}, body = {} } = {}, onProgress) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open(method, url);
|
||||||
|
if (headers) {
|
||||||
|
for (const key in headers) {
|
||||||
|
const header = headers[key];
|
||||||
|
xhr.setRequestHeader(key, header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.onload = event => resolve(event.target.responseText);
|
||||||
|
xhr.onerror = reject;
|
||||||
|
if (xhr.upload && onProgress) xhr.upload.onprogress = onProgress;
|
||||||
|
xhr.send(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const GCODE_SERVER_URL = 'https://gcodeserver.doodle3d.com';
|
||||||
|
const CONNECT_URL = 'http://connect.doodle3d.com/';
|
||||||
|
|
||||||
|
export async function slice(target, name, mesh, settings, printers, quality, material, updateProgress) {
|
||||||
|
if (!printers) throw new Error('Please select a printer');
|
||||||
|
|
||||||
|
let steps;
|
||||||
|
let currentStep = 0;
|
||||||
|
switch (target) {
|
||||||
|
case 'DOWNLOAD':
|
||||||
|
steps = 1;
|
||||||
|
break;
|
||||||
|
case 'WIFI':
|
||||||
|
steps = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
steps = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const { dimensions } = settings;
|
const { dimensions } = settings;
|
||||||
box.scale.set(dimensions.y, dimensions.z, dimensions.x);
|
const centerX = dimensions.x / 2;
|
||||||
|
const centerY = dimensions.y / 2;
|
||||||
|
|
||||||
return { control, editorControls, scene, mesh, camera, renderer, render, box, setSize };
|
const matrix = new Matrix4().makeTranslation(centerY, 0, centerX).multiply(mesh.matrix);
|
||||||
|
const { gcode } = await sliceGeometry(settings, mesh.geometry, mesh.material, matrix, false, false, ({ progress }) => {
|
||||||
|
updateProgress({
|
||||||
|
action: progress.action,
|
||||||
|
percentage: currentStep / steps + progress.done / progress.total / steps
|
||||||
|
});
|
||||||
|
});
|
||||||
|
currentStep ++;
|
||||||
|
|
||||||
|
switch (target) {
|
||||||
|
case 'DOWNLOAD': {
|
||||||
|
const blob = new File([gcode], `${name}.gcode`, { type: 'text/plain;charset=utf-8' });
|
||||||
|
fileSaver.saveAs(blob);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'WIFI': {
|
||||||
|
// upload G-code file to AWS S3
|
||||||
|
const { data: { reservation, id } } = await fetch(`${GCODE_SERVER_URL}/upload`, { method: 'POST' })
|
||||||
|
.then(response => response.json());
|
||||||
|
|
||||||
|
const body = new FormData();
|
||||||
|
const { fields } = reservation;
|
||||||
|
for (const key in fields) {
|
||||||
|
body.append(key, fields[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = ';' + JSON.stringify({
|
||||||
|
name: `${name}.gcode`,
|
||||||
|
...settings,
|
||||||
|
printer: {
|
||||||
|
type: printers,
|
||||||
|
title: printerSettings[printers].title
|
||||||
|
},
|
||||||
|
material: {
|
||||||
|
type: material,
|
||||||
|
title: materialSettings[material].title
|
||||||
|
},
|
||||||
|
quality: {
|
||||||
|
type: quality,
|
||||||
|
title: qualitySettings[quality].title
|
||||||
|
}
|
||||||
|
}).trim() + '\n' + gcode;
|
||||||
|
body.append('file', file);
|
||||||
|
|
||||||
|
await fetchProgress(reservation.url, { method: 'POST', body }, (progess) => {
|
||||||
|
updateProgress({
|
||||||
|
action: 'Uploading',
|
||||||
|
percentage: currentStep / steps + progess.loaded / progess.total / steps
|
||||||
|
});
|
||||||
|
});
|
||||||
|
currentStep ++;
|
||||||
|
|
||||||
|
const popup = window.open(`${CONNECT_URL}?uuid=${id}`, '_blank');
|
||||||
|
if (!popup) throw new Error('popup was blocked by browser');
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const TabTemplate = ({ children, selected, style }) => {
|
||||||
|
const templateStyle = {
|
||||||
|
width: '100%',
|
||||||
|
position: 'relative',
|
||||||
|
textAlign: 'initial',
|
||||||
|
...style,
|
||||||
|
...(selected ? {} : {
|
||||||
|
height: 0,
|
||||||
|
width: 0,
|
||||||
|
overflow: 'hidden'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={templateStyle}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
TabTemplate.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
selected: PropTypes.bool,
|
||||||
|
style: PropTypes.object,
|
||||||
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
zOffset: 0.3
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 200
|
x: 200
|
||||||
y: 200
|
y: 200
|
||||||
@ -10,8 +11,8 @@ bedTemperature: 70
|
|||||||
layerHeight: 0.15
|
layerHeight: 0.15
|
||||||
combing: true
|
combing: true
|
||||||
thickness:
|
thickness:
|
||||||
top: 1.2
|
top: 0.45
|
||||||
bottom: 1.2
|
bottom: 0.45
|
||||||
shell: 0.8
|
shell: 0.8
|
||||||
retraction:
|
retraction:
|
||||||
enabled: true
|
enabled: true
|
||||||
@ -38,7 +39,7 @@ outerShell:
|
|||||||
innerInfill:
|
innerInfill:
|
||||||
flowRate: 1.0
|
flowRate: 1.0
|
||||||
speed: 80.0
|
speed: 80.0
|
||||||
gridSize: 5.0
|
gridSize: 15.0
|
||||||
outerInfill:
|
outerInfill:
|
||||||
flowRate: 1.0
|
flowRate: 1.0
|
||||||
speed: 50.0
|
speed: 50.0
|
||||||
|
@ -1,40 +1,79 @@
|
|||||||
_3Dison_plus:
|
_3Dison_plus:
|
||||||
title: 3Dison plus
|
title: 3Dison plus
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 227
|
x: 227
|
||||||
y: 147
|
y: 147
|
||||||
z: 150
|
z: 150
|
||||||
bigbuilder3d:
|
bigbuilder3d:
|
||||||
title: Big Builder 3D
|
title: Big Builder 3D
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
|
dimensions:
|
||||||
|
x: 200
|
||||||
|
y: 200
|
||||||
|
z: 200
|
||||||
builder3d:
|
builder3d:
|
||||||
title: Builder 3D
|
title: Builder 3D
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
|
dimensions:
|
||||||
|
x: 200
|
||||||
|
y: 200
|
||||||
|
z: 200
|
||||||
bukobot:
|
bukobot:
|
||||||
title: Bukobot
|
title: Bukobot
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
|
dimensions:
|
||||||
|
x: 200
|
||||||
|
y: 200
|
||||||
|
z: 200
|
||||||
cartesio:
|
cartesio:
|
||||||
title: Cartesio
|
title: Cartesio
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
|
dimensions:
|
||||||
|
x: 200
|
||||||
|
y: 200
|
||||||
|
z: 200
|
||||||
colido_2_0_plus:
|
colido_2_0_plus:
|
||||||
title: ColiDo 2.0 Plus
|
title: ColiDo 2.0 Plus
|
||||||
heatedBed: true
|
heatedBed: true
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 230
|
x: 230
|
||||||
y: 150
|
y: 150
|
||||||
z: 140
|
z: 140
|
||||||
colido_compact:
|
colido_compact:
|
||||||
title: ColiDo Compact
|
title: ColiDo Compact
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 130
|
x: 130
|
||||||
y: 130
|
y: 130
|
||||||
z: 115
|
z: 115
|
||||||
colido_diy:
|
colido_diy:
|
||||||
title: ColiDo DIY
|
title: ColiDo DIY
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
|
x: 200
|
||||||
|
y: 200
|
||||||
z: 170
|
z: 170
|
||||||
colido_m2020:
|
colido_m2020:
|
||||||
title: ColiDo M2020
|
title: ColiDo M2020
|
||||||
heatedBed: true
|
heatedBed: true
|
||||||
|
filamentThickness: 2.85
|
||||||
|
dimensions:
|
||||||
|
x: 200
|
||||||
|
y: 200
|
||||||
|
z: 200
|
||||||
colido_x3045:
|
colido_x3045:
|
||||||
title: ColiDo X3045
|
title: ColiDo X3045
|
||||||
heatedBed: true
|
heatedBed: true
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 300
|
x: 300
|
||||||
y: 300
|
y: 300
|
||||||
@ -42,23 +81,27 @@ colido_x3045:
|
|||||||
craftbot_plus:
|
craftbot_plus:
|
||||||
title: CraftBot PLUS
|
title: CraftBot PLUS
|
||||||
heatedBed: true
|
heatedBed: true
|
||||||
filamentThickness: 1.75
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 250
|
x: 250
|
||||||
|
y: 200
|
||||||
|
z: 200
|
||||||
cyrus:
|
cyrus:
|
||||||
title: Cyrus
|
title: Cyrus
|
||||||
|
heatedBed: false
|
||||||
|
dimensions:
|
||||||
|
x: 195
|
||||||
|
y: 195
|
||||||
|
z: 200
|
||||||
delta_rostockmax:
|
delta_rostockmax:
|
||||||
title: Delta RostockMax
|
title: Delta RostockMax
|
||||||
dimensions:
|
heatedBed: false
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
deltamaker:
|
deltamaker:
|
||||||
title: Deltamaker
|
title: Deltamaker
|
||||||
dimensions:
|
heatedBed: false
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
doodle_dream:
|
doodle_dream:
|
||||||
title: Doodle Dream
|
title: Doodle Dream
|
||||||
|
heatedBed: false
|
||||||
filamentThickness: 1.75
|
filamentThickness: 1.75
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 120
|
x: 120
|
||||||
@ -66,68 +109,119 @@ doodle_dream:
|
|||||||
z: 80
|
z: 80
|
||||||
eventorbot:
|
eventorbot:
|
||||||
title: EventorBot
|
title: EventorBot
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
|
dimensions:
|
||||||
|
x: 203
|
||||||
|
y: 250
|
||||||
|
z: 150
|
||||||
felix:
|
felix:
|
||||||
title: Felix
|
title: Felix
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
|
dimensions:
|
||||||
|
x: 237
|
||||||
|
y: 244
|
||||||
|
z: 235
|
||||||
gigabot:
|
gigabot:
|
||||||
title: Gigabot
|
title: Gigabot
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
kossel:
|
kossel:
|
||||||
title: Kossel
|
title: Kossel
|
||||||
dimensions:
|
heatedBed: false
|
||||||
x: 0
|
filamentThickness: 2.85
|
||||||
y: 0
|
|
||||||
leapfrog_creatr:
|
leapfrog_creatr:
|
||||||
title: LeapFrog Creatr
|
title: LeapFrog Creatr
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
lulzbot_aO_101:
|
lulzbot_aO_101:
|
||||||
title: LulzBot AO-101
|
title: LulzBot AO-101
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
lulzbot_taz_4:
|
lulzbot_taz_4:
|
||||||
title: LulzBot TAZ 4
|
title: LulzBot TAZ 4
|
||||||
|
heatedBed: true
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 298
|
x: 298
|
||||||
y: 275
|
y: 275
|
||||||
z: 250
|
z: 250
|
||||||
heatedBed: true
|
|
||||||
makerbot_generic:
|
makerbot_generic:
|
||||||
title: Generic Makerbot Printer
|
title: Generic Makerbot Printer
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
makerbot_replicator2:
|
makerbot_replicator2:
|
||||||
title: MakerBot Replicator2
|
title: MakerBot Replicator2
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
makerbot_replicator2x:
|
makerbot_replicator2x:
|
||||||
title: MakerBot Replicator2x
|
title: MakerBot Replicator2x
|
||||||
heatedBed: true
|
heatedBed: true
|
||||||
|
filamentThickness: 2.85
|
||||||
makerbot_thingomatic:
|
makerbot_thingomatic:
|
||||||
title: MakerBot Thing-o-matic
|
title: MakerBot Thing-o-matic
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
makergear_m2:
|
makergear_m2:
|
||||||
title: MakerGear M2
|
title: MakerGear M2
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
makergear_prusa:
|
makergear_prusa:
|
||||||
title: MakerGear Prusa
|
title: MakerGear Prusa
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
makibox:
|
makibox:
|
||||||
title: Makibox
|
title: Makibox
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
mamba3d:
|
mamba3d:
|
||||||
title: Mamba3D
|
title: Mamba3D
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
marlin_generic:
|
marlin_generic:
|
||||||
title: Generic Marlin Printer
|
title: Generic Marlin Printer
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
minifactory:
|
minifactory:
|
||||||
title: miniFactory
|
title: miniFactory
|
||||||
|
heatedBed: true
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 150
|
x: 150
|
||||||
y: 150
|
y: 150
|
||||||
z: 155
|
z: 155
|
||||||
heatedBed: true
|
|
||||||
orca_0_3:
|
orca_0_3:
|
||||||
title: Orca 0.3
|
title: Orca 0.3
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
ord_bot_hadron:
|
ord_bot_hadron:
|
||||||
title: ORD Bot Hadron
|
title: ORD Bot Hadron
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
printrbot:
|
printrbot:
|
||||||
title: Printrbot
|
title: Printrbot
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
printxel_3d:
|
printxel_3d:
|
||||||
title: Printxel 3D
|
title: Printxel 3D
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
prusa_i3:
|
prusa_i3:
|
||||||
title: Prusa I3
|
title: Prusa I3
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
prusa_iteration_2:
|
prusa_iteration_2:
|
||||||
title: Prusa Iteration 2
|
title: Prusa Iteration 2
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
rapman:
|
rapman:
|
||||||
title: RapMan
|
title: RapMan
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
renkforce_rf100:
|
renkforce_rf100:
|
||||||
title: Renkforce RF100
|
title: Renkforce RF100
|
||||||
|
heatedBed: false
|
||||||
filamentThickness: 1.75
|
filamentThickness: 1.75
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 100
|
x: 100
|
||||||
@ -135,21 +229,36 @@ renkforce_rf100:
|
|||||||
z: 100
|
z: 100
|
||||||
reprappro_huxley:
|
reprappro_huxley:
|
||||||
title: RepRapPro Huxley
|
title: RepRapPro Huxley
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
reprappro_mendel:
|
reprappro_mendel:
|
||||||
title: RepRapPro Mendel
|
title: RepRapPro Mendel
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
rigidbot:
|
rigidbot:
|
||||||
title: Rigidbot
|
title: Rigidbot
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
robo_3d_printer:
|
robo_3d_printer:
|
||||||
title: RoBo 3D Printer
|
title: RoBo 3D Printer
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
shapercube:
|
shapercube:
|
||||||
title: ShaperCube
|
title: ShaperCube
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
tantillus:
|
tantillus:
|
||||||
title: Tantillus
|
title: Tantillus
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
ultimaker:
|
ultimaker:
|
||||||
title: Ultimaker Original
|
title: Ultimaker Original
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
ultimaker2:
|
ultimaker2:
|
||||||
title: Ultimaker 2
|
title: Ultimaker 2
|
||||||
heatedBed: true
|
heatedBed: true
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 223
|
x: 223
|
||||||
y: 223
|
y: 223
|
||||||
@ -157,12 +266,15 @@ ultimaker2:
|
|||||||
ultimaker2_plus:
|
ultimaker2_plus:
|
||||||
title: Ultimaker 2+
|
title: Ultimaker 2+
|
||||||
heatedBed: true
|
heatedBed: true
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 223
|
x: 223
|
||||||
y: 223
|
y: 223
|
||||||
z: 305
|
z: 305
|
||||||
ultimaker2go:
|
ultimaker2go:
|
||||||
title: Ultimaker 2 Go
|
title: Ultimaker 2 Go
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 120
|
x: 120
|
||||||
y: 120
|
y: 120
|
||||||
@ -170,17 +282,20 @@ ultimaker2go:
|
|||||||
ultimaker_original_plus:
|
ultimaker_original_plus:
|
||||||
title: Ultimaker Original Plus
|
title: Ultimaker Original Plus
|
||||||
heatedBed: true
|
heatedBed: true
|
||||||
|
filamentThickness: 2.85
|
||||||
vision_3d_printer:
|
vision_3d_printer:
|
||||||
title: Vision 3D Printer
|
title: Vision 3D Printer
|
||||||
|
heatedBed: false
|
||||||
|
filamentThickness: 2.85
|
||||||
wanhao_duplicator4:
|
wanhao_duplicator4:
|
||||||
title: Wanhao Duplicator 4
|
title: Wanhao Duplicator 4
|
||||||
filamentThickness: 1.75
|
|
||||||
heatedBed: true
|
heatedBed: true
|
||||||
|
filamentThickness: 1.75
|
||||||
dimensions:
|
dimensions:
|
||||||
x: 210
|
x: 210
|
||||||
y: 140
|
y: 140
|
||||||
z: 140
|
z: 140
|
||||||
wanhao_duplicator_i3_plus:
|
wanhao_duplicator_i3_plus:
|
||||||
title: Wanhao Duplicator i3 Plus
|
title: Wanhao Duplicator i3 Plus
|
||||||
filamentThickness: 1.75
|
|
||||||
heatedBed: false
|
heatedBed: false
|
||||||
|
filamentThickness: 1.75
|
||||||
|
@ -1,11 +1,54 @@
|
|||||||
low:
|
low:
|
||||||
title: "Low"
|
title: "Low"
|
||||||
|
thickness:
|
||||||
|
top: 0.30
|
||||||
|
bottom: 0.30
|
||||||
|
shell: 0.4
|
||||||
layerHeight: .2
|
layerHeight: .2
|
||||||
fill:
|
innerShell:
|
||||||
gridSize: 15.0
|
speed: 80.0
|
||||||
|
outerShell:
|
||||||
|
speed: 70.0
|
||||||
|
outerInfill:
|
||||||
|
speed: 80.0
|
||||||
|
firstLayer:
|
||||||
|
speed: 70.0
|
||||||
|
innerInfill:
|
||||||
|
speed: 80.0
|
||||||
|
gridSize: 25.0
|
||||||
medium:
|
medium:
|
||||||
title: "Medium"
|
title: "Medium"
|
||||||
layerHeight: .15
|
layerHeight: .15
|
||||||
|
thickness:
|
||||||
|
top: 0.45
|
||||||
|
bottom: 0.45
|
||||||
|
shell: 0.8
|
||||||
|
innerShell:
|
||||||
|
speed: 50.0
|
||||||
|
outerShell:
|
||||||
|
speed: 40.0
|
||||||
|
outerInfill:
|
||||||
|
speed: 50.0
|
||||||
|
firstLayer:
|
||||||
|
speed: 40.0
|
||||||
|
innerInfill:
|
||||||
|
speed: 80.0
|
||||||
|
gridSize: 25.0
|
||||||
high:
|
high:
|
||||||
title: "High"
|
title: "High"
|
||||||
|
thickness:
|
||||||
|
top: 0.60
|
||||||
|
bottom: 0.60
|
||||||
|
shell: 1.2
|
||||||
layerHeight: .1
|
layerHeight: .1
|
||||||
|
innerShell:
|
||||||
|
speed: 40.0
|
||||||
|
outerShell:
|
||||||
|
speed: 30.0
|
||||||
|
outerInfill:
|
||||||
|
speed: 40.0
|
||||||
|
firstLayer:
|
||||||
|
speed: 30.0
|
||||||
|
innerInfill:
|
||||||
|
speed: 70.0
|
||||||
|
gridSize: 10.0
|
||||||
|
@ -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,30 +1,26 @@
|
|||||||
import * as THREE from 'three';
|
|
||||||
|
|
||||||
export default function calculateLayersIntersections(lines, settings) {
|
export default function calculateLayersIntersections(lines, settings) {
|
||||||
const {
|
const {
|
||||||
|
dimensions: { z: dimensionsZ },
|
||||||
layerHeight,
|
layerHeight,
|
||||||
dimensions: { z: dimensionsZ }
|
zOffset
|
||||||
} = settings;
|
} = settings;
|
||||||
|
|
||||||
const numLayers = Math.floor(dimensionsZ / 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 max = Math.floor((Math.max(line.start.y, line.end.y) - zOffset) / layerHeight);
|
||||||
const min = Math.ceil(Math.min(line.start.y, line.end.y) / layerHeight);
|
|
||||||
const max = Math.floor(Math.max(line.start.y, line.end.y) / 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) {
|
||||||
|
const y = layerIndex * layerHeight + zOffset;
|
||||||
layerIntersectionIndexes[layerIndex].push(lineIndex);
|
|
||||||
|
|
||||||
const y = layerIndex * layerHeight;
|
|
||||||
|
|
||||||
let x, z;
|
let x, z;
|
||||||
if (line.start.y === line.end.y) {
|
if (line.start.y === line.end.y) {
|
||||||
@ -37,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 THREE.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,15 +1,20 @@
|
|||||||
import * as THREE from 'three';
|
import { Line3 } from 'three/src/math/Line3.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 THREE.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;
|
||||||
}
|
}
|
||||||
@ -18,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 THREE.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);
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import * as THREE from 'three';
|
import { Vector2 } from 'three/src/math/Vector2.js';
|
||||||
import { PRECISION } from '../../constants.js';
|
import { PRECISION } from '../../constants.js';
|
||||||
|
|
||||||
export const MOVE = 'G';
|
export const MOVE = 'G';
|
||||||
@ -16,7 +16,7 @@ export default class {
|
|||||||
|
|
||||||
this._gcode = [];
|
this._gcode = [];
|
||||||
this._currentValues = {};
|
this._currentValues = {};
|
||||||
this._nozzlePosition = new THREE.Vector2(0, 0);
|
this._nozzlePosition = new Vector2(0, 0);
|
||||||
this._extruder = 0.0;
|
this._extruder = 0.0;
|
||||||
this._duration = 0.0;
|
this._duration = 0.0;
|
||||||
this._isRetracted = false;
|
this._isRetracted = false;
|
||||||
@ -47,7 +47,7 @@ export default class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
moveTo(x, y, z, { speed }) {
|
moveTo(x, y, z, { speed }) {
|
||||||
const newNozzlePosition = new THREE.Vector2(x, y).multiplyScalar(PRECISION);
|
const newNozzlePosition = new Vector2(x, y).multiplyScalar(PRECISION);
|
||||||
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
|
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
|
||||||
|
|
||||||
this._duration += lineLength / speed;
|
this._duration += lineLength / speed;
|
||||||
@ -66,7 +66,7 @@ export default class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lineTo(x, y, z, { speed, flowRate }) {
|
lineTo(x, y, z, { speed, flowRate }) {
|
||||||
const newNozzlePosition = new THREE.Vector2(x, y).multiplyScalar(PRECISION);
|
const newNozzlePosition = new Vector2(x, y).multiplyScalar(PRECISION);
|
||||||
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
|
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
|
||||||
|
|
||||||
this._extruder += this._nozzleToFilamentRatio * lineLength * flowRate;
|
this._extruder += this._nozzleToFilamentRatio * lineLength * flowRate;
|
||||||
|
@ -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 * as THREE from 'three';
|
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, openObjectIndexes, settings) {
|
||||||
const layers = [];
|
const layers = [];
|
||||||
|
|
||||||
for (let layer = 1; layer < layerIntersectionIndexes.length; layer ++) {
|
for (let layer = 0; 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 = openObjectIndexes[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 THREE.Vector2(intersection.x, intersection.y);
|
if (distanceTo(closestSegmentEnd[0], lastPoint) < .001) endHit = true;
|
||||||
const b = new THREE.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];
|
||||||
// THREE.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,8 +1,8 @@
|
|||||||
import * as THREE from 'three';
|
import { Vector2 } from 'three/src/math/Vector2.js';
|
||||||
import Shape from 'clipper-js';
|
import Shape from 'clipper-js';
|
||||||
|
|
||||||
export default function optimizePaths(slices, settings) {
|
export default function optimizePaths(slices, settings) {
|
||||||
const start = new THREE.Vector2(0, 0);
|
const start = new Vector2(0, 0);
|
||||||
|
|
||||||
for (let layer = 0; layer < slices.length; layer ++) {
|
for (let layer = 0; layer < slices.length; layer ++) {
|
||||||
const slice = slices[layer];
|
const slice = slices[layer];
|
||||||
@ -102,7 +102,7 @@ function optimizeShape(shape, start) {
|
|||||||
|
|
||||||
if (shape.closed) {
|
if (shape.closed) {
|
||||||
for (let j = 0; j < path.length; j += 1) {
|
for (let j = 0; j < path.length; j += 1) {
|
||||||
const point = new THREE.Vector2().copy(path[j]);
|
const point = new Vector2().copy(path[j]);
|
||||||
const length = point.sub(start).length();
|
const length = point.sub(start).length();
|
||||||
if (minLength === false || length < minLength) {
|
if (minLength === false || length < minLength) {
|
||||||
minPath = path;
|
minPath = path;
|
||||||
@ -112,7 +112,7 @@ function optimizeShape(shape, start) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const startPoint = new THREE.Vector2().copy(path[0]);
|
const startPoint = new Vector2().copy(path[0]);
|
||||||
const lengthToStart = startPoint.sub(start).length();
|
const lengthToStart = startPoint.sub(start).length();
|
||||||
if (minLength === false || lengthToStart < minLength) {
|
if (minLength === false || lengthToStart < minLength) {
|
||||||
minPath = path;
|
minPath = path;
|
||||||
@ -121,7 +121,7 @@ function optimizeShape(shape, start) {
|
|||||||
pathIndex = i;
|
pathIndex = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
const endPoint = new THREE.Vector2().copy(path[path.length - 1]);
|
const endPoint = new Vector2().copy(path[path.length - 1]);
|
||||||
const lengthToEnd = endPoint.sub(start).length();
|
const lengthToEnd = endPoint.sub(start).length();
|
||||||
if (lengthToEnd < minLength) {
|
if (lengthToEnd < minLength) {
|
||||||
minPath = path;
|
minPath = path;
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import * as THREE from 'three';
|
import { Color } from 'three/src/math/Color.js';
|
||||||
|
import { BufferGeometry } from 'three/src/core/BufferGeometry.js';
|
||||||
|
import { BufferAttribute } from 'three/src/core/BufferAttribute.js';
|
||||||
|
import { LineBasicMaterial } from 'three/src/materials/LineBasicMaterial.js';
|
||||||
|
import { VertexColors } from 'three/src/constants.js';
|
||||||
|
import { LineSegments } from 'three/src/objects/LineSegments.js';
|
||||||
import calculateLayersIntersections from './calculateLayersIntersections.js';
|
import calculateLayersIntersections from './calculateLayersIntersections.js';
|
||||||
import createLines from './createLines.js';
|
import createLines from './createLines.js';
|
||||||
import generateInfills from './generateInfills.js';
|
import generateInfills from './generateInfills.js';
|
||||||
@ -10,12 +15,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 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, geometry, openObjectIndexes, constructLinePreview, onProgress) {
|
||||||
const totalStages = 12;
|
const totalStages = 11;
|
||||||
let current = -1;
|
let current = -1;
|
||||||
const updateProgress = (action) => {
|
const updateProgress = (action) => {
|
||||||
current ++;
|
current ++;
|
||||||
@ -30,23 +34,14 @@ export default function(settings, geometry, constructLinePreview, onProgress) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
geometry.computeFaceNormals();
|
|
||||||
|
|
||||||
// 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, openObjectIndexes, settings);
|
||||||
|
|
||||||
applyPrecision(shapes);
|
applyPrecision(shapes);
|
||||||
|
|
||||||
@ -86,7 +81,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}`;
|
||||||
@ -99,41 +94,35 @@ function gcodeToString(gcode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MAX_SPEED = 100 * 60;
|
const MAX_SPEED = 100 * 60;
|
||||||
|
const COLOR = new Color();
|
||||||
function createGcodeGeometry(gcode) {
|
function createGcodeGeometry(gcode) {
|
||||||
const positions = [];
|
const positions = [];
|
||||||
const colors = [];
|
const colors = [];
|
||||||
|
|
||||||
let lastPoint
|
let lastPoint = [0, 0, 0];
|
||||||
for (let i = 0; i < gcode.length; i ++) {
|
for (let i = 0; i < gcode.length; i ++) {
|
||||||
const { G, F, X, Y, Z } = gcode[i];
|
const { G, F, X, Y, Z } = gcode[i];
|
||||||
|
|
||||||
if (X || Y || Z) {
|
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 (G === 1) {
|
||||||
if (lastPoint) positions.push(lastPoint[0], lastPoint[1], lastPoint[2]);
|
positions.push(lastPoint.Y, lastPoint.Z, lastPoint.X);
|
||||||
positions.push(Y, Z, X);
|
positions.push(Y, Z, X);
|
||||||
|
|
||||||
|
const color = (G === 0) ? COLOR.setHex(0x00ff00) : COLOR.setHSL(F / MAX_SPEED, 0.5, 0.5);
|
||||||
colors.push(color.r, color.g, color.b);
|
colors.push(color.r, color.g, color.b);
|
||||||
colors.push(color.r, color.g, color.b);
|
colors.push(color.r, color.g, color.b);
|
||||||
}
|
}
|
||||||
|
lastPoint = { X, Y, Z };
|
||||||
lastPoint = [Y, Z, X];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const geometry = new THREE.BufferGeometry();
|
const geometry = new BufferGeometry();
|
||||||
|
|
||||||
geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3));
|
geometry.addAttribute('position', new BufferAttribute(new Float32Array(positions), 3));
|
||||||
geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3));
|
geometry.addAttribute('color', new BufferAttribute(new Float32Array(colors), 3));
|
||||||
|
|
||||||
const material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });
|
const material = new LineBasicMaterial({ vertexColors: VertexColors });
|
||||||
const linePreview = new THREE.LineSegments(geometry, material);
|
const linePreview = new LineSegments(geometry, material);
|
||||||
|
|
||||||
return linePreview;
|
return linePreview;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@ export default function slicesToGCode(slices, settings) {
|
|||||||
travelSpeed,
|
travelSpeed,
|
||||||
retraction,
|
retraction,
|
||||||
travel,
|
travel,
|
||||||
combing
|
combing,
|
||||||
|
zOffset
|
||||||
} = settings;
|
} = settings;
|
||||||
|
|
||||||
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
|
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
|
||||||
@ -29,7 +30,7 @@ export default function slicesToGCode(slices, settings) {
|
|||||||
let isFirstLayer = true;
|
let isFirstLayer = true;
|
||||||
for (let layer = 0; layer < slices.length; layer ++) {
|
for (let layer = 0; layer < slices.length; layer ++) {
|
||||||
const slice = slices[layer];
|
const slice = slices[layer];
|
||||||
const z = layer * layerHeight + 0.2;
|
const z = layer * layerHeight + zOffset;
|
||||||
|
|
||||||
if (layer === 1) {
|
if (layer === 1) {
|
||||||
gcode.turnFanOn();
|
gcode.turnFanOn();
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import * as THREE from 'three';
|
import { VertexColors } from 'three/src/constants.js';
|
||||||
|
import { BufferAttribute } from 'three/src/core/BufferAttribute.js';
|
||||||
|
import { LineBasicMaterial } from 'three/src/materials/LineBasicMaterial.js';
|
||||||
|
import { LineSegments } from 'three/src/objects/LineSegments.js';
|
||||||
import slice from './sliceActions/slice.js';
|
import slice from './sliceActions/slice.js';
|
||||||
import SlicerWorker from './slicer.worker.js';
|
import SlicerWorker from './slicer.worker.js';
|
||||||
|
import { FrontSide, DoubleSide } from 'three/src/constants.js';
|
||||||
|
import { BufferGeometry } from 'three/src/core/BufferGeometry.js'
|
||||||
|
|
||||||
export function sliceMesh(settings, mesh, sync = false, constructLinePreview = false, onProgress) {
|
export function sliceMesh(settings, mesh, sync = false, constructLinePreview = false, onProgress) {
|
||||||
if (!mesh || !mesh.isMesh) {
|
if (!mesh || !mesh.isMesh) {
|
||||||
@ -8,15 +13,15 @@ export function sliceMesh(settings, mesh, sync = false, constructLinePreview = f
|
|||||||
}
|
}
|
||||||
|
|
||||||
mesh.updateMatrix();
|
mesh.updateMatrix();
|
||||||
const { geometry, matrix } = mesh;
|
const { geometry, matrix, material } = mesh;
|
||||||
return sliceGeometry(settings, geometry, matrix, sync, onProgress);
|
return sliceGeometry(settings, geometry, material, matrix, sync, constructLinePreview, onProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sliceGeometry(settings, geometry, matrix, sync = false, constructLinePreview = false, onProgress) {
|
export function sliceGeometry(settings, geometry, materials, matrix, sync = false, constructLinePreview = false, onProgress) {
|
||||||
if (!geometry) {
|
if (!geometry) {
|
||||||
throw new Error('Missing required geometry argument');
|
throw new Error('Missing required geometry argument');
|
||||||
} else if (geometry.isBufferGeometry) {
|
} else if (geometry.isBufferGeometry) {
|
||||||
geometry = new THREE.Geometry().fromBufferGeometry(geometry);
|
geometry = new Geometry().fromBufferGeometry(geometry);
|
||||||
} else if (geometry.isGeometry) {
|
} else if (geometry.isGeometry) {
|
||||||
geometry = geometry.clone();
|
geometry = geometry.clone();
|
||||||
} else {
|
} else {
|
||||||
@ -31,18 +36,29 @@ export function sliceGeometry(settings, geometry, matrix, sync = false, construc
|
|||||||
geometry.applyMatrix(matrix);
|
geometry.applyMatrix(matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openObjectIndexes = materials instanceof Array ? materials.map(({ side }) => {
|
||||||
|
switch (side) {
|
||||||
|
case FrontSide:
|
||||||
|
return false;
|
||||||
|
case DoubleSide:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}) : [false];
|
||||||
|
|
||||||
if (sync) {
|
if (sync) {
|
||||||
return sliceSync(settings, geometry, constructLinePreview, onProgress);
|
return sliceSync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
|
||||||
} else {
|
} else {
|
||||||
return sliceAsync(settings, geometry, constructLinePreview, onProgress);
|
return sliceAsync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sliceSync(settings, geometry, constructLinePreview, onProgress) {
|
function sliceSync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) {
|
||||||
return slice(settings, geometry, constructLinePreview, onProgress);
|
return slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sliceAsync(settings, geometry, constructLinePreview, onProgress) {
|
function sliceAsync(settings, geometry, openObjectIndexes, 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();
|
||||||
@ -60,14 +76,14 @@ function sliceAsync(settings, geometry, constructLinePreview, onProgress) {
|
|||||||
slicerWorker.terminate();
|
slicerWorker.terminate();
|
||||||
|
|
||||||
if (data.gcode.linePreview) {
|
if (data.gcode.linePreview) {
|
||||||
const geometry = new THREE.BufferGeometry();
|
const geometry = new BufferGeometry();
|
||||||
|
|
||||||
const { position, color } = data.gcode.linePreview;
|
const { position, color } = data.gcode.linePreview;
|
||||||
geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(position), 3));
|
geometry.addAttribute('position', new BufferAttribute(new Float32Array(position), 3));
|
||||||
geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(color), 3));
|
geometry.addAttribute('color', new BufferAttribute(new Float32Array(color), 3));
|
||||||
|
|
||||||
const material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });
|
const material = new LineBasicMaterial({ vertexColors: VertexColors });
|
||||||
const linePreview = new THREE.LineSegments(geometry, material);
|
const linePreview = new LineSegments(geometry, material);
|
||||||
|
|
||||||
data.gcode.linePreview = linePreview;
|
data.gcode.linePreview = linePreview;
|
||||||
}
|
}
|
||||||
@ -88,11 +104,7 @@ function sliceAsync(settings, geometry, constructLinePreview, onProgress) {
|
|||||||
geometry = geometry.toJSON();
|
geometry = geometry.toJSON();
|
||||||
slicerWorker.postMessage({
|
slicerWorker.postMessage({
|
||||||
message: 'SLICE',
|
message: 'SLICE',
|
||||||
data: {
|
data: { settings, geometry, openObjectIndexes, constructLinePreview }
|
||||||
settings,
|
|
||||||
geometry,
|
|
||||||
constructLinePreview
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import 'core-js'; // polyfills
|
import 'core-js'; // polyfills
|
||||||
import slice from './sliceActions/slice.js';
|
import slice from './sliceActions/slice.js';
|
||||||
import * as THREE from 'three';
|
import { Matrix4 } from 'three/src/math/Matrix4.js';
|
||||||
|
import { JSONLoader } from 'three/src/loaders/JSONLoader.js';
|
||||||
const loader = new THREE.JSONLoader();
|
|
||||||
|
|
||||||
const onProgress = progress => {
|
const onProgress = progress => {
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
@ -11,16 +10,18 @@ const onProgress = progress => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addEventListener('message', (event) => {
|
const loader = new JSONLoader();
|
||||||
|
|
||||||
|
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 { settings, geometry: JSONGeometry, constructLinePreview, openObjectIndexes } = data;
|
||||||
const { settings, geometry: JSONGeometry, constructLinePreview } = data;
|
|
||||||
const { geometry } = loader.parse(JSONGeometry.data);
|
const { geometry } = loader.parse(JSONGeometry.data);
|
||||||
|
|
||||||
const gcode = slice(settings, geometry, constructLinePreview, onProgress);
|
const gcode = slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
|
||||||
|
|
||||||
|
const buffers = [];
|
||||||
if (gcode.linePreview) {
|
if (gcode.linePreview) {
|
||||||
const position = gcode.linePreview.geometry.getAttribute('position').array;
|
const position = gcode.linePreview.geometry.getAttribute('position').array;
|
||||||
const color = gcode.linePreview.geometry.getAttribute('color').array;
|
const color = gcode.linePreview.geometry.getAttribute('color').array;
|
||||||
|
@ -2,17 +2,21 @@ 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: {
|
||||||
presets: [
|
presets: [
|
||||||
require('babel-preset-env'),
|
require('babel-preset-env'),
|
||||||
|
require('babel-preset-stage-0'),
|
||||||
require('babel-preset-react')
|
require('babel-preset-react')
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
require('babel-plugin-transform-object-rest-spread'),
|
|
||||||
require('babel-plugin-transform-class-properties'),
|
require('babel-plugin-transform-class-properties'),
|
||||||
require('babel-plugin-transform-runtime')
|
require('babel-plugin-transform-object-rest-spread'),
|
||||||
|
require('babel-plugin-transform-runtime'),
|
||||||
|
require('babel-plugin-transform-es2015-classes')
|
||||||
],
|
],
|
||||||
babelrc: false
|
babelrc: false
|
||||||
}
|
}
|
||||||
@ -26,13 +30,15 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'doodle3d-slicer': path.resolve(__dirname, '../src/index.js'),
|
'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: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
@ -46,6 +52,12 @@ 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$/,
|
||||||
|
use: ['raw-loader']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
Loading…
Reference in New Issue
Block a user