implement local storage

This commit is contained in:
casperlamboo 2018-01-16 17:57:34 +01:00
parent 9d47e8dc23
commit 7b59ba1108
7 changed files with 512 additions and 629 deletions

674
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,12 +21,16 @@
"file-saver": "^1.3.3", "file-saver": "^1.3.3",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"material-ui": "^0.19.4", "material-ui": "^0.19.4",
"material-ui-icons": "^1.0.0-beta.17",
"material-ui-textfield-icon": "^0.2.2-1",
"proptypes": "^1.1.0", "proptypes": "^1.1.0",
"query-string": "^5.0.1", "query-string": "^5.0.1",
"react": "^16.0.0", "react": "^16.0.0",
"react-addons-update": "^15.6.2",
"react-dom": "^16.0.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",
"shortid": "^2.2.8",
"three": "^0.88.0" "three": "^0.88.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,2 +1,3 @@
export const PRECISION = 0.01; export const PRECISION = 0.01;
export const VERSION = '0.0.18'; export const VERSION = '0.0.18';
export const LOCAL_STORAGE_KEY = 'PRINTER_SETTINGS';

View File

@ -4,36 +4,58 @@ import _ from 'lodash';
import injectSheet from 'react-jss'; 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 { blue500, grey500 } from 'material-ui/styles/colors';
import TextFieldIcon from 'material-ui-textfield-icon';
import Clear from 'material-ui-icons/Clear';
const contextTypes = { state: PropTypes.object, onChange: PropTypes.func, disabled: PropTypes.bool }; const contextTypes = {
settings: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
disabled: PropTypes.bool.isRequired,
addPrinter: PropTypes.object.isRequired,
advancedFields: PropTypes.array.isRequired,
activePrinter: PropTypes.string
};
const propTypes = {
name: PropTypes.string.isRequired
};
export const SelectField = (props, context) => ( export const SelectField = (props, context) => (
<MaterialUISelectField <MaterialUISelectField
{...props} {...props}
disabled={context.disabled} disabled={context.disabled}
value={_.get(context.state, props.name)} value={_.get(context, props.name)}
onChange={(event, index, value) => context.onChange(props.name, value)} onChange={(event, index, value) => context.onChange(props.name, value)}
/> />
); );
SelectField.contextTypes = contextTypes; SelectField.contextTypes = contextTypes;
SelectField.propTypes = propTypes;
export const TextField = (props, context) => ( export const TextField = (props, context) => (
<MaterialUITextField <TextFieldIcon
{...props} {...props}
icon={context.advancedFields.includes(props.name) && <Clear onTouchTap={() => context.onChange(props.name, null)} />}
floatingLabelStyle={{ color: context.advancedFields.includes(props.name) ? blue500 : grey500 }}
disabled={context.disabled} disabled={context.disabled}
value={_.get(context.state, props.name)} value={_.get(context, props.name)}
onChange={(event, value) => context.onChange(props.name, value)} onChange={(event, value) => context.onChange(props.name, props.type === 'number' ? parseFloat(value) : value)}
/> />
); );
TextField.contextTypes = contextTypes; TextField.contextTypes = contextTypes;
TextField.propTypes = propTypes;
export const Checkbox = (props, context) => ( export const Checkbox = (props, context) => (
<span style={{ display: 'flex' }}>
<MaterialUICheckbox <MaterialUICheckbox
{...props} {...props}
style={{ display: 'block' }}
iconStyle={{ fill: context.advancedFields.includes(props.name) ? blue500 : grey500 }}
disabled={context.disabled} disabled={context.disabled}
checked={_.get(context.state, props.name)} checked={_.get(context, props.name)}
onCheck={(event, value) => context.onChange(props.name, value)} onCheck={(event, value) => context.onChange(props.name, value)}
/> />
{context.advancedFields.includes(props.name) && <Clear onTouchTap={() => context.onChange(props.name, null)} />}
</span>
); );
Checkbox.contextTypes = contextTypes; Checkbox.contextTypes = contextTypes;
Checkbox.propTypes = propTypes;

