mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 16:33:15 +01:00
wizard react support
This commit is contained in:
parent
ea5a3ae93e
commit
ceb9b89616
36 changed files with 580 additions and 262 deletions
|
|
@ -105,6 +105,10 @@ export default class Bus {
|
||||||
this.state[forToken] = initValue;
|
this.state[forToken] = initValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disableState(forToken) {
|
||||||
|
this.keepStateFor.delete(forToken);
|
||||||
|
delete this.state[forToken];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createToken(...fqn) {
|
export function createToken(...fqn) {
|
||||||
|
|
|
||||||
25
modules/gems/camelCaseSplit.js
Normal file
25
modules/gems/camelCaseSplit.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
export default function camelCaseSplit(str) {
|
||||||
|
function isUpperCase(str) {
|
||||||
|
return str.toUpperCase() === str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const words = [];
|
||||||
|
let word = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const c = str.charAt(i);
|
||||||
|
if (c === '_' || c === '-') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const dot = c === '.';
|
||||||
|
if ((dot || isUpperCase(c)) && word.length !== 0) {
|
||||||
|
words.push(word);
|
||||||
|
word = '';
|
||||||
|
}
|
||||||
|
if (!dot) word += c;
|
||||||
|
}
|
||||||
|
if (word.length !== 0){
|
||||||
|
words.push(word);
|
||||||
|
}
|
||||||
|
return words;
|
||||||
|
}
|
||||||
23
modules/gems/shallowEqual.js
Normal file
23
modules/gems/shallowEqual.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
export default function shallowEqual(objA, objB) {
|
||||||
|
if (objA === objB) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let aKeys = Object.keys(objA);
|
||||||
|
let bKeys = Object.keys(objB);
|
||||||
|
let len = aKeys.length;
|
||||||
|
|
||||||
|
if (bKeys.length !== len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
let key = aKeys[i];
|
||||||
|
|
||||||
|
if (objA[key] !== objB[key]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
20
modules/scene/geoms.js
Normal file
20
modules/scene/geoms.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
export function createBoxGeometry(width, height, depth) {
|
||||||
|
return new THREE.BoxGeometry(width, height, depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMeshGeometry(triangles) {
|
||||||
|
const geometry = new THREE.Geometry();
|
||||||
|
|
||||||
|
for (let tr of triangles) {
|
||||||
|
const a = geometry.vertices.length;
|
||||||
|
const b = a + 1;
|
||||||
|
const c = a + 2;
|
||||||
|
const face = new THREE.Face3(a, b, c);
|
||||||
|
tr.forEach(v => geometry.vertices.push(v.three()));
|
||||||
|
geometry.faces.push(face);
|
||||||
|
}
|
||||||
|
geometry.mergeVertices();
|
||||||
|
geometry.computeFaceNormals();
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
9
modules/scene/objects/mesh.js
Normal file
9
modules/scene/objects/mesh.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import {createMeshGeometry} from '../geoms';
|
||||||
|
|
||||||
|
export function createMesh(geometry, material) {
|
||||||
|
return new THREE.Mesh(geometry, material);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMeshFromTriangles(triangles, material) {
|
||||||
|
return createMesh(createMeshGeometry(triangles), material);
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import DPR from 'dpr';
|
import DPR from 'dpr';
|
||||||
import './utils/threeLoader'
|
import './utils/threeLoader';
|
||||||
|
import './utils/vectorThreeEnhancement';
|
||||||
|
|
||||||
export default class SceneSetUp {
|
export default class SceneSetUp {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@ export default class Window extends React.Component {
|
||||||
width: this.state.width,
|
width: this.state.width,
|
||||||
height: this.state.height,
|
height: this.state.height,
|
||||||
left: this.state.left,
|
left: this.state.left,
|
||||||
top: this.state.top
|
top: this.state.top,
|
||||||
|
zIndex: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import React from 'react';
|
||||||
|
|
||||||
import ls from './Button.less'
|
import ls from './Button.less'
|
||||||
|
|
||||||
export default function Button({text, type}) {
|
export default function Button({text, type, onClick}) {
|
||||||
|
|
||||||
return <button className={ls[type]}>{text}</button>
|
return <button onClick={onClick} className={ls[type]}>{text}</button>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export default class InputControl extends React.Component {
|
||||||
let {type, inputRef, ...props} = this.props;
|
let {type, inputRef, ...props} = this.props;
|
||||||
|
|
||||||
return <div className={ls[type]}>
|
return <div className={ls[type]}>
|
||||||
<input type='text' ref={inputRef} {...props} spellcheck='false' />
|
<input type='text' ref={inputRef} {...props} spellCheck='false' />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,7 @@ export default class NumberControl extends React.Component {
|
||||||
|
|
||||||
onWheel = (e) => {
|
onWheel = (e) => {
|
||||||
let {baseStep, round, min, max, onChange, accelerator} = this.props;
|
let {baseStep, round, min, max, onChange, accelerator} = this.props;
|
||||||
let delta = 0;
|
let delta = e.deltaY;
|
||||||
if ( e.wheelDelta ) { // WebKit / Opera / Explorer 9
|
|
||||||
delta = e.wheelDelta;
|
|
||||||
} else if ( e.detail ) { // Firefox
|
|
||||||
delta = - e.detail;
|
|
||||||
}
|
|
||||||
let val = e.target.value;
|
let val = e.target.value;
|
||||||
if (!val) val = 0;
|
if (!val) val = 0;
|
||||||
let step = baseStep * (e.shiftKey ? accelerator : 1);
|
let step = baseStep * (e.shiftKey ? accelerator : 1);
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,3 @@ export default class TextControl extends React.Component {
|
||||||
onChange={e => onChange(e.target.value)} />
|
onChange={e => onChange(e.target.value)} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextControl.propTypes = {
|
|
||||||
baseStep: PropTypes.number,
|
|
||||||
round: PropTypes.number,
|
|
||||||
min: PropTypes.number,
|
|
||||||
max: PropTypes.number,
|
|
||||||
accelerator: PropTypes.number,
|
|
||||||
initValue: PropTypes.number.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
TextControl.defaultProps = {
|
|
||||||
baseStep: 1,
|
|
||||||
round: 0,
|
|
||||||
accelerator: 100
|
|
||||||
};
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import shallowEqual from "../gems/shallowEqual";
|
||||||
|
|
||||||
export default function connect(tokens, WrappedComponent, staticProps, mapper, dispatchMapper) {
|
export default function connect(WrappedComponent, tokens, {staticProps, mapProps, mapActions}) {
|
||||||
|
|
||||||
if (!Array.isArray(tokens)) {
|
if (!Array.isArray(tokens)) {
|
||||||
tokens = [tokens];
|
tokens = [tokens];
|
||||||
}
|
}
|
||||||
|
|
||||||
mapper = createMapper(mapper);
|
mapProps = createMapper(mapProps);
|
||||||
|
|
||||||
dispatchMapper = dispatchMapper || function(dispatch) {
|
mapActions = mapActions || function(dispatch) {
|
||||||
return dispatch;
|
return dispatch;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -19,7 +20,7 @@ export default function connect(tokens, WrappedComponent, staticProps, mapper, d
|
||||||
super();
|
super();
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
this.stateProps = {};
|
this.stateProps = {};
|
||||||
this.dispatchProps = dispatchMapper(this.dispatch);
|
this.dispatchProps = mapActions(this.dispatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
|
@ -37,12 +38,17 @@ export default function connect(tokens, WrappedComponent, staticProps, mapper, d
|
||||||
}
|
}
|
||||||
|
|
||||||
setExternalState = (state) => {
|
setExternalState = (state) => {
|
||||||
this.stateProps = mapper(state);
|
this.stateProps = mapProps(state);
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
return !shallowEqual(this.props, nextProps);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
dispatch = (event, data) => {
|
dispatch = (event, data) => {
|
||||||
this.context.bus.dispatch(event, data);
|
this.context.bus.dispatch(event, data);
|
||||||
};
|
};
|
||||||
|
|
@ -51,6 +57,9 @@ export default function connect(tokens, WrappedComponent, staticProps, mapper, d
|
||||||
return <WrappedComponent {...this.stateProps} {...this.dispatchProps} {...staticProps} />
|
return <WrappedComponent {...this.stateProps} {...this.dispatchProps} {...staticProps} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidCatch() {
|
||||||
|
}
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
bus: PropTypes.object
|
bus: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,6 @@ const OPERATION_ACTIONS = [
|
||||||
},
|
},
|
||||||
...requiresFaceSelection(1)
|
...requiresFaceSelection(1)
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'BOX',
|
|
||||||
appearance: {
|
|
||||||
info: 'creates new object box'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'PLANE',
|
id: 'PLANE',
|
||||||
appearance: {
|
appearance: {
|
||||||
|
|
@ -78,7 +72,7 @@ const OPERATION_ACTIONS = [
|
||||||
|
|
||||||
function mergeInfo(action) {
|
function mergeInfo(action) {
|
||||||
const op = Operations[action.id];
|
const op = Operations[action.id];
|
||||||
action.invoke = app => app.ui.initOperation(action.id);
|
action.invoke = ({services}) => services.operation.startOperation(action.id);
|
||||||
Object.assign(action.appearance, {
|
Object.assign(action.appearance, {
|
||||||
label: op.label,
|
label: op.label,
|
||||||
icon32: op.icon + '32.png',
|
icon32: op.icon + '32.png',
|
||||||
|
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
import {Wizard} from './wizard'
|
|
||||||
import {ReadSketchFromFace} from '../../sketch/sketch-reader'
|
|
||||||
import {Loop} from '../../../../brep/topo/loop'
|
|
||||||
|
|
||||||
export class PreviewWizard extends Wizard {
|
|
||||||
|
|
||||||
constructor(app, opearation, metadata, initialState) {
|
|
||||||
super(app, opearation, metadata, initialState);
|
|
||||||
this.operation = opearation;
|
|
||||||
this.previewGroup = new THREE.Object3D();
|
|
||||||
this.previewObject = null;
|
|
||||||
this.app.viewer.workGroup.add(this.previewGroup);
|
|
||||||
this.updatePreview();
|
|
||||||
app.bus.subscribe('refreshSketch', this.onSketchUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
onSketchUpdate = () => {
|
|
||||||
this.updatePreview();
|
|
||||||
};
|
|
||||||
|
|
||||||
createPreviewObject() {throw 'abstract'};
|
|
||||||
|
|
||||||
updatePreview() {
|
|
||||||
this.destroyPreviewObject();
|
|
||||||
this.previewObject = this.createPreviewObject(this.app, this.readFormFields());
|
|
||||||
if (this.previewObject !== null) {
|
|
||||||
this.previewGroup.add( this.previewObject );
|
|
||||||
}
|
|
||||||
this.app.viewer.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyPreviewObject() {
|
|
||||||
if (this.previewObject !== null) {
|
|
||||||
this.previewGroup.remove( this.previewObject );
|
|
||||||
this.previewObject.geometry.dispose();
|
|
||||||
this.previewObject = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onUIChange() {
|
|
||||||
super.onUIChange();
|
|
||||||
this.updatePreview();
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this.app.bus.unSubscribe('refreshSketch', this.onSketchUpdate);
|
|
||||||
this.destroyPreviewObject();
|
|
||||||
this.app.viewer.workGroup.remove(this.previewGroup);
|
|
||||||
this.app.viewer.render();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PreviewWizard.createMesh = function(triangles) {
|
|
||||||
const geometry = new THREE.Geometry();
|
|
||||||
|
|
||||||
for (let tr of triangles) {
|
|
||||||
const a = geometry.vertices.length;
|
|
||||||
const b = a + 1;
|
|
||||||
const c = a + 2;
|
|
||||||
const face = new THREE.Face3(a, b, c);
|
|
||||||
tr.forEach(v => geometry.vertices.push(v.three()));
|
|
||||||
geometry.faces.push(face);
|
|
||||||
}
|
|
||||||
geometry.mergeVertices();
|
|
||||||
geometry.computeFaceNormals();
|
|
||||||
|
|
||||||
return new THREE.Mesh(geometry, IMAGINARY_SURFACE_MATERIAL);
|
|
||||||
};
|
|
||||||
|
|
||||||
export class SketchBasedPreviewer {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
//this.fixToCCW = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
createImpl(app, params, sketch, face) {
|
|
||||||
throw 'not implemented';
|
|
||||||
}
|
|
||||||
|
|
||||||
create(app, params) {
|
|
||||||
const face = app.findFace(params.face);
|
|
||||||
if (!face) return null;
|
|
||||||
const triangles = this.createImpl(app, params, face.sketch.fetchContours(), face);
|
|
||||||
return PreviewWizard.createMesh(triangles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SketchBasedNurbsPreviewer {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
createNurbses(app, params, sketch, face) {
|
|
||||||
throw 'not implemented';
|
|
||||||
}
|
|
||||||
|
|
||||||
createMesh(app, params) {
|
|
||||||
const face = app.findFace(params.face);
|
|
||||||
if (!face) return null;
|
|
||||||
const needSketchRead = !this.sketch || params.face != this.face;
|
|
||||||
if (needSketchRead) {
|
|
||||||
this.sketch = ReadSketchFromFace(app, face);
|
|
||||||
this.face = params.face;
|
|
||||||
}
|
|
||||||
const nurbses = this.createNurbses(app, params, this.sketch, face);
|
|
||||||
const geom = new THREE.Geometry();
|
|
||||||
|
|
||||||
for (let nurbs of nurbses) {
|
|
||||||
const off = geom.vertices.length;
|
|
||||||
const tess = nurbs.tessellate({maxDepth: 3});
|
|
||||||
const points = [];
|
|
||||||
tess.points.forEach(p => geom.vertices.push(new THREE.Vector3().fromArray(p)));
|
|
||||||
for (let faceIndices of tess.faces) {
|
|
||||||
let normales = faceIndices.map(function(x) {
|
|
||||||
var vn = tess.normals[x];
|
|
||||||
return new THREE.Vector3( vn[0], vn[1], vn[2] );
|
|
||||||
});
|
|
||||||
const face = new THREE.Face3(faceIndices[0] + off, faceIndices[1] + off, faceIndices[2] + off, normales);
|
|
||||||
geom.faces.push(face);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new THREE.Mesh(geom, IMAGINARY_SURFACE_MATERIAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IMAGINARY_SURFACE_MATERIAL = new THREE.MeshPhongMaterial({
|
|
||||||
vertexColors: THREE.FaceColors,
|
|
||||||
color: 0xFA8072,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.5,
|
|
||||||
shininess: 0,
|
|
||||||
depthWrite: false,
|
|
||||||
depthTest: false,
|
|
||||||
side : THREE.DoubleSide
|
|
||||||
});
|
|
||||||
67
web/app/cad/craft/craftPlugin.js
Normal file
67
web/app/cad/craft/craftPlugin.js
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import {BoxWizard} from "./mesh/wizards/box";
|
||||||
|
|
||||||
|
export function activate({bus, services}) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function createWizard(type, overridingHistory, initParams, face) {
|
||||||
|
let wizard = null;
|
||||||
|
if ('CUT' === type) {
|
||||||
|
wizard = new CutWizard(this.app, initParams);
|
||||||
|
} else if ('EXTRUDE' === type) {
|
||||||
|
wizard = new ExtrudeWizard(this.app, initParams);
|
||||||
|
} else if ('REVOLVE' === type) {
|
||||||
|
wizard = new RevolveWizard(this.app, face, initParams);
|
||||||
|
} else if ('PLANE' === type) {
|
||||||
|
wizard = new PlaneWizard(this.app, initParams);
|
||||||
|
} else if ('BOX' === type) {
|
||||||
|
wizard = new BoxWizard(this.app, initParams);
|
||||||
|
} else if ('SPHERE' === type) {
|
||||||
|
wizard = new SphereWizard(this.app.viewer, initParams);
|
||||||
|
} else if ('IMPORT_STL' === type) {
|
||||||
|
wizard = new ImportWizard(this.app.viewer, initParams);
|
||||||
|
} else {
|
||||||
|
console.log('unknown operation');
|
||||||
|
}
|
||||||
|
if (wizard != null) {
|
||||||
|
this.registerWizard(wizard, overridingHistory);
|
||||||
|
}
|
||||||
|
return wizard;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function startOperation(id) {
|
||||||
|
let selection = services.selection.face();
|
||||||
|
|
||||||
|
if ('CUT' === type) {
|
||||||
|
wizard = new CutWizard(this.app, initParams);
|
||||||
|
} else if ('EXTRUDE' === type) {
|
||||||
|
wizard = new ExtrudeWizard(this.app, initParams);
|
||||||
|
} else if ('REVOLVE' === type) {
|
||||||
|
wizard = new RevolveWizard(this.app, face, initParams);
|
||||||
|
} else if ('PLANE' === type) {
|
||||||
|
wizard = new PlaneWizard(this.app, initParams);
|
||||||
|
} else if ('BOX' === type) {
|
||||||
|
wizard = new BoxWizard(this.app, initParams);
|
||||||
|
} else if ('SPHERE' === type) {
|
||||||
|
wizard = new SphereWizard(this.app.viewer, initParams);
|
||||||
|
} else if ('IMPORT_STL' === type) {
|
||||||
|
wizard = new ImportWizard(this.app.viewer, initParams);
|
||||||
|
} else {
|
||||||
|
console.log('unknown operation');
|
||||||
|
}
|
||||||
|
if (wizard != null) {
|
||||||
|
this.registerWizard(wizard, overridingHistory);
|
||||||
|
}
|
||||||
|
return wizard;
|
||||||
|
|
||||||
|
return this.createWizard(op, false, undefined, selection[0]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
services.operation = {
|
||||||
|
startOperation
|
||||||
|
}
|
||||||
|
}
|
||||||
56
web/app/cad/craft/operationPlugin.js
Normal file
56
web/app/cad/craft/operationPlugin.js
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import {createToken} from 'bus';
|
||||||
|
import {TOKENS as WIZARD_TOKENS} from './wizard/wizardPlugin'
|
||||||
|
|
||||||
|
export function activate(context) {
|
||||||
|
let {bus, services} = context;
|
||||||
|
|
||||||
|
let registry = {};
|
||||||
|
|
||||||
|
function addOperation(descriptor, actions) {
|
||||||
|
let {id, label, info, icon, actionParams} = descriptor;
|
||||||
|
|
||||||
|
let opAction = {
|
||||||
|
id: id,
|
||||||
|
appearance: {
|
||||||
|
label,
|
||||||
|
info,
|
||||||
|
icon32: icon + '32.png',
|
||||||
|
icon96: icon + '96.png',
|
||||||
|
},
|
||||||
|
invoke: () => bus.dispatch(WIZARD_TOKENS.OPEN, {type: id}),
|
||||||
|
...actionParams
|
||||||
|
};
|
||||||
|
actions.push(opAction);
|
||||||
|
|
||||||
|
registry[id] = descriptor;
|
||||||
|
|
||||||
|
bus.subscribe(TOKENS.RUN, ({type, params}) => registry[type].run(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerOperations(operations) {
|
||||||
|
let actions = [];
|
||||||
|
for (let op of operations) {
|
||||||
|
addOperation(op, actions);
|
||||||
|
}
|
||||||
|
services.action.registerActions(actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(id) {
|
||||||
|
let op = registry[id];
|
||||||
|
if (!op) {
|
||||||
|
this `operation ${id} is not registered`;
|
||||||
|
}
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
services.operation = {
|
||||||
|
registerOperations,
|
||||||
|
registry,
|
||||||
|
get
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const TOKENS = {
|
||||||
|
RUN: createToken('operation', 'run'),
|
||||||
|
};
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// import {MESH_OPERATIONS} from './mesh/workbench'
|
// import {MESH_OPERATIONS} from './mesh/workbench'
|
||||||
// import {Extrude, Cut} from './brep/cut-extrude'
|
// import {Extrude, Cut} from './brep/cut-extrude'
|
||||||
// import {Revolve} from './brep/revolve'
|
// import {Revolve} from './brep/revolve'
|
||||||
// import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'
|
import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'
|
||||||
// import {PlaneSceneObject} from '../scene/wrappers/planeSceneObject'
|
// import {PlaneSceneObject} from '../scene/wrappers/planeSceneObject'
|
||||||
// import {box} from '../../brep/brep-primitives'
|
import {box} from '../../brep/brep-primitives'
|
||||||
|
|
||||||
export const CUT = {
|
export const CUT = {
|
||||||
icon: 'img/cad/cut',
|
icon: 'img/cad/cut',
|
||||||
|
|
|
||||||
28
web/app/cad/craft/primitives/boxOperation.js
Normal file
28
web/app/cad/craft/primitives/boxOperation.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import {box} from '../../../brep/brep-primitives'
|
||||||
|
import {BREPSceneSolid} from '../../scene/wrappers/brepSceneObject';
|
||||||
|
import {createPreviewer} from "../../preview/scenePreviewer";
|
||||||
|
import {createBoxGeometry} from "../../../../../modules/scene/geoms";
|
||||||
|
|
||||||
|
const METADATA = [
|
||||||
|
['width' , 'number', 500, {min: 0}],
|
||||||
|
['height' , 'number', 500, {min: 0}],
|
||||||
|
['depth' , 'number', 500, {min: 0}]
|
||||||
|
];
|
||||||
|
|
||||||
|
function createBox(solids, {width, height, depth}) {
|
||||||
|
return {
|
||||||
|
outdated: [],
|
||||||
|
created: [new BREPSceneSolid(box(width, height, depth))]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
id: 'BOX',
|
||||||
|
metadata: METADATA,
|
||||||
|
label: 'Box',
|
||||||
|
info: 'creates new object box',
|
||||||
|
paramsInfo: ({width, height, depth}) => `(${width}, ${height}, ${depth})`,
|
||||||
|
previewer: createPreviewer(({width, height, depth}) => createBoxGeometry(width, height, depth)),
|
||||||
|
run: createBox
|
||||||
|
};
|
||||||
|
|
||||||
26
web/app/cad/craft/wizard/wizardPlugin.js
Normal file
26
web/app/cad/craft/wizard/wizardPlugin.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import {createToken} from 'bus';
|
||||||
|
|
||||||
|
export function activate({bus, services}) {
|
||||||
|
|
||||||
|
bus.enableState(TOKENS.WIZARDS, []);
|
||||||
|
bus.subscribe(TOKENS.OPEN, ({type, initialState, overridingHistory}) => {
|
||||||
|
|
||||||
|
let wizard = {
|
||||||
|
type,
|
||||||
|
initialState,
|
||||||
|
overridingHistory,
|
||||||
|
};
|
||||||
|
|
||||||
|
bus.updateState(TOKENS.WIZARDS, opened => [...opened, wizard])
|
||||||
|
});
|
||||||
|
|
||||||
|
bus.subscribe(TOKENS.CLOSE, wizard => {
|
||||||
|
bus.updateState(TOKENS.WIZARDS, opened => opened.filter(w => w === wizard));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TOKENS = {
|
||||||
|
WIZARDS: createToken('wizards'),
|
||||||
|
OPEN: createToken('wizards', 'open'),
|
||||||
|
CLOSE: createToken('wizards', 'close'),
|
||||||
|
};
|
||||||
|
|
@ -4,7 +4,7 @@ import ls from './ActionInfo.less';
|
||||||
import AuxWidget from '../../../../../modules/ui/components/AuxWidget';
|
import AuxWidget from '../../../../../modules/ui/components/AuxWidget';
|
||||||
import connect from '../../../../../modules/ui/connect';
|
import connect from '../../../../../modules/ui/connect';
|
||||||
import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
|
import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin';
|
||||||
import {TOKENS as KeyboardTokens} from "../../keyboard/keyboardPlugin";
|
import {TOKENS as KeyboardTokens} from '../../keyboard/keyboardPlugin';
|
||||||
|
|
||||||
function ActionInfo({actionId, x, y, info, hint, hotKey}) {
|
function ActionInfo({actionId, x, y, info, hint, hotKey}) {
|
||||||
let visible = !!actionId;
|
let visible = !!actionId;
|
||||||
|
|
@ -19,6 +19,7 @@ function ActionInfo({actionId, x, y, info, hint, hotKey}) {
|
||||||
</AuxWidget>;
|
</AuxWidget>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect([ACTION_TOKENS.HINT, KeyboardTokens.KEYMAP], ActionInfo, undefined,
|
export default connect(ActionInfo, [ACTION_TOKENS.HINT, KeyboardTokens.KEYMAP], {
|
||||||
([ hintInfo, keymap ]) => (Object.assign({hotKey: hintInfo && keymap[hintInfo.actionId]}, hintInfo)) );
|
mapProps: ([ hintInfo, keymap ]) => (Object.assign({hotKey: hintInfo && keymap[hintInfo.actionId]}, hintInfo))
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,13 @@ export default function PlugableControlBar() {
|
||||||
function ButtonGroup({actions}) {
|
function ButtonGroup({actions}) {
|
||||||
return actions.map(actionRef => {
|
return actions.map(actionRef => {
|
||||||
let [id, overrides] = toIdAndOverrides(actionRef);
|
let [id, overrides] = toIdAndOverrides(actionRef);
|
||||||
let Comp = connect([ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
|
let Comp = connect(ActionButton,
|
||||||
ActionButton, {actionId: id},
|
[ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
|
||||||
([appearance, state]) => Object.assign({}, appearance, state, overrides),
|
{
|
||||||
mapActionBehavior(id)
|
staticProps: {actionId: id},
|
||||||
|
mapProps: ([appearance, state]) => Object.assign({}, appearance, state, overrides),
|
||||||
|
mapActions: mapActionBehavior(id)
|
||||||
|
}
|
||||||
);
|
);
|
||||||
return <Comp key={id}/>;
|
return <Comp key={id}/>;
|
||||||
});
|
});
|
||||||
|
|
@ -46,8 +49,12 @@ class ActionButton extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const LeftGroup = connect(UI_TOKENS.CONTROL_BAR_LEFT, ButtonGroup, undefined, ([actions]) => ({actions}));
|
const BUTTON_CONNECTOR = {
|
||||||
const RightGroup = connect(UI_TOKENS.CONTROL_BAR_RIGHT, ButtonGroup, undefined, ([actions]) => ({actions}));
|
mapProps: ([actions]) => ({actions})
|
||||||
|
};
|
||||||
|
|
||||||
|
const LeftGroup = connect(ButtonGroup, UI_TOKENS.CONTROL_BAR_LEFT, BUTTON_CONNECTOR);
|
||||||
|
const RightGroup = connect(ButtonGroup, UI_TOKENS.CONTROL_BAR_RIGHT, BUTTON_CONNECTOR);
|
||||||
|
|
||||||
function getMenuData(el) {
|
function getMenuData(el) {
|
||||||
//TODO: make more generic
|
//TODO: make more generic
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,11 @@ function ConfigurableToolbar({actions, small, ...props}) {
|
||||||
{actions.map(actionRef => {
|
{actions.map(actionRef => {
|
||||||
let [id, overrides] = toIdAndOverrides(actionRef);
|
let [id, overrides] = toIdAndOverrides(actionRef);
|
||||||
let Comp = connect(
|
let Comp = connect(
|
||||||
[ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)],
|
ActionButton,
|
||||||
ActionButton, {small},
|
[ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)], {
|
||||||
([appearance, state]) => Object.assign({}, appearance, state, overrides));
|
staticProps: {small},
|
||||||
|
mapProps: ([appearance, state]) => Object.assign({}, appearance, state, overrides)
|
||||||
|
});
|
||||||
return <Comp key={id}/>
|
return <Comp key={id}/>
|
||||||
})}
|
})}
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|
@ -37,7 +39,10 @@ function ActionButton({label, icon96, cssIcons, small, enabled, visible, onClick
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPlugableToolbar(configToken, small) {
|
export function createPlugableToolbar(configToken, small) {
|
||||||
return connect(configToken, ConfigurableToolbar, {small}, ([actions]) => ({actions}) );
|
return connect(ConfigurableToolbar, configToken, {
|
||||||
|
staticProps: {small},
|
||||||
|
mapProps: ([actions]) => ({actions})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlugableToolbarLeft = createPlugableToolbar(UI_TOKENS.TOOLBAR_BAR_LEFT);
|
export const PlugableToolbarLeft = createPlugableToolbar(UI_TOKENS.TOOLBAR_BAR_LEFT);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import MenuHolder from "../menu/MenuHolder";
|
import MenuHolder from '../menu/MenuHolder';
|
||||||
import {TOKENS as MENU_TOKENS} from '../menu/menuPlugin';
|
import {TOKENS as MENU_TOKENS} from '../menu/menuPlugin';
|
||||||
|
|
||||||
import WindowSystem from 'ui/WindowSystem';
|
import WindowSystem from 'ui/WindowSystem';
|
||||||
import ActionInfo from "../actionInfo/ActionInfo";
|
import ActionInfo from '../actionInfo/ActionInfo';
|
||||||
|
|
||||||
export default class UISystem extends React.Component {
|
export default class UISystem extends React.Component {
|
||||||
|
|
||||||
|
|
@ -17,12 +17,15 @@ export default class UISystem extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
closeAllUpPopups = () => {
|
closeAllUpPopups = () => {
|
||||||
let openedMenus = this.context.bus.state[MENU_TOKENS.OPENED];
|
let openedMenus = this.context.bus.state[MENU_TOKENS.OPENED];
|
||||||
if (openedMenus && openedMenus.length !== 0) {
|
if (openedMenus && openedMenus.length !== 0) {
|
||||||
this.context.bus.dispatch(MENU_TOKENS.CLOSE_ALL);
|
this.context.bus.dispatch(MENU_TOKENS.CLOSE_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
getChildContext() {
|
getChildContext() {
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,15 @@ import ButtonGroup from "ui/components/controls/ButtonGroup";
|
||||||
import Button from "ui/components/controls/Button";
|
import Button from "ui/components/controls/Button";
|
||||||
import TextControl from './../../../../../modules/ui/components/controls/TextControl';
|
import TextControl from './../../../../../modules/ui/components/controls/TextControl';
|
||||||
import UISystem from './UISystem';
|
import UISystem from './UISystem';
|
||||||
|
import WizardManager from './wizard/WizardManager';
|
||||||
|
|
||||||
|
|
||||||
export default class View3d extends React.PureComponent {
|
export default class View3d extends React.Component {
|
||||||
|
|
||||||
|
shouldComponentUpdate() {
|
||||||
|
//we don't want the dom to be updated under any circumstances or we loose the WEB-GL container
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <UISystem className={ls.root} >
|
return <UISystem className={ls.root} >
|
||||||
|
|
@ -43,8 +49,8 @@ export default class View3d extends React.PureComponent {
|
||||||
<PlugableToolbarRight />
|
<PlugableToolbarRight />
|
||||||
</Abs>
|
</Abs>
|
||||||
<PlugableControlBar />
|
<PlugableControlBar />
|
||||||
|
<WizardManager />
|
||||||
|
|
||||||
<WindowSystem />
|
|
||||||
<Window initWidth={250} initLeft={500} title="Test">
|
<Window initWidth={250} initLeft={500} title="Test">
|
||||||
<Stack >
|
<Stack >
|
||||||
<Field>
|
<Field>
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ const DEFAULT_VIEW = {id: 'view3d', label: '3D View', Component: View3d};
|
||||||
|
|
||||||
export default class WebApplication extends React.Component {
|
export default class WebApplication extends React.Component {
|
||||||
|
|
||||||
constructor({bus}) {
|
constructor({appContext}) {
|
||||||
super();
|
super();
|
||||||
this.bus = bus;
|
this.appContext = appContext;
|
||||||
this.views = [DEFAULT_VIEW, {id: 'XXX', label: '3D View2', Component: Fragment}];
|
this.views = [DEFAULT_VIEW, {id: 'XXX', label: '3D View2', Component: Fragment}];
|
||||||
this.state = {
|
this.state = {
|
||||||
activeView: DEFAULT_VIEW
|
activeView: DEFAULT_VIEW
|
||||||
|
|
@ -49,11 +49,12 @@ export default class WebApplication extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildContext() {
|
getChildContext() {
|
||||||
return {bus: this.bus};
|
return this.appContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
static childContextTypes = {
|
static childContextTypes = {
|
||||||
bus: PropTypes.object
|
bus: PropTypes.object,
|
||||||
|
services: PropTypes.object
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
81
web/app/cad/dom/components/wizard/Wizard.jsx
Normal file
81
web/app/cad/dom/components/wizard/Wizard.jsx
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Window from 'ui/components/Window';
|
||||||
|
import Stack from 'ui/components/Stack';
|
||||||
|
import Field from 'ui/components/controls/Field';
|
||||||
|
import Label from 'ui/components/controls/Label';
|
||||||
|
import camelCaseSplit from 'gems/camelCaseSplit';
|
||||||
|
import NumberControl from 'ui/components/controls/NumberControl';
|
||||||
|
import TextControl from 'ui/components/controls/TextControl';
|
||||||
|
import Button from 'ui/components/controls/Button';
|
||||||
|
import ButtonGroup from 'ui/components/controls/ButtonGroup';
|
||||||
|
|
||||||
|
export default class Wizard extends React.Component {
|
||||||
|
|
||||||
|
constructor({initialState, metadata, previewer}) {
|
||||||
|
super();
|
||||||
|
this.params = {};
|
||||||
|
|
||||||
|
metadata.forEach(([name,, v]) => this.params[name] = v);
|
||||||
|
|
||||||
|
Object.assign(this.params, initialState);
|
||||||
|
|
||||||
|
this.preview = previewer(this.params);
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate() {
|
||||||
|
// all controls are unmanaged and they should keep their state
|
||||||
|
// if the wizard manager gets updated when a new wizard appears
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let {left, title, metadata, onOK, onCancel} = this.props;
|
||||||
|
let onClose = () => {
|
||||||
|
this.onClose();
|
||||||
|
onCancel();
|
||||||
|
};
|
||||||
|
return <Window initWidth={250} initLeft={left} title={title} onClose={onClose}>
|
||||||
|
<Stack >
|
||||||
|
{metadata.map(([name, type, , params], index) => {
|
||||||
|
return <Field key={index}>
|
||||||
|
<Label>{uiLabel(name)}</Label>
|
||||||
|
{this.controlForType(name, type, params)}
|
||||||
|
</Field>
|
||||||
|
} )}
|
||||||
|
<ButtonGroup>
|
||||||
|
<Button text='Cancel' onClick={onClose} />
|
||||||
|
<Button text='OK' type='accent' onClick={() => {
|
||||||
|
this.onClose();
|
||||||
|
onOK(this.params);
|
||||||
|
}} />
|
||||||
|
</ButtonGroup>
|
||||||
|
</Stack>
|
||||||
|
</Window>;
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
this.preview.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
controlForType(name, type, params) {
|
||||||
|
const onChange = val => {
|
||||||
|
this.params[name] = val;
|
||||||
|
this.preview.update(this.params);
|
||||||
|
};
|
||||||
|
let initValue = this.params[name];
|
||||||
|
let commonProps = {
|
||||||
|
onChange, initValue, ...params
|
||||||
|
};
|
||||||
|
if (type === 'number') {
|
||||||
|
return <NumberControl {...commonProps} />
|
||||||
|
} else {
|
||||||
|
return <TextControl {...commonProps} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function uiLabel(name) {
|
||||||
|
return camelCaseSplit(name).map(w => w.toLowerCase()).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
32
web/app/cad/dom/components/wizard/WizardManager.jsx
Normal file
32
web/app/cad/dom/components/wizard/WizardManager.jsx
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {TOKENS as WIZARD_TOKENS} from '../../../craft/wizard/wizardPlugin';
|
||||||
|
import connect from 'ui/connect';
|
||||||
|
import Wizard from "./Wizard";
|
||||||
|
|
||||||
|
function WizardManager({wizards, close}, {services}) {
|
||||||
|
return wizards.map( ({type, initialState}, wizardIndex) => {
|
||||||
|
let {metadata, previewer, run} = services.operation.get(type);
|
||||||
|
|
||||||
|
function onOK(params) {
|
||||||
|
close();
|
||||||
|
run(type, params);
|
||||||
|
}
|
||||||
|
previewer = previewer.bind(null, {services});
|
||||||
|
return <Wizard key={wizardIndex} previewer={previewer} metadata={metadata}
|
||||||
|
onOK={onOK}
|
||||||
|
onCancel={close}
|
||||||
|
initialState={initialState} title={type} left={70 + wizardIndex * 250 + 20} />
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
WizardManager.contextTypes = {
|
||||||
|
services: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(WizardManager, WIZARD_TOKENS.WIZARDS, {
|
||||||
|
mapProps: ([wizards]) => ({wizards}),
|
||||||
|
mapActions: dispatch => ({
|
||||||
|
close: wizard => dispatch(WIZARD_TOKENS.CLOSE, wizard)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
@ -10,8 +10,11 @@ import {TOKENS as KeyboardTokens} from "../../keyboard/keyboardPlugin";
|
||||||
function MenuHolder({menus}) {
|
function MenuHolder({menus}) {
|
||||||
return menus.map(({id, actions}) => {
|
return menus.map(({id, actions}) => {
|
||||||
let menuToken = MENU_TOKENS.menuState(id);
|
let menuToken = MENU_TOKENS.menuState(id);
|
||||||
return React.createElement(connect([menuToken, KeyboardTokens.KEYMAP],
|
let connectedMenu = connect(ActionMenu, [menuToken, KeyboardTokens.KEYMAP], {
|
||||||
ActionMenu, {actions}, [,keymap => ({keymap})]), {key: id});
|
staticProps: {actions},
|
||||||
|
mapProps: [,keymap => ({keymap})]
|
||||||
|
});
|
||||||
|
return React.createElement(connectedMenu, {key: id});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,13 +26,12 @@ function ActionMenu({actions, keymap, ...props}) {
|
||||||
}
|
}
|
||||||
const runToken = ACTION_TOKENS.actionRun(action);
|
const runToken = ACTION_TOKENS.actionRun(action);
|
||||||
return React.createElement(
|
return React.createElement(
|
||||||
connect([ACTION_TOKENS.actionState(action), ACTION_TOKENS.actionAppearance(action)],
|
connect(ActionMenuItem, [ACTION_TOKENS.actionState(action), ACTION_TOKENS.actionAppearance(action)], {
|
||||||
ActionMenuItem,
|
staticProps: {hotKey: keymap[action]},
|
||||||
{hotKey: keymap[action]}, undefined,
|
mapActions: dispatch => ({
|
||||||
dispatch => ({
|
|
||||||
onClick: () => dispatch(runToken)
|
onClick: () => dispatch(runToken)
|
||||||
})),
|
})
|
||||||
{key: action});
|
}), {key: action});
|
||||||
})}
|
})}
|
||||||
</Menu>;
|
</Menu>;
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +62,9 @@ function ActionMenuItem({label, cssIcons, icon32, icon96, onClick, enabled, hotK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default connect(MENU_TOKENS.MENUS, MenuHolder, undefined, ([menus]) => ({menus}));
|
export default connect(MenuHolder, MENU_TOKENS.MENUS, {
|
||||||
|
mapProps: ([menus]) => ({menus})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import WebApplication from './components/WebApplication';
|
import WebApplication from './components/WebApplication';
|
||||||
|
|
||||||
export default function startReact(bus, callback) {
|
export default function startReact(context, callback) {
|
||||||
return ReactDOM.render(
|
return ReactDOM.render(
|
||||||
<WebApplication bus={bus} />,
|
<WebApplication appContext={context} />,
|
||||||
document.getElementById('app'),
|
document.getElementById('app'),
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import * as ActionSystemPlugin from '../actions/actionSystemPlugin';
|
||||||
import * as UiEntryPointsPlugin from '../dom/uiEntryPointsPlugin';
|
import * as UiEntryPointsPlugin from '../dom/uiEntryPointsPlugin';
|
||||||
import * as MenuPlugin from '../dom/menu/menuPlugin';
|
import * as MenuPlugin from '../dom/menu/menuPlugin';
|
||||||
import * as KeyboardPlugin from '../keyboard/keyboardPlugin';
|
import * as KeyboardPlugin from '../keyboard/keyboardPlugin';
|
||||||
|
import * as WizardPlugin from '../craft/wizard/wizardPlugin';
|
||||||
|
import * as OperationPlugin from '../craft/operationPlugin';
|
||||||
|
|
||||||
import * as PartModellerPlugin from '../part/partModellerPlugin';
|
import * as PartModellerPlugin from '../part/partModellerPlugin';
|
||||||
|
|
||||||
|
|
@ -20,7 +22,9 @@ export default function startApplication(callback) {
|
||||||
ActionSystemPlugin,
|
ActionSystemPlugin,
|
||||||
MenuPlugin,
|
MenuPlugin,
|
||||||
UiEntryPointsPlugin,
|
UiEntryPointsPlugin,
|
||||||
KeyboardPlugin
|
KeyboardPlugin,
|
||||||
|
WizardPlugin,
|
||||||
|
OperationPlugin
|
||||||
];
|
];
|
||||||
|
|
||||||
let plugins = [
|
let plugins = [
|
||||||
|
|
@ -38,14 +42,14 @@ export default function startApplication(callback) {
|
||||||
|
|
||||||
activatePlugins(preUIPlugins, context);
|
activatePlugins(preUIPlugins, context);
|
||||||
|
|
||||||
startReact(context.bus, () => {
|
startReact(context, () => {
|
||||||
activatePlugins(plugins, context);
|
activatePlugins(plugins, context);
|
||||||
context.services.viewer.render();
|
context.services.viewer.render();
|
||||||
callback(context);
|
callback(context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function activatePlugins(plugins, context) {
|
export function activatePlugins(plugins, context) {
|
||||||
for (let plugin of plugins) {
|
for (let plugin of plugins) {
|
||||||
plugin.activate(context);
|
plugin.activate(context);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import '../../../modules/scene/utils/vectorThreeEnhancement'
|
|
||||||
import Bus from 'bus'
|
import Bus from 'bus'
|
||||||
import {Viewer} from './scene/viewer'
|
import {Viewer} from './scene/viewer'
|
||||||
import {UI} from './ui/ctrl'
|
import {UI} from './ui/ctrl'
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,12 @@
|
||||||
import CoreActions from '../actions/coreActions';
|
import * as UIConfigPlugin from './uiConfigPlugin';
|
||||||
import OperationActions from '../actions/operationActions';
|
import * as PartOperationsPlugin from './partOperationsPlugin';
|
||||||
import HistoryActions from '../actions/historyActions';
|
import {activatePlugins} from "../init/startApplication";
|
||||||
import {TOKENS as UI_TOKENS} from '../dom/uiEntryPointsPlugin';
|
|
||||||
import menuConfig from "./menuConfig";
|
|
||||||
|
|
||||||
export function activate({bus, services}) {
|
const PART_MODELLER_PLUGINS = [
|
||||||
|
UIConfigPlugin,
|
||||||
|
PartOperationsPlugin
|
||||||
|
];
|
||||||
|
|
||||||
services.action.registerActions(CoreActions);
|
export function activate(context) {
|
||||||
services.action.registerActions(OperationActions);
|
activatePlugins(PART_MODELLER_PLUGINS, context);
|
||||||
services.action.registerActions(HistoryActions);
|
|
||||||
|
|
||||||
services.menu.registerMenus(menuConfig);
|
|
||||||
|
|
||||||
bus.dispatch(UI_TOKENS.CONTROL_BAR_LEFT, ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'Donate', 'GitHub']);
|
|
||||||
bus.dispatch(UI_TOKENS.CONTROL_BAR_RIGHT, [
|
|
||||||
['Info', {label: null}],
|
|
||||||
['RefreshSketches', {label: null}],
|
|
||||||
['ShowSketches', {label: 'sketches'}], ['DeselectAll', {label: null}], ['ToggleCameraMode', {label: null}]
|
|
||||||
]);
|
|
||||||
|
|
||||||
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT, ['PLANE', 'EditFace', 'EXTRUDE', 'CUT', 'REVOLVE']);
|
|
||||||
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT_SECONDARY, ['INTERSECTION', 'DIFFERENCE', 'UNION']);
|
|
||||||
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_RIGHT, ['Save', 'StlExport']);
|
|
||||||
}
|
}
|
||||||
7
web/app/cad/part/partOperationsPlugin.js
Normal file
7
web/app/cad/part/partOperationsPlugin.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import boxOperation from '../craft/primitives/boxOperation';
|
||||||
|
|
||||||
|
export function activate({bus, services}) {
|
||||||
|
services.operation.registerOperations([
|
||||||
|
boxOperation
|
||||||
|
])
|
||||||
|
}
|
||||||
25
web/app/cad/part/uiConfigPlugin.js
Normal file
25
web/app/cad/part/uiConfigPlugin.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import CoreActions from '../actions/coreActions';
|
||||||
|
import OperationActions from '../actions/operationActions';
|
||||||
|
import HistoryActions from '../actions/historyActions';
|
||||||
|
import {TOKENS as UI_TOKENS} from '../dom/uiEntryPointsPlugin';
|
||||||
|
import menuConfig from "./menuConfig";
|
||||||
|
|
||||||
|
export function activate({bus, services}) {
|
||||||
|
|
||||||
|
services.action.registerActions(CoreActions);
|
||||||
|
services.action.registerActions(OperationActions);
|
||||||
|
services.action.registerActions(HistoryActions);
|
||||||
|
|
||||||
|
services.menu.registerMenus(menuConfig);
|
||||||
|
|
||||||
|
bus.dispatch(UI_TOKENS.CONTROL_BAR_LEFT, ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'Donate', 'GitHub']);
|
||||||
|
bus.dispatch(UI_TOKENS.CONTROL_BAR_RIGHT, [
|
||||||
|
['Info', {label: null}],
|
||||||
|
['RefreshSketches', {label: null}],
|
||||||
|
['ShowSketches', {label: 'sketches'}], ['DeselectAll', {label: null}], ['ToggleCameraMode', {label: null}]
|
||||||
|
]);
|
||||||
|
|
||||||
|
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT, ['PLANE', 'EditFace', 'EXTRUDE', 'CUT', 'REVOLVE']);
|
||||||
|
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_LEFT_SECONDARY, ['INTERSECTION', 'DIFFERENCE', 'UNION']);
|
||||||
|
bus.dispatch(UI_TOKENS.TOOLBAR_BAR_RIGHT, ['Save', 'StlExport']);
|
||||||
|
}
|
||||||
76
web/app/cad/preview/scenePreviewer.js
Normal file
76
web/app/cad/preview/scenePreviewer.js
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
import * as SceneGraph from 'scene/sceneGraph';
|
||||||
|
import {createTransparentPhongMaterial} from 'scene/materials';
|
||||||
|
import {createMesh} from 'scene/objects/mesh';
|
||||||
|
|
||||||
|
|
||||||
|
export function createPreviewer(sceneGeometryCreator) {
|
||||||
|
|
||||||
|
return function({services}, initParams) {
|
||||||
|
const previewGroup = SceneGraph.createGroup();
|
||||||
|
SceneGraph.addToGroup(services.cadScene.workGroup, previewGroup);
|
||||||
|
|
||||||
|
let previewObject = null;
|
||||||
|
function destroyPreviewObject() {
|
||||||
|
if (previewObject === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
previewGroup.remove(previewObject);
|
||||||
|
previewObject.geometry.dispose();
|
||||||
|
previewObject = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(params) {
|
||||||
|
destroyPreviewObject();
|
||||||
|
previewObject = createMesh(sceneGeometryCreator(params), IMAGINARY_SURFACE_MATERIAL);
|
||||||
|
previewGroup.add(previewObject);
|
||||||
|
services.viewer.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispose() {
|
||||||
|
destroyPreviewObject();
|
||||||
|
SceneGraph.removeFromGroup(services.cadScene.workGroup, previewGroup);
|
||||||
|
services.viewer.render();
|
||||||
|
}
|
||||||
|
update(initParams);
|
||||||
|
|
||||||
|
return {update, dispose};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// function sketchBasedPreviewCreator(params) {
|
||||||
|
// const face = app.findFace(params.face);
|
||||||
|
// if (!face) return null;
|
||||||
|
// const triangles = this.createImpl(app, params, face.sketch.fetchContours(), face);
|
||||||
|
// return createMeshFromTriangles(triangles, IMAGINARY_SURFACE_MATERIAL);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function sketchBasedNurbsPreviewCreator(params) {
|
||||||
|
// const face = app.findFace(params.face);
|
||||||
|
// if (!face) return null;
|
||||||
|
// const needSketchRead = !this.sketch || params.face != this.face;
|
||||||
|
// if (needSketchRead) {
|
||||||
|
// this.sketch = ReadSketchFromFace(app, face);
|
||||||
|
// this.face = params.face;
|
||||||
|
// }
|
||||||
|
// const nurbses = this.createNurbses(app, params, this.sketch, face);
|
||||||
|
// const geom = new THREE.Geometry();
|
||||||
|
//
|
||||||
|
// for (let nurbs of nurbses) {
|
||||||
|
// const off = geom.vertices.length;
|
||||||
|
// const tess = nurbs.tessellate({maxDepth: 3});
|
||||||
|
// const points = [];
|
||||||
|
// tess.points.forEach(p => geom.vertices.push(new THREE.Vector3().fromArray(p)));
|
||||||
|
// for (let faceIndices of tess.faces) {
|
||||||
|
// let normales = faceIndices.map(function(x) {
|
||||||
|
// var vn = tess.normals[x];
|
||||||
|
// return new THREE.Vector3( vn[0], vn[1], vn[2] );
|
||||||
|
// });
|
||||||
|
// const face = new THREE.Face3(faceIndices[0] + off, faceIndices[1] + off, faceIndices[2] + off, normales);
|
||||||
|
// geom.faces.push(face);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return new THREE.Mesh(geom, IMAGINARY_SURFACE_MATERIAL);
|
||||||
|
// }
|
||||||
|
|
||||||
|
export const IMAGINARY_SURFACE_MATERIAL = createTransparentPhongMaterial(0xFA8072, 0.5);
|
||||||
|
|
@ -41,32 +41,6 @@ export function swap(arr, i1, i2) {
|
||||||
arr[i2] = tmp;
|
arr[i2] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function camelCaseSplit(str) {
|
|
||||||
function isUpperCase(str) {
|
|
||||||
return str.toUpperCase() == str;
|
|
||||||
}
|
|
||||||
|
|
||||||
const words = [];
|
|
||||||
let word = '';
|
|
||||||
|
|
||||||
for (let i = 0; i < str.length; i++) {
|
|
||||||
const c = str.charAt(i);
|
|
||||||
if (c == '_' || c == '-') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const dot = c === '.';
|
|
||||||
if ((dot || isUpperCase(c)) && word.length != 0) {
|
|
||||||
words.push(word);
|
|
||||||
word = '';
|
|
||||||
}
|
|
||||||
if (!dot) word += c;
|
|
||||||
}
|
|
||||||
if (word.length != 0){
|
|
||||||
words.push(word);
|
|
||||||
}
|
|
||||||
return words;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function defineIterable(obj, name, iteratorFactory) {
|
export function defineIterable(obj, name, iteratorFactory) {
|
||||||
obj[name] = {};
|
obj[name] = {};
|
||||||
obj[name][Symbol.iterator] = iteratorFactory;
|
obj[name][Symbol.iterator] = iteratorFactory;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue