{
- ({onChange}) => this.state.value.map(data => {
+ ({onChange}) => this.props.value.map(data => {
let subContext = {
data,
onChange
diff --git a/web/app/cad/craft/wizard/components/form/SingleEntity.jsx b/web/app/cad/craft/wizard/components/form/SingleEntity.jsx
index 59cc53dd..4ecfefb2 100644
--- a/web/app/cad/craft/wizard/components/form/SingleEntity.jsx
+++ b/web/app/cad/craft/wizard/components/form/SingleEntity.jsx
@@ -1,40 +1,46 @@
import React from 'react';
-import PropTypes from 'prop-types';
-import {attachToForm, formFieldDecorator} from './Form';
import mapContext from 'ui/mapContext';
+import ls from './SingleEntity.less';
+import Label from 'ui/components/controls/Label';
+import Field from 'ui/components/controls/Field';
+import Fa from 'ui/components/Fa';
+import Button from 'ui/components/controls/Button';
+import {attachToForm} from './Form';
import {camelCaseSplitToStr} from 'gems/camelCaseSplit';
@attachToForm
-@mapContext(({streams}) => ({streams}))
+@mapContext(({streams, services}) => ({
+ streams,
+ findEntity: services.cadRegistry.findEntity
+}))
export default class SingleEntity extends React.Component {
- constructor({initValue}) {
- super();
- this.state = {
- selectedItem: initValue
- }
- }
-
- selectionChanged = selection => {
- let selectedItem = selection[0];
- if (selectedItem) {
- this.setState({selectedItem});
- this.props.onChange(selectedItem);
- }
- };
-
componentDidMount() {
- let {streams, entity} = this.props;
- this.detacher = streams.selection[entity].attach(this.selectionChanged);
+ let {streams, entity, onChange, value, findEntity} = this.props;
+ let selection$ = streams.selection[entity];
+ if (findEntity(entity, value)) {
+ selection$.next([value]);
+ }
+ this.detacher = selection$.attach(selection => onChange(selection[0]));
}
componentWillUnmount() {
this.detacher();
}
-
+
+ deselect = () => {
+ let {streams, entity} = this.props;
+ streams.selection[entity].next([]);
+ };
+
render() {
- return
- {camelCaseSplitToStr(this.props.name)}: {this.state.selectedItem}
-
;
+ let {name, label, streams, entity} = this.props;
+ let selection = streams.selection[entity].value[0];
+ return
+
+ {selection ?
+ {selection} :
+ {''}}
+ ;
}
}
diff --git a/web/app/cad/craft/wizard/components/form/SingleEntity.less b/web/app/cad/craft/wizard/components/form/SingleEntity.less
new file mode 100644
index 00000000..0a8c1207
--- /dev/null
+++ b/web/app/cad/craft/wizard/components/form/SingleEntity.less
@@ -0,0 +1,4 @@
+.emptySelection {
+ font-style: initial;
+ color: #BFBFBF;
+}
\ No newline at end of file
diff --git a/web/app/cad/craft/wizard/wizardPlugin.js b/web/app/cad/craft/wizard/wizardPlugin.js
index f66b5fe2..2c751303 100644
--- a/web/app/cad/craft/wizard/wizardPlugin.js
+++ b/web/app/cad/craft/wizard/wizardPlugin.js
@@ -1,23 +1,95 @@
-import {state} from '../../../../../modules/lstream';
+import {state} from 'lstream';
+import initializeBySchema from '../intializeBySchema';
+import {clone, EMPTY_OBJECT} from 'gems/objects';
-export function activate({streams, services}) {
+export function activate(ctx) {
- streams.wizard = state(null);
+ let {streams, services} = ctx;
+
+ streams.wizard = {};
- services.wizard = {
-
- open: ({type}) => {
+ streams.wizard.insertOperation = state(EMPTY_OBJECT);
- let wizard = {
- type
+ streams.wizard.effectiveOperation = state(EMPTY_OBJECT);
+
+ streams.wizard.insertOperation.attach(insertOperationReq => {
+ if (insertOperationReq.type) {
+ let type = insertOperationReq.type;
+ let operation = ctx.services.operation.get(type);
+ streams.wizard.effectiveOperation.value = {
+ type: operation.id,
+ initialOverrides: insertOperationReq.initialOverrides,
+ changingHistory: false
};
+ }
+ });
+
+ function gotoEditHistoryModeIfNeeded({pointer, history}) {
+ if (pointer !== history.length - 1) {
+ let {type, params} = history[pointer + 1];
+ streams.wizard.effectiveOperation.value = {
+ type,
+ params,
+ changingHistory: true
+ };
+ } else {
+ streams.wizard.effectiveOperation.value = EMPTY_OBJECT;
+ }
- streams.wizard.value = wizard;
+ }
+
+ streams.craft.modifications.attach(mod => {
+ if (streams.wizard.insertOperation.value.type) {
+ return;
+ }
+ gotoEditHistoryModeIfNeeded(mod);
+ });
+
+ streams.wizard.workingRequest = streams.wizard.effectiveOperation.map(opRequest => {
+ if (!opRequest.type) {
+ return EMPTY_OBJECT;
+ }
+ let operation = ctx.services.operation.get(opRequest.type);
+ let params;
+ if (opRequest.changingHistory) {
+ params = clone(opRequest.params)
+ } else {
+ params = initializeBySchema(operation.schema, ctx);
+ if (opRequest.initialOverrides) {
+ applyOverrides(params, opRequest.initialOverrides);
+ }
+ }
+ return {
+ type: opRequest.type,
+ params,
+ }
+ }).remember(EMPTY_OBJECT);
+
+ services.wizard = {
+
+ open: (type, initialOverrides) => {
+ streams.wizard.insertOperation.value = {
+ type,
+ initialOverrides
+ };
+ },
+
+ cancel: () => {
+ streams.wizard.insertOperation.value = EMPTY_OBJECT;
+ gotoEditHistoryModeIfNeeded(streams.craft.modifications.value);
},
- close: () => {
- streams.wizard.value = null;
+ applyWorkingRequest: () => {
+ let request = clone(streams.wizard.workingRequest.value);
+ if (streams.wizard.insertOperation.value.type) {
+ ctx.services.craft.modify(request, () => streams.wizard.insertOperation.value = EMPTY_OBJECT);
+ } else {
+ ctx.services.craft.modifyInHistoryAndStep(request, () => streams.wizard.effectiveOperation.value = EMPTY_OBJECT);
+ }
}
- }
-}
+ };
+}
+function applyOverrides(params, initialOverrides) {
+ Object.assign(params, initialOverrides);
+}
diff --git a/web/app/cad/init/startApplication.js b/web/app/cad/init/startApplication.js
index ac405098..b15a613d 100644
--- a/web/app/cad/init/startApplication.js
+++ b/web/app/cad/init/startApplication.js
@@ -2,6 +2,7 @@ import * as LifecyclePlugin from './lifecyclePlugin';
import * as AppTabsPlugin from '../dom/appTabsPlugin';
import * as DomPlugin from '../dom/domPlugin';
import * as PickControlPlugin from '../scene/controls/pickControlPlugin';
+import * as MouseEventSystemPlugin from '../scene/controls/mouseEventSystemPlugin';
import * as ScenePlugin from '../scene/scenePlugin';
import * as SelectionMarkerPlugin from '../scene/selectionMarker/selectionMarkerPlugin';
import * as ActionSystemPlugin from '../actions/actionSystemPlugin';
@@ -9,6 +10,7 @@ import * as UiEntryPointsPlugin from '../dom/uiEntryPointsPlugin';
import * as MenuPlugin from '../dom/menu/menuPlugin';
import * as KeyboardPlugin from '../keyboard/keyboardPlugin';
import * as WizardPlugin from '../craft/wizard/wizardPlugin';
+import * as PreviewPlugin from '../preview/previewPlugin';
import * as OperationPlugin from '../craft/operationPlugin';
import * as CraftEnginesPlugin from '../craft/enginesPlugin';
import * as CadRegistryPlugin from '../craft/cadRegistryPlugin';
@@ -39,10 +41,11 @@ export default function startApplication(callback) {
UiEntryPointsPlugin,
MenuPlugin,
KeyboardPlugin,
- WizardPlugin,
CraftEnginesPlugin,
OperationPlugin,
CraftPlugin,
+ WizardPlugin,
+ PreviewPlugin,
CraftUiPlugin,
CadRegistryPlugin,
tpiPlugin
@@ -51,6 +54,7 @@ export default function startApplication(callback) {
let plugins = [
DomPlugin,
ScenePlugin,
+ MouseEventSystemPlugin,
PickControlPlugin,
SelectionMarkerPlugin,
SketcherPlugin,
diff --git a/web/app/cad/model/mdatum.js b/web/app/cad/model/mdatum.js
index 821beaad..6a3d23f1 100644
--- a/web/app/cad/model/mdatum.js
+++ b/web/app/cad/model/mdatum.js
@@ -3,7 +3,7 @@ import {MObject} from './mobject';
export class MDatum extends MObject {
static TYPE = 'datum';
- static ID_COUNTER = 0;
+ static ID_COUNTER = 0; // TODO: reset the counter
constructor(csys) {
super();
diff --git a/web/app/cad/model/mface.js b/web/app/cad/model/mface.js
index 963bde71..49f52788 100644
--- a/web/app/cad/model/mface.js
+++ b/web/app/cad/model/mface.js
@@ -37,6 +37,14 @@ export class MFace extends MObject {
return this._basis;
}
+ get csys() {
+ if (!this._csys) {
+ let [x,y,z] = this.basis();
+ this._csys = new CSys(this.normal().multiply(this.depth()), x, y, z);
+ }
+ return this._csys;
+ }
+
setSketch(sketch) {
this.sketch = sketch;
this.sketchObjects = [];
diff --git a/web/app/cad/part/menuConfig.js b/web/app/cad/part/menuConfig.js
index 359304bd..6ba4f466 100644
--- a/web/app/cad/part/menuConfig.js
+++ b/web/app/cad/part/menuConfig.js
@@ -8,7 +8,7 @@ export default [
id: 'craft',
cssIcons: ['magic'],
info: 'set of available craft operations on a solid',
- actions: ['EXTRUDE', 'CUT', 'REVOLVE', 'SHELL', 'FILLET']
+ actions: ['EXTRUDE', 'CUT', 'REVOLVE', 'SHELL', 'FILLET', 'DATUM_CREATE']
},
{
id: 'primitives',
@@ -37,5 +37,15 @@ export default [
label: 'solid-context',
info: 'solid context actions',
actions: ['LookAtSolid']
- }
+ },
+ {
+ id: 'datum',
+ label: 'datum',
+ cssIcons: ['magic'],
+ info: 'operations on datum',
+ actions: ['DATUM_MOVE']
+ // actions: ['DATUM_MOVE', 'DATUM_ROTATE', 'DATUM_REBASE', '-', 'PLANE_FROM_DATUM', 'BOX', 'SPHERE', 'TORUS',
+ // 'CONE', 'CYLINDER']
+ },
+
];
diff --git a/web/app/cad/part/partOperationsPlugin.js b/web/app/cad/part/partOperationsPlugin.js
index e5f700ea..cb6e6e4b 100644
--- a/web/app/cad/part/partOperationsPlugin.js
+++ b/web/app/cad/part/partOperationsPlugin.js
@@ -4,6 +4,8 @@ import cutOperation from '../craft/cutExtrude/cutOperation';
import planeOperation from '../craft/primitives/planeOperation';
import filletOperation from '../craft/fillet/filletOperation';
import revolveOperation from '../craft/revolve/revolveOperation';
+import createDatumOperation from '../craft/datum/create/createDatumOperation';
+import moveDatumOperation from '../craft/datum/move/moveDatumOperation';
export function activate({services}) {
services.operation.registerOperations([
@@ -12,6 +14,8 @@ export function activate({services}) {
extrudeOperation,
cutOperation,
revolveOperation,
- filletOperation
+ filletOperation,
+ createDatumOperation,
+ moveDatumOperation
])
}
\ No newline at end of file
diff --git a/web/app/cad/preview/previewPlugin.js b/web/app/cad/preview/previewPlugin.js
new file mode 100644
index 00000000..73f8a3c7
--- /dev/null
+++ b/web/app/cad/preview/previewPlugin.js
@@ -0,0 +1,48 @@
+import {createPreviewer} from './scenePreviewer';
+
+export function activate(ctx) {
+ let {streams, services} = ctx;
+
+ const updateParams = mutator => streams.wizard.workingRequest.mutate(data => mutator(data.params));
+
+ let previewContext = {
+ operation: null,
+ previewer: null
+ };
+
+ streams.wizard.workingRequest.attach(({type, params}) => {
+ if (!type) {
+ if (previewContext.previewer) {
+ previewContext.previewer.dispose();
+ previewContext.previewer = null;
+ previewContext.operation = null;
+ ctx.services.viewer.requestRender();
+ }
+ return;
+ }
+ if (type !== previewContext.operation) {
+ if (previewContext.previewer != null) {
+ previewContext.previewer.dispose();
+ ctx.services.viewer.requestRender();
+ previewContext.previewer = null;
+ }
+ let operation = services.operation.get(type);
+
+ if (operation.previewGeomProvider) {
+ previewContext.previewer = createPreviewer(operation.previewGeomProvider, services, params);
+ ctx.services.viewer.requestRender();
+ } else if (operation.previewer) {
+ previewContext.previewer = operation.previewer(ctx, params, updateParams);
+ ctx.services.viewer.requestRender();
+ } else {
+ previewContext.previewer = null;
+ }
+ previewContext.operation = type;
+ } else {
+ if (previewContext.previewer) {
+ previewContext.previewer.update(params);
+ ctx.services.viewer.requestRender();
+ }
+ }
+ });
+}
\ No newline at end of file
diff --git a/web/app/cad/preview/scenePreviewer.js b/web/app/cad/preview/scenePreviewer.js
index b588a634..f6270ae6 100644
--- a/web/app/cad/preview/scenePreviewer.js
+++ b/web/app/cad/preview/scenePreviewer.js
@@ -3,7 +3,7 @@ import {createTransparentPhongMaterial} from 'scene/materials';
import {createMesh} from 'scene/objects/mesh';
-export function createPreviewer(sceneGeometryCreator, services) {
+export function createPreviewer(sceneGeometryCreator, services, initialParams) {
const previewGroup = SceneGraph.createGroup();
SceneGraph.addToGroup(services.cadScene.workGroup, previewGroup);
@@ -19,17 +19,21 @@ export function createPreviewer(sceneGeometryCreator, services) {
function update(params) {
destroyPreviewObject();
- previewObject = createMesh(sceneGeometryCreator(params, services), IMAGINARY_SURFACE_MATERIAL);
+ let geometry = sceneGeometryCreator(params, services);
+ if (!geometry) {
+ services.viewer.requestRender();
+ return;
+ }
+ previewObject = createMesh(geometry, IMAGINARY_SURFACE_MATERIAL);
previewGroup.add(previewObject);
- services.viewer.render();
}
function dispose() {
destroyPreviewObject();
SceneGraph.removeFromGroup(services.cadScene.workGroup, previewGroup);
- services.viewer.render();
}
+ update(initialParams);
return {update, dispose};
}
diff --git a/web/app/cad/sandbox.js b/web/app/cad/sandbox.js
index 1dca8b0e..023148b9 100644
--- a/web/app/cad/sandbox.js
+++ b/web/app/cad/sandbox.js
@@ -7,8 +7,10 @@ import NurbsCurve from "../brep/geom/curves/nurbsCurve";
import {surfaceIntersect} from '../brep/geom/intersection/surfaceSurface';
import {closestToCurveParam, findClosestToCurveParamRoughly} from '../brep/geom/curves/closestPoint';
import NurbsSurface from '../brep/geom/surfaces/nurbsSurface';
+import DatumObject3D from './craft/datum/datumObject';
+import CSys from '../math/csys';
-export function runSandbox({bus, services: { viewer, cadScene, cadRegistry, tpi, tpi: {addShellOnScene} }}) {
+export function runSandbox({bus, services, services: { viewer, cadScene, cadRegistry, tpi, tpi: {addShellOnScene} }}) {
function test1() {
@@ -238,7 +240,18 @@ export function runSandbox({bus, services: { viewer, cadScene, cadRegistry, tpi,
// cylinderAndPlaneIntersect();
// curvesIntersect();
// cylTest();
- surfaceSurfaceIntersect();
+ // surfaceSurfaceIntersect();
+
+
+ // let o1 = new DatumObject3D(CSys.origin().move(new Vector(200, 200, 200)), viewer.sceneSetup);
+ // o1.setMoveMode(DatumObject3D.AXIS.Y);
+ // cadScene.auxGroup.add(o1);
+ // let o2 = new DatumObject3D(CSys.origin().move(new Vector(-200, -200, -200)), viewer.sceneSetup);
+ // o2.setMoveMode(DatumObject3D.AXIS.Z);
+ // cadScene.auxGroup.add(o2);
+
+ services.action.run('DATUM_CREATE');
+
}
diff --git a/web/app/cad/scene/controls/mouseEventSystemPlugin.js b/web/app/cad/scene/controls/mouseEventSystemPlugin.js
new file mode 100644
index 00000000..16ff022f
--- /dev/null
+++ b/web/app/cad/scene/controls/mouseEventSystemPlugin.js
@@ -0,0 +1,110 @@
+import {findAncestor} from 'scene/sceneGraph';
+
+export function activate(context) {
+ const {services, streams} = context;
+ let domElement = services.viewer.sceneSetup.domElement();
+
+ domElement.addEventListener('mousedown', mousedown, false);
+ domElement.addEventListener('mouseup', mouseup, false);
+ domElement.addEventListener('mousemove', mousemove, false);
+
+ let performRaycast = e => services.viewer.raycast(e, services.viewer.sceneSetup.scene.children);
+
+ let toDrag = null;
+ let pressed = new Set();
+
+ function startDrag(objectToDrag, e) {
+ if (toDrag) {
+ stopDrag(e);
+ }
+ toDrag = objectToDrag;
+ services.viewer.sceneSetup.trackballControls.enabled = false;
+ }
+
+ function stopDrag(e) {
+ toDrag.dragDrop(e);
+ toDrag = null;
+ services.viewer.sceneSetup.trackballControls.enabled = true;
+ }
+
+ function mousedown(e) {
+ pressed.clear();
+ let hits = performRaycast(e);
+ for (let hit of hits) {
+ let obj = hit.object;
+ if (obj && obj.onMouseDown) {
+ obj.onMouseDown(e, hits, objectToDrag => startDrag(objectToDrag, e));
+ }
+ pressed.add(obj);
+ if (!hit.object.passMouseEvent || !hit.object.passMouseEvent(e, hits)) {
+ break;
+ }
+ }
+ }
+
+ function mouseup(e) {
+ if (toDrag) {
+ stopDrag(e);
+ mousemove(e);
+ } else {
+ let hits = performRaycast(e);
+ for (let hit of hits) {
+ let obj = hit.object;
+ if (obj && obj.onMouseUp) {
+ obj.onMouseUp(e, hits);
+ }
+ if (pressed.has(obj) && obj.onMouseClick) {
+ obj.onMouseClick(e, hits);
+ }
+ if (!hit.object.passMouseEvent || !hit.object.passMouseEvent(e, hits)) {
+ break;
+ }
+ }
+ pressed.clear();
+ }
+ }
+
+ let entered = new Set();
+ let valid = new Set();
+
+ function mousemove(e) {
+
+ if (toDrag) {
+ toDrag.dragMove(e);
+ } else {
+ let hits = performRaycast(e);
+
+ valid.clear();
+ for (let hit of hits) {
+ valid.add(hit.object);
+ if (!hit.object.passMouseEvent || !hit.object.passMouseEvent(e, hits)) {
+ break;
+ }
+ }
+
+ entered.forEach(e => {
+ if (!valid.has(e) && e.onMouseLeave) {
+ e.onMouseLeave(e, hits);
+ }
+ });
+
+ valid.forEach(e => {
+ if (!entered.has(e) && e.onMouseEnter) {
+ e.onMouseEnter(e, hits);
+ }
+ if (e.onMouseMove) {
+ e.onMouseMove(e, hits);
+ }
+ });
+
+ let t = valid;
+ valid = entered;
+ entered = t;
+ valid.clear();
+ }
+ }
+}
+
+export function hasObject(hits, object) {
+ return hits.find(hit => hit.object === object);
+}
\ No newline at end of file
diff --git a/web/app/cad/scene/controls/pickControlPlugin.js b/web/app/cad/scene/controls/pickControlPlugin.js
index 5b45ba13..33c5540e 100644
--- a/web/app/cad/scene/controls/pickControlPlugin.js
+++ b/web/app/cad/scene/controls/pickControlPlugin.js
@@ -1,7 +1,7 @@
import * as mask from 'gems/mask'
-import {getAttribute, setAttribute} from '../../../../../modules/scene/objectData';
+import {getAttribute, setAttribute} from 'scene/objectData';
import {FACE, EDGE, SKETCH_OBJECT} from '../entites';
-import {state} from '../../../../../modules/lstream';
+import {state} from 'lstream';
export const PICK_KIND = {
FACE: mask.type(1),
@@ -85,7 +85,7 @@ export function activate(context) {
}
function raycastObjects(event, kind, visitor) {
- let pickResults = services.viewer.raycast(event, services.cadScene.workGroup);
+ let pickResults = services.viewer.raycast(event, services.cadScene.workGroup.children);
const pickers = [
(pickResult) => {
if (mask.is(kind, PICK_KIND.SKETCH) && pickResult.object instanceof THREE.Line) {
@@ -126,36 +126,39 @@ export function activate(context) {
}
}
-function initStateAndServices({streams, services}) {
-
- services.selection = {
- };
-
+export function defineStreams({streams}) {
streams.selection = {
};
-
+ SELECTABLE_ENTITIES.forEach(entity => {
+ let selectionState = state([]);
+ streams.selection[entity] = state([]);
+ });
+
+}
+
+function initStateAndServices({streams, services}) {
+
+ services.selection = {};
+
SELECTABLE_ENTITIES.forEach(entity => {
let entitySelectApi = {
objects: [],
single: undefined
};
services.selection[entity] = entitySelectApi;
- let selectionState = state([]);
- streams.selection[entity] = selectionState;
+ let selectionState = streams.selection[entity];
selectionState.attach(selection => {
entitySelectApi.objects = selection.map(id => services.cadRegistry.findEntity(entity, id));
- entitySelectApi.single = entitySelectApi.objects[0];
+ entitySelectApi.single = entitySelectApi.objects[0];
});
entitySelectApi.select = selection => selectionState.value = selection;
});
- //withdraw all
streams.craft.models.attach(() => {
- Object.values(streams.selection).forEach(ss => ss.next([]))
- })
+ withdrawAll(streams.selection)
+ });
}
-
-
-
-
+export function withdrawAll(selectionStreams) {
+ Object.values(selectionStreams).forEach(stream => stream.next([]))
+}
diff --git a/web/app/cad/scene/entites.js b/web/app/cad/scene/entites.js
index 83f99209..4d98eba3 100644
--- a/web/app/cad/scene/entites.js
+++ b/web/app/cad/scene/entites.js
@@ -3,14 +3,16 @@ import {MFace} from '../model/mface';
import {MEdge} from '../model/medge';
import {MVertex} from '../model/mvertex';
import {MSketchObject} from '../model/msketchObject';
+import {MDatum} from '../model/mdatum';
export const SHELL = MShell.TYPE;
export const FACE = MFace.TYPE;
export const EDGE = MEdge.TYPE;
export const VERTEX = MVertex.TYPE;
export const SKETCH_OBJECT = MSketchObject.TYPE;
+export const DATUM = MDatum.TYPE;
-export const ENTITIES = [SHELL, FACE, EDGE, VERTEX, SKETCH_OBJECT];
+export const ENTITIES = [SHELL, DATUM, FACE, EDGE, VERTEX, SKETCH_OBJECT];
export const PART_MODELING_ENTITIES = [SHELL, FACE, EDGE, VERTEX, SKETCH_OBJECT];
export const ASSEMBLY_ENTITIES = [SHELL, FACE, EDGE, VERTEX];
diff --git a/web/app/cad/scene/selectionMarker/selectionMarkerPlugin.js b/web/app/cad/scene/selectionMarker/selectionMarkerPlugin.js
index 8bc16eac..781b084f 100644
--- a/web/app/cad/scene/selectionMarker/selectionMarkerPlugin.js
+++ b/web/app/cad/scene/selectionMarker/selectionMarkerPlugin.js
@@ -16,7 +16,7 @@ export function activate({streams, services}) {
model.ext.view.mark();
}
});
- services.viewer.render();
+ services.viewer.requestRender();
};
streams.selection.face.pairwise([]).attach(selectionSync(FACE));
diff --git a/web/app/cad/scene/viewSyncPlugin.js b/web/app/cad/scene/viewSyncPlugin.js
index c6d53cbb..7c53cd0a 100644
--- a/web/app/cad/scene/viewSyncPlugin.js
+++ b/web/app/cad/scene/viewSyncPlugin.js
@@ -5,6 +5,10 @@ import {MOpenFaceShell} from '../model/mopenFace';
import {EDGE, FACE, SHELL, SKETCH_OBJECT} from './entites';
import {OpenFaceShellView} from './views/openFaceView';
import {findDiff} from '../../../../modules/gems/iterables';
+import {MShell} from '../model/mshell';
+import {MDatum} from '../model/mdatum';
+import DatumView from './views/datumView';
+import {View} from './views/view';
export function activate(context) {
let {streams} = context;
@@ -12,15 +16,15 @@ export function activate(context) {
streams.sketcher.update.attach(mFace => mFace.ext.view.updateSketch());
}
-function sceneSynchronizer({services: {cadScene, cadRegistry}}) {
+function sceneSynchronizer({services: {cadScene, cadRegistry, viewer, wizard, action}}) {
return function() {
let wgChildren = cadScene.workGroup.children;
let existent = new Set();
for (let i = wgChildren.length - 1; i >= 0; --i) {
let obj = wgChildren[i];
- let shellView = getAttribute(obj, SHELL);
+ let shellView = getAttribute(obj, View.MARKER);
if (shellView) {
- let exists = cadRegistry.shellIndex.has(shellView.model.id);
+ let exists = cadRegistry.modelIndex.has(shellView.model.id);
if (!exists) {
SceneGraph.removeFromGroup(cadScene.workGroup, obj);
shellView.dispose();
@@ -30,17 +34,19 @@ function sceneSynchronizer({services: {cadScene, cadRegistry}}) {
}
}
- let allShells = cadRegistry.getAllShells();
-
- for (let shell of allShells) {
- if (!existent.has(shell.id)) {
- let shellView;
- if (shell instanceof MOpenFaceShell) {
- shellView = new OpenFaceShellView(shell);
+ for (let model of cadRegistry.models) {
+ if (!existent.has(model.id)) {
+ let modelView;
+ if (model instanceof MOpenFaceShell) {
+ modelView = new OpenFaceShellView(model);
+ } else if (model instanceof MShell) {
+ modelView = new ShellView(model);
+ } else if (model instanceof MDatum) {
+ modelView = new DatumView(model, viewer, wizard.open, (e) => action.run('menu.datum', e));
} else {
- shellView = new ShellView(shell);
+ console.warn('unsupported model ' + model);
}
- SceneGraph.addToGroup(cadScene.workGroup, shellView.rootGroup);
+ SceneGraph.addToGroup(cadScene.workGroup, modelView.rootGroup);
}
}
}
diff --git a/web/app/cad/scene/viewer.js b/web/app/cad/scene/viewer.js
index 25a5a63c..cc4cbd57 100644
--- a/web/app/cad/scene/viewer.js
+++ b/web/app/cad/scene/viewer.js
@@ -11,7 +11,7 @@ export default class Viewer {
this.sceneSetup.render();
}
- requestRender() {
+ requestRender = () => {
if (this.renderRequested) {
return;
}
@@ -19,14 +19,21 @@ export default class Viewer {
this.renderRequested = false;
this.render();
});
- }
+ };
+
+ setVisualProp = (obj, prop, value) => {
+ if (obj[prop] !== value) {
+ obj[prop] = value;
+ this.requestRender();
+ }
+ };
lookAt(obj) {
this.sceneSetup.lookAt(obj);
}
- raycast(event, group) {
- return this.sceneSetup.raycast(event, group);
+ raycast(event, objects) {
+ return this.sceneSetup.raycast(event, objects);
}
setCameraMode(mode) {
diff --git a/web/app/cad/scene/views/datumView.js b/web/app/cad/scene/views/datumView.js
index bc585596..ec1aeaaa 100644
--- a/web/app/cad/scene/views/datumView.js
+++ b/web/app/cad/scene/views/datumView.js
@@ -1,31 +1,164 @@
import {View} from './view';
-import * as SceneGraph from '../../../../../modules/scene/sceneGraph';
-import {createArrow} from '../../../../../modules/scene/objects/auxiliary';
-import {moveObject3D} from '../../../../../modules/scene/objects/transform';
-import {AXIS} from '../../../math/l3space';
+import DatumObject3D from '../../craft/datum/datumObject';
+import {DATUM} from '../entites';
+import {setAttribute} from 'scene/objectData';
+import {Mesh, MeshBasicMaterial, PolyhedronGeometry, SphereGeometry} from 'three';
+import {CSYS_SIZE_MODEL} from '../../craft/datum/csysObject';
export default class DatumView extends View {
- constructor(edge) {
- super(edge);
- this.rootGroup = SceneGraph.createGroup();
- }
-
- setUpAxises() {
- let arrowLength = 100;
- let createAxisArrow = createArrow.bind(null, arrowLength, 5, 2);
- let addAxis = (axis, color) => {
- let arrow = createAxisArrow(axis, color, 0.2);
- moveObject3D(arrow, axis.scale(-arrowLength * 0.5));
- SceneGraph.addToGroup(this.auxGroup, arrow);
- };
- addAxis(AXIS.X, 0xFF0000);
- addAxis(AXIS.Y, 0x00FF00);
- addAxis(AXIS.Z, 0x0000FF);
- }
+ constructor(datum, viewer, beginOperation, showDatumMenu) {
+ super(datum);
+ class MenuButton extends Mesh {
+
+ mouseInside;
+
+ constructor() {
+ super(new SphereGeometry( 1 ), new MeshBasicMaterial({
+ transparent: true,
+ opacity: 0.5,
+ color: 0xFFFFFF,
+ visible: false
+ }));
+ this.scale.multiplyScalar(CSYS_SIZE_MODEL * 0.2);
+ }
+
+ dispose() {
+ this.geometry.dispose();
+ this.material.dispose();
+ }
+
+ onMouseEnter() {
+ this.mouseInside = true;
+ this.updateVisibility();
+ this.material.color.setHex(0xFBB4FF);
+ viewer.requestRender();
+ }
+
+ onMouseLeave(e, hits, behindHits) {
+ this.mouseInside = false;
+ this.updateVisibility();
+ this.material.color.setHex(0xFFFFFF);
+ viewer.requestRender();
+ }
+
+ onMouseDown() {
+ this.material.color.setHex(0xB500FF);
+ viewer.requestRender();
+ }
+
+ onMouseUp() {
+ this.material.color.setHex(0xFBB4FF);
+ viewer.requestRender();
+ }
+
+ onMouseClick(e) {
+ showDatumMenu({
+ x: e.offsetX,
+ y: e.offsetY
+ });
+ }
+
+ updateVisibility() {
+ let datum3D = this.parent.parent;
+ viewer.setVisualProp(this.material, 'visible', !datum3D.operationStarted &&
+ (this.mouseInside || datum3D.affordanceArea.mouseInside));
+ }
+ }
+
+ class ActiveAffordanceBox extends AffordanceBox {
+
+ mouseInside;
+
+ onMouseEnter(e, hits) {
+ this.mouseInside = true;
+ this.parent.parent.menuButton.updateVisibility();
+
+ }
+
+ onMouseLeave(e, hits) {
+ this.mouseInside = false;
+ this.parent.parent.menuButton.updateVisibility();
+ }
+
+ passMouseEvent(e, hits) {
+ return true;
+ }
+ }
+
+ class StartingOperationDatumObject3D extends DatumObject3D {
+
+ operationStarted = false;
+
+ constructor(csys, viewer) {
+ super(csys, viewer);
+ this.affordanceArea = new ActiveAffordanceBox();
+ this.menuButton = new MenuButton();
+ this.csysObj.add(this.affordanceArea);
+ this.csysObj.add(this.menuButton);
+ }
+
+ dragStart(e, axis) {
+ if (!this.operationStarted) {
+ beginOperation('DATUM_MOVE', {
+ datum: datum.id
+ });
+ this.beginOperation();
+ }
+ super.dragStart(e, axis);
+ }
+
+ beginOperation() {
+ this.operationStarted = true;
+ this.menuButton.updateVisibility();
+ }
+
+ finishOperation() {
+ this.operationStarted = false;
+ this.menuButton.updateVisibility();
+ this.exitEditMode();
+ }
+
+ dispose() {
+ super.dispose();
+ this.affordanceArea.dispose();
+ this.menuButton.dispose();
+ }
+ }
+ this.rootGroup = new StartingOperationDatumObject3D(datum.csys, viewer);
+
+ setAttribute(this.rootGroup, DATUM, this);
+ setAttribute(this.rootGroup, View.MARKER, this);
+ }
dispose() {
+ super.dispose();
+ this.rootGroup.dispose();
+ }
+}
+
+class AffordanceBox extends Mesh {
+
+ constructor() {
+ super(new PolyhedronGeometry(
+ [0,0,0, 1,0,0, 0,1,0, 0,0,1],
+ [0,2,1, 0,1,3, 0,3,2, 1,2,3]
+ ), new MeshBasicMaterial({
+ transparent: true,
+ opacity: 0.5,
+ color: 0xAA8439,
+ visible: false
+ }));
+ let size = CSYS_SIZE_MODEL * 1.5;
+ let shift = -(size - CSYS_SIZE_MODEL) * 0.3;
+ this.scale.set(size, size, size);
+ this.position.set(shift, shift, shift);
+ }
+
+ dispose() {
+ this.geometry.dispose();
+ this.material.dispose();
}
}
\ No newline at end of file
diff --git a/web/app/cad/scene/views/openFaceView.js b/web/app/cad/scene/views/openFaceView.js
index f0fb9764..fa14284c 100644
--- a/web/app/cad/scene/views/openFaceView.js
+++ b/web/app/cad/scene/views/openFaceView.js
@@ -8,7 +8,8 @@ export class OpenFaceShellView extends View {
constructor(shell) {
super(shell);
this.openFace = new OpenFaceView(shell.face);
- setAttribute(this.rootGroup, SHELL, this)
+ setAttribute(this.rootGroup, SHELL, this);
+ setAttribute(this.rootGroup, View.MARKER, this);
}
get rootGroup() {
diff --git a/web/app/cad/scene/views/shellView.js b/web/app/cad/scene/views/shellView.js
index 63b869f2..032ec2cd 100644
--- a/web/app/cad/scene/views/shellView.js
+++ b/web/app/cad/scene/views/shellView.js
@@ -1,11 +1,10 @@
import {View} from './view';
import * as SceneGraph from '../../../../../modules/scene/sceneGraph';
-import {genSolidId} from '../../craft/cadRegistryPlugin';
import {setAttribute} from '../../../../../modules/scene/objectData';
import {createSolidMaterial} from '../wrappers/sceneObject';
import {FaceView} from './faceView';
-import {SHELL} from '../entites';
import {EdgeView} from './edgeView';
+import {SHELL} from '../entites';
export class ShellView extends View {
@@ -24,6 +23,7 @@ export class ShellView extends View {
SceneGraph.addToGroup(this.rootGroup, this.vertexGroup);
setAttribute(this.rootGroup, SHELL, this);
+ setAttribute(this.rootGroup, View.MARKER, this);
const geometry = new THREE.Geometry();
geometry.dynamic = true;
diff --git a/web/app/cad/scene/views/view.js b/web/app/cad/scene/views/view.js
index a3214e86..b73b9058 100644
--- a/web/app/cad/scene/views/view.js
+++ b/web/app/cad/scene/views/view.js
@@ -1,5 +1,7 @@
export class View {
+ static MARKER = 'ModelView';
+
constructor(model) {
this.model = model;
model.ext.view = this;
diff --git a/web/app/math/csys.js b/web/app/math/csys.js
index 57e58962..d3df8a95 100644
--- a/web/app/math/csys.js
+++ b/web/app/math/csys.js
@@ -6,6 +6,10 @@ export default class CSys {
return new CSys(origin, dir, normal.cross(dir), normal)
}
+ static origin() {
+ return new CSys(ORIGIN.copy(), AXIS.X.copy(), AXIS.Y.copy(), AXIS.Z.copy());
+ }
+
constructor(origin, x, y, z) {
this.origin = origin;
this.x = x;
@@ -32,9 +36,22 @@ export default class CSys {
return this._outTr;
}
- copy() {
- return CSys(this.origin, this.x, this.y, this.z);
+ copy(csys) {
+ this.origin.setV(csys.origin);
+ this.x.setV(csys.x);
+ this.y.setV(csys.y);
+ this.z.setV(csys.z);
+ return this;
}
+
+ clone() {
+ return new CSys(this.origin.copy(), this.x.copy(), this.y.copy(), this.z.copy());
+ }
+
+ move(x, y, z) {
+ this.origin.set(x, y, z);
+ return this;
+ }
+
}
-CSys.ORIGIN = new CSys(ORIGIN, AXIS.X, AXIS.Y, AXIS.Z);