make settings editable

This commit is contained in:
casperlamboo 2017-11-13 02:09:39 +01:00
parent c1117a8ce5
commit 6cd899f32b
6 changed files with 251 additions and 63 deletions

3
package-lock.json generated
View File

@ -2985,8 +2985,7 @@
"lodash": { "lodash": {
"version": "4.17.4", "version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
"dev": true
}, },
"lodash.merge": { "lodash.merge": {
"version": "4.6.0", "version": "4.6.0",

View File

@ -15,6 +15,7 @@
}, },
"dependencies": { "dependencies": {
"@doodle3d/clipper-js": "^1.0.7", "@doodle3d/clipper-js": "^1.0.7",
"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.1.0",

View File

@ -0,0 +1,60 @@
import React from 'react';
import PropTypes from 'proptypes';
import _ from 'lodash';
import injectSheet from 'react-jss';
import MaterialUISelectField from 'material-ui/SelectField'
import MaterialUICheckbox from 'material-ui/Checkbox';
import MaterialUITextField from 'material-ui/TextField';
import { grey100, grey300, grey500 } from 'material-ui/styles/colors';
const styles = {
fieldSet: {
border: 'none',
backgroundColor: grey100,
marginTop: '20px',
'& legend': {
border: `1px solid ${grey300}`,
backgroundColor: 'white',
padding: '3px 13px',
color: grey500
}
}
};
export const SettingsGroup = injectSheet(styles)(({ name, classes, children }) => (
<fieldset className={classes.fieldSet}>
<legend>{name}</legend>
{children}
</fieldset>
));
SettingsGroup.propTypes = {
classes: PropTypes.objectOf(PropTypes.string),
name: PropTypes.string.isRequired,
children: PropTypes.node
};
export const SelectField = (props, context) => (
<MaterialUISelectField
{ ...props }
value={context.state[props.name]}
onChange={(event, index, value) => props.onChange(props.name, value)}
/>
);
SelectField.contextTypes = { state: PropTypes.object };
export const TextField = (props, context) => (
<MaterialUITextField
{ ...props }
value={_.get(context.state, props.name)}
onChange={(event, value) => props.onChange(props.name, value)}
/>
);
TextField.contextTypes = { state: PropTypes.object };
export const Checkbox = (props, context) => (
<MaterialUICheckbox
{ ...props }
checked={_.get(context.state, props.name)}
onCheck={(event, value) => props.onChange(props.name, value)}
/>
);
Checkbox.contextTypes = { state: PropTypes.object };

175
src/interface/Settings.js Normal file
View File

@ -0,0 +1,175 @@
import React from 'react';
import PropTypes from 'proptypes';
import _ from 'lodash';
import baseSettings from '../settings/default.yml';
import printerSettings from '../settings/printer.yml';
import materialSettings from '../settings/material.yml';
import qualitySettings from '../settings/quality.yml';
import { Tabs, Tab } from 'material-ui/Tabs';
import MenuItem from 'material-ui/MenuItem';
import injectSheet from 'react-jss';
import { SettingsGroup, SelectField, TextField, Checkbox } from './FormComponents.js';
import { grey500 } from 'material-ui/styles/colors';
const styles = {
textFieldRow: {
display: 'flex'
},
content: {
maxHeight: '300px',
overflowY: 'scroll'
}
};
const DEFAULT_PRINTER = 'ultimaker2';
const DEFAULT_MATERIAL = 'pla';
const DEFAULT_QUALITY = 'medium';
const DEFAULT_SETTINGS = {
'printer': printerSettings,
'quality': qualitySettings,
'material': materialSettings
};
class Settings extends React.Component {
constructor(props) {
super(props);
this.state = {
settings: {
...baseSettings,
...printerSettings[DEFAULT_PRINTER],
...qualitySettings[DEFAULT_MATERIAL],
...materialSettings[DEFAULT_MATERIAL],
title: null
},
printer: DEFAULT_PRINTER,
quality: DEFAULT_QUALITY,
material: DEFAULT_MATERIAL
};
}
changeSettings = (fieldName, value) => {
switch (fieldName) {
case 'printer':
case 'quality':
case 'material':
this.setState({
[fieldName]: value,
settings: {
...this.state.settings,
...DEFAULT_SETTINGS[fieldName][value],
title: null
}
});
break;
default:
this.setState(_.set(this.state, fieldName, value));
break;
}
};
getChildContext() {
return { state: this.state };
}
render() {
const { classes } = this.props;
return (
<Tabs>
<Tab label="basic settings">
<div className={classes.content}>
<SelectField name="printer" floatingLabelText="Printer" fullWidth onChange={this.changeSettings}>
{Object.entries(printerSettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
<SelectField name="quality" floatingLabelText="Quality" fullWidth onChange={this.changeSettings}>
{Object.entries(qualitySettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
<SelectField name="material" floatingLabelText="Material" fullWidth onChange={this.changeSettings}>
{Object.entries(materialSettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
</div>
</Tab>
<Tab label="advanced settings">
<div className={classes.content}>
<SettingsGroup name="Printer dimensions">
<div className={classes.textFieldRow}>
<TextField name="settings.dimensions.x" fullWidth floatingLabelText="X" type="number" onChange={this.changeSettings} />
<TextField name="settings.dimensions.y" fullWidth floatingLabelText="Y" type="number" onChange={this.changeSettings} />
<TextField name="settings.dimensions.z" fullWidth floatingLabelText="Z" type="number" onChange={this.changeSettings} />
</div>
</SettingsGroup>
<SettingsGroup name="Nozzle">
<TextField name="settings.nozzleDiameter" fullWidth floatingLabelText="Diameter" type="number" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="Bed">
<TextField name="settings.bedTemperature" fullWidth floatingLabelText="Temperature" type="number" onChange={this.changeSettings} />
<Checkbox name="settings.heatedBed" label="Heated" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="Material">
<TextField name="settings.filamentThickness" fullWidth floatingLabelText="Thickness" type="number" onChange={this.changeSettings} />
<TextField name="settings.temperature" fullWidth floatingLabelText="Temperature" type="number" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="Thickness">
<TextField name="settings.thickness.top" fullWidth floatingLabelText="top" type="number" onChange={this.changeSettings} />
<TextField name="settings.thickness.bottom" fullWidth floatingLabelText="bottom" type="number" onChange={this.changeSettings} />
<TextField name="settings.thickness.shell" fullWidth floatingLabelText="shell" type="number" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="Retraction">
<Checkbox name="settings.retraction.enabled" label="Enabled" onChange={this.changeSettings} />
<TextField name="settings.retraction.amount" fullWidth floatingLabelText="Amount" type="number" onChange={this.changeSettings} />
<TextField name="settings.retraction.speed" fullWidth floatingLabelText="Speed" type="number" onChange={this.changeSettings} />
<TextField name="settings.retraction.minDistance" fullWidth floatingLabelText="Min distance" type="number" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="Travel">
<TextField name="settings.travel.speed" fullWidth floatingLabelText="Speed" type="number" onChange={this.changeSettings} />
<Checkbox name="settings.combing" label="Combing" />
</SettingsGroup>
<SettingsGroup name="Inner shell">
<TextField name="settings.innerShell.speed" fullWidth floatingLabelText="Speed" type="number" onChange={this.changeSettings} />
<TextField name="settings.innerShell.flowRate" fullWidth floatingLabelText="Flow rate" type="number" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="Outer shell">
<TextField name="settings.outerShell.speed" fullWidth floatingLabelText="Speed" type="number" onChange={this.changeSettings} />
<TextField name="settings.outerShell.flowRate" fullWidth floatingLabelText="Flow rate" type="number" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="Inner infill">
<TextField name="settings.innerInfill.gridSize" fullWidth floatingLabelText="Grid size" type="number" onChange={this.changeSettings} />
<TextField name="settings.innerInfill.speed" fullWidth floatingLabelText="Speed" type="number" onChange={this.changeSettings} />
<TextField name="settings.innerInfill.flowRate" fullWidth floatingLabelText="Flow rate" type="number" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="Outer infill">
<TextField name="settings.outerInfill.speed" fullWidth floatingLabelText="Speed" type="number" onChange={this.changeSettings} />
<TextField name="settings.outerInfill.flowRate" fullWidth floatingLabelText="Flow rate" type="number" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="Brim">
<TextField name="settings.brim.offset" fullWidth floatingLabelText="Offset" type="number" onChange={this.changeSettings} />
<TextField name="settings.brim.speed" fullWidth floatingLabelText="Speed" type="number" onChange={this.changeSettings} />
<TextField name="settings.brim.flowRate" fullWidth floatingLabelText="Flow rate" type="number" onChange={this.changeSettings} />
</SettingsGroup>
<SettingsGroup name="First layer">
<TextField name="settings.firstLayer.speed" fullWidth floatingLabelText="Speed" type="number" onChange={this.changeSettings} />
<TextField name="settings.firstLayer.flowRate" fullWidth floatingLabelText="Flow rate" type="number" onChange={this.changeSettings} />
</SettingsGroup>
</div>
</Tab>
</Tabs>
);
}
}
Settings.childContextTypes = {
state: PropTypes.object
};
Settings.propTypes = {
classes: PropTypes.objectOf(PropTypes.string),
onChange: PropTypes.func
};
export default injectSheet(styles)(Settings);

View File

@ -1,20 +1,15 @@
import React from 'react'; import React from 'react';
import * as THREE from 'three'; import * as THREE from 'three';
import { placeOnGround, createScene, createGcodeGeometry } from './utils.js';
import baseSettings from '../settings/default.yml';
import printerSettings from '../settings/printer.yml';
import materialSettings from '../settings/material.yml';
import qualitySettings from '../settings/quality.yml';
import PropTypes from 'proptypes'; import PropTypes from 'proptypes';
import { placeOnGround, createScene, createGcodeGeometry } from './utils.js';
import injectSheet from 'react-jss'; import injectSheet from 'react-jss';
import { sliceGeometry } from '../slicer.js'; import { sliceGeometry } from '../slicer.js';
import RaisedButton from 'material-ui/RaisedButton'; import RaisedButton from 'material-ui/RaisedButton';
import Paper from 'material-ui/Paper'; import Paper from 'material-ui/Paper';
import Slider from 'material-ui/Slider'; import Slider from 'material-ui/Slider';
import { Tabs, Tab } from 'material-ui/Tabs';
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem';
import { grey50 } from 'material-ui/styles/colors'; import { grey50 } from 'material-ui/styles/colors';
import Settings from './Settings.js';
import baseSettings from '../settings/default.yml';
const styles = { const styles = {
container: { container: {
@ -59,12 +54,10 @@ class Interface extends React.Component {
this.state = { this.state = {
controlMode: 'translate', controlMode: 'translate',
isSlicing: false, isSlicing: false,
sliced: false, sliced: false
printer: props.defaultPrinter
}; };
} }
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);
@ -97,14 +90,9 @@ class Interface extends React.Component {
}; };
slice = async () => { slice = async () => {
const { mesh, render, scene, control, printer } = this.state; const { mesh, render, scene, control } = this.state;
const settings = {
...baseSettings,
...materialSettings.pla,
...printerSettings[printer]
};
const { dimensions } = settings; const { dimensions } = baseSettings;
const centerX = dimensions.x / 2; const centerX = dimensions.x / 2;
const centerY = dimensions.y / 2; const centerY = dimensions.y / 2;
@ -114,7 +102,7 @@ class Interface extends React.Component {
this.setState({ isSlicing: true, progress: { actions: [], percentage: 0 } }); this.setState({ isSlicing: true, progress: { actions: [], percentage: 0 } });
const matrix = new THREE.Matrix4().makeTranslation(centerY, 0, centerX).multiply(mesh.matrix); const matrix = new THREE.Matrix4().makeTranslation(centerY, 0, centerX).multiply(mesh.matrix);
const gcode = await sliceGeometry(settings, geometry, matrix, false, true, ({ progress }) => { const gcode = await sliceGeometry(baseSettings, geometry, matrix, false, true, ({ progress }) => {
this.setState({ progress: { this.setState({ progress: {
actions: [...this.state.progress.actions, progress.action], actions: [...this.state.progress.actions, progress.action],
percentage: progress.done / progress.total percentage: progress.done / progress.total
@ -155,7 +143,7 @@ class Interface extends React.Component {
render() { render() {
const { width, height, classes, onCompleteActions, printers, materials, quality } = this.props; const { width, height, classes, onCompleteActions, printers, materials, quality } = this.props;
const { sliced, isSlicing, progress, gcode, controlMode, printer } = this.state; const { sliced, isSlicing, progress, gcode, controlMode } = this.state;
return ( return (
<div style={{ width, height }} className={classes.container}> <div style={{ width, height }} className={classes.container}>
@ -178,31 +166,7 @@ class Interface extends React.Component {
/> />
</div>} </div>}
{!sliced && <Paper className={classes.sliceBar}> {!sliced && <Paper className={classes.sliceBar}>
<Tabs> <Settings printers={printers} />
<Tab label="basic settings">
<div>
<SelectField name="printer" value={printer} floatingLabelText="Printer" fullWidth>
{Object.entries(printers).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
<SelectField value="medium" floatingLabelText="Quality" fullWidth>
{Object.entries(quality).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
<SelectField value="pla" floatingLabelText="Material" fullWidth>
{Object.entries(materials).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
</div>
</Tab>
<Tab label="advanced settings">
<div>
</div>
</Tab>
</Tabs>
<RaisedButton className={classes.button} fullWidth disabled={isSlicing} onTouchTap={this.slice} primary label="slice" /> <RaisedButton className={classes.button} fullWidth disabled={isSlicing} onTouchTap={this.slice} primary label="slice" />
</Paper>} </Paper>}
{sliced && <Paper className={classes.sliceBar}> {sliced && <Paper className={classes.sliceBar}>
@ -223,13 +187,7 @@ class Interface extends React.Component {
} }
Interface.defaultProps = { Interface.defaultProps = {
width: 720, width: 720,
height: 480, height: 480
printers: printerSettings,
defaultPrinter: 'ultimaker2',
quality: qualitySettings,
defaultQuality: 'medium',
materials: materialSettings,
defaultMaterial: 'pla',
}; };
Interface.propTypes = { Interface.propTypes = {
geometry(props, propName) { geometry(props, propName) {
@ -240,12 +198,6 @@ Interface.propTypes = {
width: PropTypes.number.isRequired, width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired, height: PropTypes.number.isRequired,
classes: PropTypes.objectOf(PropTypes.string), classes: PropTypes.objectOf(PropTypes.string),
printers: PropTypes.object.isRequired,
defaultPrinter: PropTypes.string.isRequired,
quality: PropTypes.object.isRequired,
defaultQuality: PropTypes.string.isRequired,
materials: PropTypes.object.isRequired,
defaultMaterial: PropTypes.string.isRequired,
onCompleteActions: PropTypes.arrayOf(PropTypes.shape({ title: PropTypes.string, callback: PropTypes.func })).isRequired, onCompleteActions: PropTypes.arrayOf(PropTypes.shape({ title: PropTypes.string, callback: PropTypes.func })).isRequired,
}; };

View File

@ -1,6 +1,7 @@
import * as THREE from 'three'; import * as THREE from 'three';
import 'three/examples/js/controls/EditorControls'; import 'three/examples/js/controls/EditorControls';
import 'three/examples/js/controls/TransformControls'; import 'three/examples/js/controls/TransformControls';
import printerSettings from '../settings/printer.yml';
export function placeOnGround(mesh) { export function placeOnGround(mesh) {
const boundingBox = new THREE.Box3().setFromObject(mesh); const boundingBox = new THREE.Box3().setFromObject(mesh);
@ -10,8 +11,8 @@ export function placeOnGround(mesh) {
} }
export function createScene(canvas, props, state) { export function createScene(canvas, props, state) {
const { width, height, printers, geometry } = props; const { width, height, geometry } = props;
const { controlMode, printer } = state; const { controlMode } = state;
// center geometry // center geometry
geometry.computeBoundingBox(); geometry.computeBoundingBox();
@ -68,7 +69,7 @@ export function createScene(canvas, props, state) {
box.material.color.setHex(0x72bcd4); box.material.color.setHex(0x72bcd4);
scene.add(box); scene.add(box);
const { dimensions } = printers[printer]; const { dimensions } = printerSettings['ultimaker2'];
box.scale.set(dimensions.y, dimensions.z, dimensions.x); box.scale.set(dimensions.y, dimensions.z, dimensions.x);
render(); render();