diff --git a/index.js b/index.js index 54d517b..e6ae0cb 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,13 @@ 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'; +import queryString from 'query-string'; injectTapEventPlugin(); @@ -26,17 +22,10 @@ jss.createStyleSheet({ } }).attach(); -function init(mesh) { - render(( - - - - ), document.getElementById('app')); -} +const { file } = queryString.parse(location.search); -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); +render(( + + + +), document.getElementById('app')); diff --git a/package-lock.json b/package-lock.json index cd11baa..d1dc294 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "integrity": "sha512-glELSijsD9b+/0d9iOdasBwqH3s+xPxD59tJ7aXkBx7klugygGOMXn7PB05AdhVyA1OYMj7GUCegaQa7nvLtmQ==" }, "@doodle3d/doodle3d-core": { - "version": "github:doodle3d/doodle3d-core#0c4e410a27ea2df8336a956e966ee16ad8ac04d7", + "version": "github:doodle3d/doodle3d-core#36a73c233e569fca79d2059a50edb6bdb511aa58", "dev": true, "requires": { "@doodle3d/cal": "0.0.8", @@ -35,16 +35,18 @@ "@doodle3d/threejs-export-stl": "0.0.5", "@doodle3d/touch-events": "0.0.7", "babel-polyfill": "6.26.0", - "bezier-js": "2.2.3", + "bezier-js": "2.2.5", "blueimp-canvas-to-blob": "3.14.0", "bowser": "1.8.1", "fit-curve": "0.1.6", + "google-fonts-webpack-plugin": "0.4.4", "imports-loader": "0.7.1", - "jss": "9.4.0", + "jss": "9.5.1", "keycode": "2.1.9", "lodash": "4.17.4", "memoizee": "0.3.10", - "pouchdb": "6.4.0", + "normalize-wheel": "1.0.1", + "pouchdb": "6.4.1", "proptypes": "1.1.0", "raf": "3.4.0", "ramda": "0.21.0", @@ -66,9 +68,9 @@ }, "dependencies": { "jss": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/jss/-/jss-9.4.0.tgz", - "integrity": "sha512-ckJpElL5CimehboeLDQoHeY7mlxn0KPnPn2EZVbn6pomhfbTXiQJ6fAJXSp9rUM2hPtE0PG8Swzdy9vhB2v82w==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/jss/-/jss-9.5.1.tgz", + "integrity": "sha512-py//ogG1xeztpEDmosJtrkfUXibx3qiAr+1GQvfLHp7azpqkzTPLCnainDgH7Zn0q6S7rcM1eINrVT9n/r5f2w==", "dev": true, "requires": { "is-in-browser": "1.1.3", @@ -1716,9 +1718,9 @@ } }, "bezier-js": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-2.2.3.tgz", - "integrity": "sha1-xVdBFqSjVkpxU41z4LDVFdqN3sU=", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-2.2.5.tgz", + "integrity": "sha512-HGh+GevPguxrAmnWF2/A+8c8FEatnKcE6WttpYWA5fn1CfpJz4reFbr11DuyFs2gwaIo9vF7aVXW2xg1iaqvyg==", "dev": true }, "big.js": { @@ -1952,6 +1954,12 @@ "isarray": "1.0.0" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, "buffer-from": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.1.tgz", @@ -2456,6 +2464,11 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", @@ -2729,9 +2742,9 @@ } }, "encoding-down": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-3.0.0.tgz", - "integrity": "sha1-IGjLZ7E3G14frJtfF44FpVUr+l4=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-3.0.1.tgz", + "integrity": "sha512-uvx+39YNqiPLqhXAvOSGBVy/oYBh4p2ShwG9YFCipwgfOhnVIOxuOPE3R9dEGM44bn0VHIrC3ojXq6lNf2ulwg==", "dev": true, "requires": { "abstract-leveldown": "3.0.0", @@ -2748,9 +2761,9 @@ } }, "end-of-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", - "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { "once": "1.4.0" @@ -2836,9 +2849,9 @@ } }, "es6-error": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.0.2.tgz", - "integrity": "sha1-7sXHJurO9Rt/a3PCDbbhsTsGnJg=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, "es6-iterator": { @@ -3173,6 +3186,15 @@ } } }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "1.2.0" + } + }, "file-saver": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.3.tgz", @@ -4081,14 +4103,6 @@ } } }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, "string-width": { "version": "1.0.2", "bundled": true, @@ -4099,6 +4113,14 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, "stringstream": { "version": "0.0.5", "bundled": true, @@ -4338,6 +4360,36 @@ "pinkie-promise": "2.0.1" } }, + "google-fonts-webpack-plugin": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/google-fonts-webpack-plugin/-/google-fonts-webpack-plugin-0.4.4.tgz", + "integrity": "sha512-+e2D9/DVBG9EDydRovzoqMZ658SsTBGbC0c65GyZqkwNvdj8vRSYQKXqbz7/yt7QaXsCPT1MpH45r3ivWOitcw==", + "dev": true, + "requires": { + "lodash": "4.17.4", + "node-fetch": "1.7.3", + "webpack-sources": "0.2.3", + "yauzl": "2.9.1" + }, + "dependencies": { + "source-list-map": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz", + "integrity": "sha1-mIkBnRAkzOVc3AaUmDN+9hhqEaE=", + "dev": true + }, + "webpack-sources": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz", + "integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=", + "dev": true, + "requires": { + "source-list-map": "1.1.2", + "source-map": "0.5.6" + } + } + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -5504,7 +5556,7 @@ "integrity": "sha512-I97zvGOZ6fJ9OFfYv+QmgBpDWbC+UaP5ERJ3oraTyk1v+ABAL4tazris5ym5qL2iLe+qNjXNM/iP8LQcoZMEWw==", "dev": true, "requires": { - "encoding-down": "3.0.0", + "encoding-down": "3.0.1", "levelup": "2.0.1" } }, @@ -6198,6 +6250,12 @@ "white-space-x": "3.0.0" } }, + "normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU=", + "dev": true + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -6534,6 +6592,12 @@ "sha.js": "2.4.9" } }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, "pepjs": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/pepjs/-/pepjs-0.4.3.tgz", @@ -6596,9 +6660,9 @@ } }, "pouchdb": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/pouchdb/-/pouchdb-6.4.0.tgz", - "integrity": "sha512-R9sm7USMctC1/itY9UdtA8iVOF04Ui+rsGnNdO9zLTpolzglWskSL/0B3RQ2OchGYLNgsaZS0UzQ7AQ1SHXobg==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/pouchdb/-/pouchdb-6.4.1.tgz", + "integrity": "sha512-7YrhsBXbQh/iPA8O5Nzixi9QigaQJjqbbCFr+D7Kc258oeXNW9a0t/tOME1Lh84TJiFRuN9982FGVnrBrUhLiA==", "dev": true, "requires": { "argsarray": "0.0.1", @@ -6679,7 +6743,7 @@ "npmlog": "4.1.2", "os-homedir": "1.0.2", "pump": "1.0.3", - "rc": "1.2.2", + "rc": "1.2.3", "simple-get": "1.4.3", "tar-fs": "1.16.0", "tunnel-agent": "0.6.0", @@ -6802,7 +6866,7 @@ "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", "dev": true, "requires": { - "end-of-stream": "1.4.0", + "end-of-stream": "1.4.1", "once": "1.4.0" } }, @@ -6818,6 +6882,16 @@ "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", "dev": true }, + "query-string": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.0.1.tgz", + "integrity": "sha512-aM+MkQClojlNiKkO09tiN2Fv8jM/L7GWIjG2liWeKljlOdOPNWr+bW3KQ+w5V/uKprpezC7fAsAMsJtJ+2rLKA==", + "requires": { + "decode-uri-component": "0.2.0", + "object-assign": "4.1.1", + "strict-uri-encode": "1.1.0" + } + }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -6944,9 +7018,9 @@ "dev": true }, "rc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz", - "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.3.tgz", + "integrity": "sha1-UVdakA+N1oOBxxC0cSwhVMPiA1s=", "dev": true, "requires": { "deep-extend": "0.4.2", @@ -7210,7 +7284,7 @@ "dev": true, "requires": { "deep-equal": "1.0.1", - "es6-error": "4.0.2", + "es6-error": "4.1.1", "hoist-non-react-statics": "2.3.1", "invariant": "2.2.2", "is-promise": "2.1.0", @@ -7873,14 +7947,10 @@ "xtend": "4.0.1" } }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, "string-width": { "version": "2.1.1", @@ -7915,6 +7985,15 @@ } } }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", @@ -7991,7 +8070,7 @@ "dev": true, "requires": { "bl": "1.2.1", - "end-of-stream": "1.4.0", + "end-of-stream": "1.4.1", "readable-stream": "2.3.3", "xtend": "4.0.1" } @@ -9036,6 +9115,16 @@ } } }, + "yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha1-qBmB6nCleUYTOIPwKcWCGok1mn8=", + "dev": true, + "requires": { + "buffer-crc32": "0.2.13", + "fd-slicer": "1.0.1" + } + }, "yml-loader": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/yml-loader/-/yml-loader-2.1.0.tgz", diff --git a/package.json b/package.json index 26cabdb..6d7cfc5 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,13 @@ }, "dependencies": { "@doodle3d/clipper-js": "^1.0.7", + "@doodle3d/doodle3d-core": "github:doodle3d/doodle3d-core", "babel-plugin-transform-class-properties": "^6.24.1", "file-saver": "^1.3.3", "lodash": "^4.17.4", "material-ui": "^0.19.4", "proptypes": "^1.1.0", + "query-string": "^5.0.1", "react": "^16.0.0", "react-dom": "^16.0.0", "react-jss": "^7.2.0", @@ -28,7 +30,6 @@ "three": "^0.88.0" }, "devDependencies": { - "@doodle3d/doodle3d-core": "github:doodle3d/doodle3d-core", "babel-cli": "6.24.1", "babel-loader": "7.0.0", "babel-plugin-transform-es2015-classes": "^6.24.1", diff --git a/src/interface/index.js b/src/interface/index.js index bc31557..b4e94ec 100644 --- a/src/interface/index.js +++ b/src/interface/index.js @@ -4,7 +4,7 @@ import { Quaternion } from 'three/src/math/Quaternion.js'; import { Vector3 } from 'three/src/math/Vector3.js'; import { Mesh } from 'three/src/objects/Mesh.js'; import PropTypes from 'proptypes'; -import { placeOnGround, createScene, fetchProgress, slice, TabTemplate } from './utils.js'; +import { centerGeometry, placeOnGround, createScene, fetchProgress, slice, TabTemplate } from './utils.js'; import injectSheet from 'react-jss'; import RaisedButton from 'material-ui/RaisedButton'; import FlatButton from 'material-ui/FlatButton'; @@ -21,6 +21,10 @@ import printerSettings from '../settings/printer.yml'; import materialSettings from '../settings/material.yml'; import qualitySettings from '../settings/quality.yml'; import ReactResizeDetector from 'react-resize-detector'; +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'; const MAX_FULLSCREEN_WIDTH = 720; @@ -82,7 +86,10 @@ const styles = { class Interface extends React.Component { static propTypes = { - mesh: PropTypes.shape({ isMesh: PropTypes.oneOf([true]) }).isRequired, + file: PropTypes.oneOfType([ + PropTypes.shape({ isMesh: PropTypes.oneOf([true]) }), + PropTypes.string + ]).isRequired, classes: PropTypes.objectOf(PropTypes.string), defaultSettings: PropTypes.object.isRequired, printers: PropTypes.object.isRequired, @@ -110,9 +117,11 @@ class Interface extends React.Component { constructor(props) { super(props); const { defaultPrinter, defaultQuality, defaultMaterial, printers, quality, material, defaultSettings } = props; + this.state = { showFullScreen: false, isSlicing: false, + isLoading: true, error: null, printers: defaultPrinter, quality: defaultQuality, @@ -133,9 +142,35 @@ class Interface extends React.Component { componentDidMount() { const { canvas } = this.refs; - const scene = createScene(canvas, this.props, this.state); - this.setState({ ...scene }); + + this.setState({ scene }); + + const { file } = this.props; + + if (!file) { + throw new Error('no file provided'); + } if (typeof file === 'string') { + fetch(file) + .then(resonse => resonse.json()) + .then(json => JSONToSketchData(json)) + .then(file => createSceneData(file)) + .then(sketch => generateExportMesh(sketch, { offsetSingleWalls: false, matrix: new Matrix4() })) + .then(mesh => this.updateMesh(mesh, scene)); + } else if (file.isMesh) { + this.updateMesh(file, scene); + } else { + throw new Error('unknown file property'); + } + } + + updateMesh(mesh, scene) { + scene.mesh.geometry = mesh.geometry; + centerGeometry(scene.mesh); + placeOnGround(scene.mesh); + scene.render(); + + this.setState({ mesh, isLoading: false }); } componentWillUnmount() { @@ -146,8 +181,8 @@ class Interface extends React.Component { } resetMesh = () => { - if (isSlicing) return; const { scene: { mesh, render }, isSlicing, isLoading } = this.state; + if (isSlicing || isLoading) return; if (mesh) { mesh.position.set(0, 0, 0); mesh.scale.set(1, 1, 1); @@ -161,8 +196,8 @@ class Interface extends React.Component { scaleUp = () => this.scaleMesh(0.9); scaleDown = () => this.scaleMesh(1.0 / 0.9); scaleMesh = (factor) => { - if (isSlicing) return; const { scene: { mesh, render }, isSlicing, isLoading } = this.state; + if (isSlicing || isLoading) return; if (mesh) { mesh.scale.multiplyScalar(factor); mesh.updateMatrix(); @@ -175,8 +210,8 @@ class Interface extends React.Component { 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) => { - if (isSlicing) return; const { scene: { mesh, render }, isSlicing, isLoading } = this.state; + if (isSlicing || isLoading) return; if (mesh) { mesh.rotateOnWorldAxis(axis, angle); placeOnGround(mesh); @@ -185,10 +220,10 @@ class Interface extends React.Component { }; slice = async (target) => { - const { isSlicing, isLoading, settings, printers, quality, scene: { material, mesh: { matrix } } } = this.state; - const { name, mesh } = this.props; + const { isSlicing, isLoading, settings, printers, quality, mesh, scene: { material, mesh: { matrix } } } = this.state; + const { name } = this.props; - if (isSlicing) return; + if (isSlicing || isLoading) return; this.closePopover(); @@ -265,14 +300,15 @@ class Interface extends React.Component { render() { const { classes, defaultPrinter, defaultQuality, defaultMaterial, onCancel } = this.props; - const { isSlicing, progress, settings, printers, quality, material, showFullScreen, error } = this.state; + const { isSlicing, isLoading, progress, settings, printers, quality, material, showFullScreen, error } = this.state; + const disableUI = isSlicing || isLoading; const style = { ...(showFullScreen ? {} : { maxWidth: 'inherit', width: '100%', height: '100%' }) }; const settingsPanel = (
- - - - - - + + + + + +
); diff --git a/src/interface/utils.js b/src/interface/utils.js index 5feb1c1..0d81a2f 100644 --- a/src/interface/utils.js +++ b/src/interface/utils.js @@ -27,14 +27,16 @@ export function placeOnGround(mesh) { mesh.updateMatrix(); } -export function createScene(canvas, props, state) { - const { pixelRatio, mesh: { geometry } } = props; - const { settings } = state; - +export function centerGeometry(mesh) { // center geometry - geometry.computeBoundingBox(); - const center = geometry.boundingBox.getCenter(); - geometry.applyMatrix(new Matrix4().makeTranslation(-center.x, -center.y, -center.z)); + mesh.geometry.computeBoundingBox(); + const center = mesh.geometry.boundingBox.getCenter(); + mesh.geometry.applyMatrix(new Matrix4().makeTranslation(-center.x, -center.y, -center.z)); +} + +export function createScene(canvas, props, state) { + const { pixelRatio } = props; + const { settings } = state; const scene = new Scene(); @@ -53,8 +55,7 @@ export function createScene(canvas, props, state) { scene.add(light); const material = new MeshPhongMaterial({ color: 0x2194ce, side: DoubleSide, specular: 0xc5c5c5, shininess: 5 }); - const mesh = new Mesh(geometry, material); - placeOnGround(mesh); + const mesh = new Mesh(new THREE.Geometry(), material); scene.add(mesh); const box = new BoxHelper(new Mesh(new BoxGeometry(1, 1, 1).applyMatrix(new Matrix4().makeTranslation(0, 0.5, 0))), 0x72bcd4);