View File

@ -5,7 +5,17 @@ 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 { SelectField, TextField, Checkbox } from './FormComponents.js'; import { SelectField, TextField, Checkbox } from './FormComponents.js';
import { grey800, cyan500 } from 'material-ui/styles/colors'; import { grey800, cyan500, red500 } from 'material-ui/styles/colors';
import Divider from 'material-ui/Divider';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import { LOCAL_STORAGE_KEY } from '../constants.js';
import shortid from 'shortid';
import defaultSettings from '../settings/default.yml';
import printerSettings from '../settings/printer.yml';
import materialSettings from '../settings/material.yml';
import qualitySettings from '../settings/quality.yml';
import update from 'react-addons-update';
const styles = { const styles = {
textFieldRow: { textFieldRow: {
@ -19,74 +29,232 @@ const styles = {
fontWeight: 'bold', fontWeight: 'bold',
margin: '30px 0 0 0' margin: '30px 0 0 0'
} }
},
error: {
color: red500
} }
}; };
class Settings extends React.Component { const getLocalStorage = () => {
static childContextTypes = { state: PropTypes.object, onChange: PropTypes.func, disabled: PropTypes.bool }; let localStorage = window.localStorage.getItem(LOCAL_STORAGE_KEY);
static defaultProps: {
disabled: false if (!localStorage) {
localStorage = { printers: {}, active: null };
updateLocalStorage(localStorage);
} else {
localStorage = JSON.parse(localStorage);
}
return localStorage;
}; };
const updateLocalStorage = (localStorage) => {
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(localStorage));
};
class Settings extends React.Component {
static propTypes = { static propTypes = {
classes: PropTypes.objectOf(PropTypes.string), classes: PropTypes.objectOf(PropTypes.string),
onChange: PropTypes.func, 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 disabled: PropTypes.bool.isRequired
}; };
constructor(props) { static defaultProps: {
super(); disabled: false
this.state = {
settings: props.initialSettings,
printers: props.defaultPrinter,
quality: props.defaultQuality,
material: props.defaultMaterial
}; };
static childContextTypes = {
settings: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
disabled: PropTypes.bool.isRequired,
addPrinter: PropTypes.object.isRequired,
activePrinter: PropTypes.string,
advancedFields: PropTypes.array.isRequired
};
state = {
localStorage: getLocalStorage(),
addPrinter: {
open: false,
name: '',
printer: '',
error: null
}
};
componentDidMount() {
const { onChange } = this.props;
const { localStorage } = this.state;
if (localStorage.active) {
if (onChange) onChange(this.constructSettings(localStorage));
} else {
this.openAddPrinterDialog();
}
} }
changeSettings = (fieldName, value) => { changeSettings = (fieldName, value) => {
const { onChange } = this.props; const { onChange } = this.props;
const { localStorage } = this.state;
let state; let state = _.cloneDeep(this.state);
switch (fieldName) {
case 'printers': const removeAddPrinterError = () => {
case 'quality': state = update(state, { addPrinter: { error: { $set: null } } });
case 'material':
state = {
[fieldName]: value,
settings: _.merge({}, this.state.settings, this.props[fieldName][value])
}; };
switch (fieldName) {
case 'addPrinter.printer':
state = update(state, { addPrinter: { printer: { $set: value } } });
state = update(state, { addPrinter: { name: { $set: printerSettings[value].title } } });
removeAddPrinterError();
break;
case 'addPrinter.name':
state = update(state, { addPrinter: { name: { $set: value } } });
removeAddPrinterError();
break;
case 'activePrinter':
if (value !== 'add_printer') state = update(state, { localStorage: { active: { $set: value } } });
break;
case 'settings.quality':
case 'settings.material':
if (!localStorage.active) return this.openAddPrinterDialog();
state = _.set(state, `localStorage.printers[${localStorage.active}].${fieldName}`, value);
break;
case 'settings.layerHeight':
case 'settings.dimensions.x':
case 'settings.dimensions.y':
case 'settings.dimensions.z':
case 'settings.nozzleDiameter':
case 'settings.bedTemperature':
case 'settings.heatedBed':
case 'settings.filamentThickness':
case 'settings.temperature':
case 'settings.thickness.top':
case 'settings.thickness.bottom':
case 'settings.thickness.shell':
case 'settings.retraction.enabled':
case 'settings.retraction.amount':
case 'settings.retraction.speed':
case 'settings.retraction.minDistance':
case 'settings.travel.speed':
case 'settings.combing':
case 'settings.innerShell.speed':
case 'settings.innerShell.flowRate':
case 'settings.outerShell.speed':
case 'settings.outerShell.flowRate':
case 'settings.innerInfill.gridSize':
case 'settings.innerInfill.speed':
case 'settings.innerInfill.flowRate':
case 'settings.outerInfill.speed':
case 'settings.outerInfill.flowRate':
case 'settings.brim.size':
case 'settings.brim.speed':
case 'settings.brim.flowRate':
case 'settings.firstLayer.speed':
case 'settings.firstLayer.flowRate':
if (!localStorage.active) return this.openAddPrinterDialog();
if (value === null) {
const advanced = { ...state.localStorage.printers[localStorage.active].settings.advanced };
delete advanced[fieldName];
state = update(state, { localStorage: { printers: { [localStorage.active]: { settings: { advanced: { $set: advanced } } } } } });
} else {
state = _.set(state, `localStorage.printers[${localStorage.active}].settings.advanced[${JSON.stringify(fieldName)}]`, value);
}
break; break;
default: default:
state = _.set(_.cloneDeep(this.state), fieldName, value);
break; break;
} }
if (onChange) onChange(state); this.setState(state);
if (state) this.setState(state); if (localStorage.active) {
}; if (onChange) onChange(this.constructSettings(state.localStorage));
updateLocalStorage(state.localStorage);
getChildContext() { }
return { state: this.state, onChange: this.changeSettings, disabled: this.props.disabled };
} }
getChildContext() {
const { localStorage, addPrinter } = this.state;
return {
addPrinter,
activePrinter: localStorage.active,
advancedFields: localStorage.active ? Object.keys(localStorage.printers[localStorage.active].settings.advanced) : [],
settings: this.constructSettings(localStorage),
onChange: this.changeSettings,
disabled: this.props.disabled
};
}
constructSettings(localStorage) {
if (!localStorage.active) return defaultSettings;
const { printer, material, quality, advanced } = localStorage.printers[localStorage.active].settings;
let settings = {
...defaultSettings,
printer,
material,
quality
};
settings = _.merge({}, settings, printerSettings[printer]);
settings = _.merge({}, settings, qualitySettings[quality]);
settings = _.merge({}, settings, materialSettings[material]);
for (const key in advanced) {
const value = advanced[key];
settings = _.set(_.cloneDeep(settings), key.replace('settings.', ''), value);
}
return settings;
}
addPrinter = () => {
const { name, printer } = this.state.addPrinter;
if (!name || !printer) {
this.setState({ addPrinter: { ...this.state.addPrinter, error: 'Please enter a name and printer' } });
return;
}
const id = shortid.generate();
const localStorage = {
active: id,
printers: {
...this.state.localStorage.printers,
[id]: { name, settings: { printer, material: 'pla', quality: 'medium', advanced: {} } }
}
};
this.setState({ localStorage });
updateLocalStorage(localStorage);
this.closeAddPrinterDialog();
const { onChange } = this.props;
if (onChange) onChange(this.constructSettings(localStorage));
}
closeAddPrinterDialog = () => this.setAddPrinterDialog(false);
openAddPrinterDialog = () => this.setAddPrinterDialog(true);
setAddPrinterDialog = (open) => this.setState({ addPrinter: { name: '', printer: '', error: null, open } });
render() { render() {
const { classes, printers, quality, material, disabled } = this.props; const { addPrinter, localStorage } = this.state;
const { classes, disabled } = this.props;
return ( return (
<div className={classes.container}> <div className={classes.container}>
<SelectField name="printers" floatingLabelText="Printer" fullWidth> <SelectField name="activePrinter" floatingLabelText="Printer" fullWidth>
{Object.entries(printers).map(([value, { title }]) => ( {Object.entries(localStorage.printers).map(([id, { name }]) => (
<MenuItem key={value} value={value} primaryText={title} /> <MenuItem key={id} value={id} primaryText={name} />
))} ))}
<Divider />
<MenuItem onTouchTap={this.openAddPrinterDialog} value="add_printer" primaryText="Add Printer" />
</SelectField> </SelectField>
<SelectField name="material" floatingLabelText="Material" fullWidth> <SelectField name="settings.material" floatingLabelText="Material" fullWidth>
{Object.entries(material).map(([value, { title }]) => ( {Object.entries(materialSettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} /> <MenuItem key={value} value={value} primaryText={title} />
))} ))}
</SelectField> </SelectField>
@ -94,8 +262,8 @@ class Settings extends React.Component {
<Tabs inkBarStyle={{ backgroundColor: cyan500 }}> <Tabs inkBarStyle={{ backgroundColor: cyan500 }}>
<Tab buttonStyle={{ color: grey800, backgroundColor: 'white' }} label="Basic"> <Tab buttonStyle={{ color: grey800, backgroundColor: 'white' }} label="Basic">
<div> <div>
<SelectField name="quality" floatingLabelText="Quality" fullWidth> <SelectField name="settings.quality" floatingLabelText="Quality" fullWidth>
{Object.entries(quality).map(([value, { title }]) => ( {Object.entries(qualitySettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} /> <MenuItem key={value} value={value} primaryText={title} />
))} ))}
</SelectField> </SelectField>
@ -123,8 +291,6 @@ class Settings extends React.Component {
<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" />
<p>Combing</p>
<Checkbox name="settings.combing" label="Enabled" />
<p>Retraction</p> <p>Retraction</p>
<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" />
@ -156,6 +322,30 @@ class Settings extends React.Component {
</div> </div>
</Tab> </Tab>
</Tabs> </Tabs>
<Dialog
title="Add Printer"
open={addPrinter.open}
onRequestClose={this.closeAddPrinterDialog}
contentStyle={{ maxWidth: '400px' }}
actions={[
<FlatButton
label="Cancel"
onTouchTap={this.closeAddPrinterDialog}
/>,
<FlatButton
label="Add"
primary
onTouchTap={this.addPrinter}
/>
]}
>
<SelectField name="addPrinter.printer" floatingLabelText="Printer" fullWidth>
{Object.entries(printerSettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} /> ))}
</SelectField>
<TextField name="addPrinter.name" floatingLabelText="Name" fullWidth />
{addPrinter.error && <p className={classes.error}>{addPrinter.error}</p>}
</Dialog>
</div> </div>
); );
} }

View File

@ -16,10 +16,6 @@ import Menu from 'material-ui/Menu';
import MenuItem from 'material-ui/MenuItem'; import MenuItem from 'material-ui/MenuItem';
import { Tabs, Tab } from 'material-ui/Tabs'; import { Tabs, Tab } from 'material-ui/Tabs';
import Settings from './Settings.js'; import Settings from './Settings.js';
import defaultSettings from '../settings/default.yml';
import printerSettings from '../settings/printer.yml';
import materialSettings from '../settings/material.yml';
import qualitySettings from '../settings/quality.yml';
import ReactResizeDetector from 'react-resize-detector'; import ReactResizeDetector from 'react-resize-detector';
import JSONToSketchData from 'doodle3d-core/shape/JSONToSketchData'; import JSONToSketchData from 'doodle3d-core/shape/JSONToSketchData';
import createSceneData from 'doodle3d-core/d3/createSceneData.js'; import createSceneData from 'doodle3d-core/d3/createSceneData.js';
@ -91,63 +87,40 @@ class Interface extends React.Component {
PropTypes.string PropTypes.string
]).isRequired, ]).isRequired,
classes: PropTypes.objectOf(PropTypes.string), 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, pixelRatio: PropTypes.number.isRequired,
onCancel: PropTypes.func, onCancel: PropTypes.func,
name: PropTypes.string.isRequired name: PropTypes.string.isRequired
}; };
static defaultProps = { static defaultProps = {
defaultSettings: defaultSettings,
printers: printerSettings,
quality: qualitySettings,
defaultQuality: 'medium',
material: materialSettings,
defaultMaterial: 'pla',
pixelRatio: 1, pixelRatio: 1,
name: 'Doodle3D' name: 'Doodle3D'
}; };
constructor(props) { constructor(props) {
super(props); super(props);
const { defaultPrinter, defaultQuality, defaultMaterial, printers, quality, material, defaultSettings } = props;
const scene = createScene(this.props);
this.state = { this.state = {
scene,
settings: null,
showFullScreen: false, showFullScreen: false,
isSlicing: false, isSlicing: false,
isLoading: true, isLoading: true,
error: null, error: null,
printers: defaultPrinter,
quality: defaultQuality,
material: defaultMaterial,
popover: { popover: {
element: null, element: null,
open: false open: false
}, }
settings: _.merge(
{},
defaultSettings,
printers[defaultPrinter],
quality[defaultQuality],
material[defaultMaterial]
)
}; };
} }
componentDidMount() { componentDidMount() {
const { canvas } = this.refs; const { canvas } = this.refs;
const scene = createScene(canvas, this.props, this.state); const { scene } = this.state;
scene.updateCanvas(canvas);
this.setState({ scene });
const { file } = this.props; const { file } = this.props;
if (!file) { if (!file) {
throw new Error('no file provided'); throw new Error('no file provided');
} if (typeof file === 'string') { } if (typeof file === 'string') {
@ -220,22 +193,20 @@ class Interface extends React.Component {
}; };
slice = async (target) => { slice = async (target) => {
const { isSlicing, isLoading, settings, printers, quality, mesh, scene: { material, mesh: { matrix } } } = this.state; const { isSlicing, isLoading, settings, mesh, scene: { material, mesh: { matrix } } } = this.state;
const { name } = this.props; const { name } = this.props;
if (isSlicing || isLoading) return; if (isSlicing || isLoading) return;
this.closePopover(); this.closePopover();
this.setState({ isSlicing: true, progress: { action: '', percentage: 0, step: 0 }, error: null }); this.setState({ isSlicing: true, progress: { action: '', percentage: 0, step: 0 }, error: null });
const exportMesh = new Mesh(mesh.geometry, mesh.material); const exportMesh = new Mesh(mesh.geometry, mesh.material);
exportMesh.applyMatrix(matrix); exportMesh.applyMatrix(matrix);
try { try {
await slice(target, name, exportMesh, settings, printers, quality, material, progress => { const updateProgres = progress => this.setState({ progress: { ...this.state.progress, ...progress } });
this.setState({ progress: { ...this.state.progress, ...progress } }); await slice(target, name, exportMesh, settings, updateProgres);
});
} catch (error) { } catch (error) {
this.setState({ error: error.message }); this.setState({ error: error.message });
throw error; throw error;
@ -263,23 +234,6 @@ class Interface extends React.Component {
}); });
}; };
onChangeSettings = (settings) => {
this.setState(settings);
};
componentWillUpdate(nextProps, nextState) {
if (!this.state.scene) return;
const { scene: { box, render, setSize } } = this.state;
let changed = false;
if (box && nextState.settings.dimensions !== this.state.settings.dimensions) {
const { dimensions } = nextState.settings;
box.scale.set(dimensions.y, dimensions.z, dimensions.x);
box.updateMatrix();
changed = true;
}
if (changed) render();
}
componentDidUpdate() { componentDidUpdate() {
const { scene: { updateCanvas } } = this.state; const { scene: { updateCanvas } } = this.state;
const { canvas } = this.refs; const { canvas } = this.refs;
@ -298,9 +252,23 @@ class Interface extends React.Component {
this.setState({ showFullScreen: width > MAX_FULLSCREEN_WIDTH }); this.setState({ showFullScreen: width > MAX_FULLSCREEN_WIDTH });
}; };
onChangeSettings = (settings) => {
const { scene: { box, render } } = this.state;
let changed = false;
if (!this.state.settings || this.state.settings.dimensions !== settings.dimensions) {
box.scale.set(settings.dimensions.y, settings.dimensions.z, settings.dimensions.x);
box.updateMatrix();
changed = true;
}
if (changed) render();
this.setState({ settings, error: null });
};
render() { render() {
const { classes, defaultPrinter, defaultQuality, defaultMaterial, onCancel } = this.props; const { classes, onCancel } = this.props;
const { isSlicing, isLoading, progress, settings, printers, quality, material, showFullScreen, error } = this.state; const { isSlicing, isLoading, progress, showFullScreen, error } = this.state;
const disableUI = isSlicing || isLoading; const disableUI = isSlicing || isLoading;
const style = { ...(showFullScreen ? {} : { maxWidth: 'inherit', width: '100%', height: '100%' }) }; const style = { ...(showFullScreen ? {} : { maxWidth: 'inherit', width: '100%', height: '100%' }) };
@ -309,13 +277,6 @@ class Interface extends React.Component {
<div className={classes.settingsBar} style={style}> <div className={classes.settingsBar} style={style}>
<Settings <Settings
disabled={disableUI} disabled={disableUI}
printers={printerSettings}
defaultPrinter={defaultPrinter}
quality={qualitySettings}
defaultQuality={defaultQuality}
material={materialSettings}
defaultMaterial={defaultMaterial}
initialSettings={settings}
onChange={this.onChangeSettings} onChange={this.onChangeSettings}
/> />
<div className={classes.sliceActions}> <div className={classes.sliceActions}>

View File

@ -1,6 +1,7 @@
import * as THREE from 'three'; import * as THREE from 'three';
import { Box3 } from 'three/src/math/Box3.js'; import { Box3 } from 'three/src/math/Box3.js';
import { Matrix4 } from 'three/src/math/Matrix4.js'; import { Matrix4 } from 'three/src/math/Matrix4.js';
import { Vector3 } from 'three/src/math/Vector3.js';
import { Scene } from 'three/src/scenes/Scene.js'; import { Scene } from 'three/src/scenes/Scene.js';
import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js'; import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera.js';
import { AmbientLight } from 'three/src/lights/AmbientLight.js'; import { AmbientLight } from 'three/src/lights/AmbientLight.js';
@ -34,14 +35,12 @@ export function centerGeometry(mesh) {
mesh.geometry.applyMatrix(new Matrix4().makeTranslation(-center.x, -center.y, -center.z)); mesh.geometry.applyMatrix(new Matrix4().makeTranslation(-center.x, -center.y, -center.z));
} }
export function createScene(canvas, props, state) { export function createScene({ pixelRatio }) {
const { pixelRatio } = props;
const { settings } = state;
const scene = new Scene(); const scene = new Scene();
const camera = new PerspectiveCamera(50, 1, 1, 10000); const camera = new PerspectiveCamera(50, 1, 1, 10000);
camera.position.set(0, 400, 300); camera.position.set(0, 400, 300);
camera.lookAt(new Vector3(0, 0, 0));
const directionalLightA = new DirectionalLight(0xa2a2a2); const directionalLightA = new DirectionalLight(0xa2a2a2);
directionalLightA.position.set(1, 1, 1); directionalLightA.position.set(1, 1, 1);
@ -61,8 +60,10 @@ export function createScene(canvas, props, state) {
const box = new BoxHelper(new Mesh(new BoxGeometry(1, 1, 1).applyMatrix(new Matrix4().makeTranslation(0, 0.5, 0))), 0x72bcd4); const box = new BoxHelper(new Mesh(new BoxGeometry(1, 1, 1).applyMatrix(new Matrix4().makeTranslation(0, 0.5, 0))), 0x72bcd4);
scene.add(box); scene.add(box);
const { dimensions } = settings; let renderer = new WebGLRenderer({ alpha: true, antialias: true });
box.scale.set(dimensions.y, dimensions.z, dimensions.x); let editorControls = new THREE.EditorControls(camera, renderer.domElement);
box.scale.set(1., 1., 1.);
box.updateMatrix(); box.updateMatrix();
const render = () => renderer.render(scene, camera); const render = () => renderer.render(scene, camera);
@ -75,8 +76,6 @@ export function createScene(canvas, props, state) {
render(); render();
}; };
let editorControls;
let renderer;
const updateCanvas = (canvas) => { const updateCanvas = (canvas) => {
if (!renderer || renderer.domElement !== canvas) { if (!renderer || renderer.domElement !== canvas) {
if (renderer) renderer.dispose(); if (renderer) renderer.dispose();
@ -86,13 +85,11 @@ export function createScene(canvas, props, state) {
if (!editorControls || editorControls.domElement !== canvas) { if (!editorControls || editorControls.domElement !== canvas) {
if (editorControls) editorControls.dispose(); if (editorControls) editorControls.dispose();
editorControls = new THREE.EditorControls(camera, canvas); editorControls = new THREE.EditorControls(camera, canvas);
editorControls.focus(mesh);
editorControls.addEventListener('change', render); editorControls.addEventListener('change', render);
} }
render(); render();
}; };
updateCanvas(canvas);
const focus = () => editorControls.focus(mesh); const focus = () => editorControls.focus(mesh);
@ -119,8 +116,8 @@ export function fetchProgress(url, { method = 'get', headers = {}, body = {} } =
const GCODE_SERVER_URL = 'https://gcodeserver.doodle3d.com'; const GCODE_SERVER_URL = 'https://gcodeserver.doodle3d.com';
const CONNECT_URL = 'http://connect.doodle3d.com/'; const CONNECT_URL = 'http://connect.doodle3d.com/';
export async function slice(target, name, mesh, settings, printers, quality, material, updateProgress) { export async function slice(target, name, mesh, settings, updateProgress) {
if (!printers) throw new Error('Please select a printer'); if (!settings) throw new Error('please select a printer first');
let steps; let steps;
let currentStep = 0; let currentStep = 0;
@ -167,22 +164,13 @@ export async function slice(target, name, mesh, settings, printers, quality, mat
body.append(key, fields[key]); body.append(key, fields[key]);
} }
const file = ';' + JSON.stringify({ const file = `;${JSON.stringify({
name: `${name}.gcode`,
...settings, ...settings,
printer: { name: `${name}.gcode`,
type: printers, printer: { type: settings.printers, title: printerSettings[settings.printer].title },
title: printerSettings[printers].title material: { type: settings.material, title: materialSettings[settings.material].title },
}, quality: { type: settings.quality, title: qualitySettings[settings.quality].title }
material: { }).trim()}\n${gcode}`;
type: material,
title: materialSettings[material].title
},
quality: {
type: quality,
title: qualitySettings[quality].title
}
}).trim() + '\n' + gcode;
body.append('file', file); body.append('file', file);
await fetchProgress(reservation.url, { method: 'POST', body }, (progess) => { await fetchProgress(reservation.url, { method: 'POST', body }, (progess) => {
@ -195,6 +183,7 @@ export async function slice(target, name, mesh, settings, printers, quality, mat
const popup = window.open(`${CONNECT_URL}?uuid=${id}`, '_blank'); const popup = window.open(`${CONNECT_URL}?uuid=${id}`, '_blank');
if (!popup) throw new Error('popup was blocked by browser'); if (!popup) throw new Error('popup was blocked by browser');
break;
} }
default: default: