import _ from 'lodash'; import React from 'react'; import * as THREE from 'three'; import PropTypes from 'proptypes'; import { placeOnGround, createScene, createGcodeGeometry } from './utils.js'; import injectSheet from 'react-jss'; import { sliceGeometry } from '../slicer.js'; import RaisedButton from 'material-ui/RaisedButton'; import Paper from 'material-ui/Paper'; import Slider from 'material-ui/Slider'; import { grey50 } from 'material-ui/styles/colors'; import Settings from './Settings.js'; import baseSettings from '../settings/default.yml'; import printerSettings from '../settings/printer.yml'; import materialSettings from '../settings/material.yml'; import qualitySettings from '../settings/quality.yml'; const styles = { container: { position: 'relative', backgroundColor: grey50 }, canvas: { position: 'absolute', }, controlBar: { position: 'absolute', bottom: '10px', left: '10px' }, sliceBar: { position: 'absolute', top: '10px', right: '10px', width: '380px', padding: '10px 20px', }, overlay: { position: 'absolute', backgroundColor: 'rgba(0, 0, 0, 0.5)', color: 'white', top: 0, right: 0, bottom: 0, left: 0 }, sliceActions: { listStyleType: 'none' }, button: { margin: '5px 0' }, controlButton: { marginRight: '2px' } }; class Interface extends React.Component { constructor(props) { super(props); const { defaultPrinter, defaultQuality, defaultMaterial, printers, quality, material, defaultSettings } = props; this.state = { controlMode: 'translate', isSlicing: false, sliced: false, settings: _.merge( {}, defaultSettings, printers[defaultPrinter], quality[defaultQuality], material[defaultMaterial] ) }; } componentDidMount() { const { canvas } = this.refs; const scene = createScene(canvas, this.props, this.state); this.setState(scene); } resetMesh = () => { const { mesh, render } = this.state; if (mesh) { mesh.position.set(0, 0, 0); mesh.scale.set(1, 1, 1); mesh.rotation.set(0, 0, 0); mesh.updateMatrix(); placeOnGround(mesh); render(); } }; reset = () => { const { control, mesh, render, gcode, scene } = this.state; control.enabled = true; control.visible = true; mesh.visible = true; scene.remove(gcode.linePreview); gcode.linePreview.geometry.dispose(); this.setState({ sliced: false, gcode: null }); render(); }; slice = async () => { const { mesh, render, scene, control, settings } = this.state; const { dimensions } = settings; const centerX = dimensions.x / 2; const centerY = dimensions.y / 2; const geometry = mesh.geometry.clone(); mesh.updateMatrix(); this.setState({ isSlicing: true, progress: { actions: [], percentage: 0 } }); const matrix = new THREE.Matrix4().makeTranslation(centerY, 0, centerX).multiply(mesh.matrix); const gcode = await sliceGeometry(settings, geometry, matrix, false, true, ({ progress }) => { this.setState({ progress: { actions: [...this.state.progress.actions, progress.action], percentage: progress.done / progress.total } }); }); this.setState({ isSlicing: false }); // TODO // can't disable control ui still interacts with mouse input control.enabled = false; control.visible = false; mesh.visible = false; gcode.linePreview.position.x = -centerY; gcode.linePreview.position.z = -centerX; scene.add(gcode.linePreview); this.setState({ sliced: true, gcode }); render(); }; onChangeSettings = (settings) => { this.setState({ settings }); }; updateDrawRange = (event, value) => { const { gcode, render } = this.state; gcode.linePreview.geometry.setDrawRange(0, value); render(); }; componentWillUnmount() { if (this.state.editorControls) this.state.editorControls.dispose(); if (this.state.control) this.state.control.dispose(); } componentWillUpdate(nextProps, nextState) { const { control, box, render, setSize } = this.state; if (control && nextState.controlMode !== this.state.controlMode) control.setMode(nextState.controlMode); if (box && nextState.settings.dimensions !== this.state.settings.dimensions) { const { dimensions } = nextState.settings; box.scale.set(dimensions.y, dimensions.z, dimensions.x); render(); } if (setSize && nextProps.width !== this.props.width || nextProps.height !== this.props.height || nextProps.pixelRatio !== this.props.pixelRatio) { console.log('update pixel ratio'); setSize(nextProps.width, nextProps.height, nextProps.pixelRatio); } } render() { const { width, height, classes, onCompleteActions, defaultPrinter, defaultQuality, defaultMaterial } = this.props; const { sliced, isSlicing, progress, gcode, controlMode, settings } = this.state; return (
{!sliced &&
this.setState({ controlMode: 'translate' })} primary label="translate" /> this.setState({ controlMode: 'rotate' })} primary label="rotate" /> this.setState({ controlMode: 'scale' })} primary label="scale" />
} {sliced &&
} {!sliced && } {sliced && {onCompleteActions.map(({ title, callback }, i) => ( callback(gcode.gcode, settings)} primary label={title} /> ))} } {isSlicing &&

Slicing: {progress.percentage.toLocaleString(navigator.language, { style: 'percent' })}

    {progress.actions.map((action, i) =>
  • {action}
  • )}
}
); } } Interface.propTypes = { geometry(props, propName) { if (!(props[propName].isGeometry || props[propName].isBufferGeometry)) { throw new Error('invalid prop, is not geometry'); } }, width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, 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', width: 720, height: 480, pixelRatio: 1 }; export default injectSheet(styles)(Interface);