mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 16:33:15 +01:00
UI modularization / decouple 3D rendering
This commit is contained in:
parent
e8be3fe473
commit
046a10fe16
37 changed files with 631 additions and 436 deletions
72
modules/bus/index.js
Normal file
72
modules/bus/index.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
export default class Bus {
|
||||
|
||||
constructor() {
|
||||
this.listeners = {};
|
||||
this.state = {};
|
||||
this.recordFor = new Set();
|
||||
this.lock = new Set();
|
||||
}
|
||||
|
||||
subscribe(key, callback) {
|
||||
let listenerList = this.listeners[key];
|
||||
if (listenerList === undefined) {
|
||||
listenerList = [];
|
||||
this.listeners[key] = listenerList;
|
||||
}
|
||||
listenerList.push(callback);
|
||||
|
||||
if (this.recordFor.has(key)) {
|
||||
callback(this.state[key]);
|
||||
}
|
||||
return callback;
|
||||
};
|
||||
|
||||
unSubscribe(key, callback) {
|
||||
const listenerList = this.listeners[key];
|
||||
for (let i = 0; i < listenerList.length; i++) {
|
||||
if (listenerList[i] === callback) {
|
||||
listenerList.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dispatch(key, data) {
|
||||
if (this.lock.has(key)) {
|
||||
console.warn('recursive dispatch');
|
||||
return
|
||||
}
|
||||
this.lock.add(key);
|
||||
try {
|
||||
let listenerList = this.listeners[key];
|
||||
if (listenerList !== undefined) {
|
||||
for (let i = 0; i < listenerList.length; i++) {
|
||||
const callback = listenerList[i];
|
||||
try {
|
||||
callback(data);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.lock.delete(key);
|
||||
if (this.recordFor.has(key)) {
|
||||
this.state[key] = data;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enableState(forEvent, initValue) {
|
||||
this.recordFor.add(forEvent);
|
||||
this.state[forEvent] = initValue;
|
||||
}
|
||||
|
||||
disableState(forEvent) {
|
||||
this.recordFor.delete(forEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
61
modules/bus/store.js
Normal file
61
modules/bus/store.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
export class Store {
|
||||
|
||||
constructor() {
|
||||
this.state = {};
|
||||
this.listeners = {};
|
||||
this.locked = false;
|
||||
}
|
||||
|
||||
subscribe(key, callback) {
|
||||
let listenerList = this.listeners[key];
|
||||
if (listenerList === undefined) {
|
||||
listenerList = [];
|
||||
this.listeners[key] = listenerList;
|
||||
}
|
||||
listenerList.push(callback);
|
||||
return callback;
|
||||
};
|
||||
|
||||
unSubscribe(key, callback) {
|
||||
const listenerList = this.listeners[key];
|
||||
for (let i = 0; i < listenerList.length; i++) {
|
||||
if (listenerList[i] === callback) {
|
||||
listenerList.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
dispatch(key, newValue, oldValue) {
|
||||
if (this.locked === true) {
|
||||
throw 'concurrent state modification';
|
||||
}
|
||||
this.locked = true;
|
||||
try {
|
||||
let listenerList = this.listeners[key];
|
||||
if (listenerList !== undefined) {
|
||||
for (let i = 0; i < listenerList.length; i++) {
|
||||
const callback = listenerList[i];
|
||||
try {
|
||||
callback(newValue, oldValue, this);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.locked = false;
|
||||
}
|
||||
};
|
||||
|
||||
set(key, value) {
|
||||
let oldValue = this.state[key];
|
||||
this.state[key] = value;
|
||||
this.dispatch(key, value, oldValue);
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return this.state[key];
|
||||
}
|
||||
}
|
||||
29
modules/gems/iterables.js
Normal file
29
modules/gems/iterables.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
export function findDiff(arr1, arr2) {
|
||||
|
||||
let both = [];
|
||||
let firstOnly = [];
|
||||
let secondOnly = [];
|
||||
|
||||
for (let e1 of arr1) {
|
||||
for (let e2 of arr2) {
|
||||
if (e1 === e2) {
|
||||
both.push(e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let e1 of arr1) {
|
||||
if (both.indexOf(e1) === -1) {
|
||||
firstOnly.push(e1);
|
||||
}
|
||||
}
|
||||
|
||||
for (let e2 of arr2) {
|
||||
if (both.indexOf(e2) === -1) {
|
||||
secondOnly.push(e2);
|
||||
}
|
||||
}
|
||||
|
||||
return [both, firstOnly, secondOnly]
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import {MeshPhongMaterial, FaceColors, DoubleSide} from 'three';
|
||||
import DPR from 'dpr';
|
||||
import {MeshPhongMaterial, LineBasicMaterial, FaceColors, DoubleSide} from 'three';
|
||||
|
||||
export function createTransparentPhongMaterial(color, opacity) {
|
||||
return new MeshPhongMaterial({
|
||||
|
|
@ -13,4 +14,9 @@ export function createTransparentPhongMaterial(color, opacity) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
export function createLineMaterial(color, linewidth) {
|
||||
return new LineBasicMaterial({
|
||||
color,
|
||||
linewidth: linewidth / DPR
|
||||
});
|
||||
}
|
||||
17
modules/scene/objectData.js
Normal file
17
modules/scene/objectData.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
export function setAttribute(obj, key, value) {
|
||||
getData(obj)[key] = value;
|
||||
}
|
||||
|
||||
export function getAttribute(obj, key) {
|
||||
return getData(obj)[key];
|
||||
}
|
||||
|
||||
export function getData(obj) {
|
||||
let data = obj.__TCAD_CUSTOM_DATA;
|
||||
if (data === undefined) {
|
||||
data = {};
|
||||
obj.__TCAD_CUSTOM_DATA = data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
export function checkForSelectedFaces(amount) {
|
||||
return (state, app) => {
|
||||
state.enabled = app.viewer.selectionMgr.selection.length >= amount;
|
||||
state.enabled = app.getFaceSelection().length >= amount;
|
||||
if (!state.enabled) {
|
||||
state.hint = amount == 1 ? 'requires a face to be selected' : 'requires ' + amount + ' faces to be selected';
|
||||
state.hint = amount === 1 ? 'requires a face to be selected' : 'requires ' + amount + ' faces to be selected';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function checkForSelectedSolids(amount) {
|
||||
return (state, app) => {
|
||||
state.enabled = app.viewer.selectionMgr.selection.length >= amount;
|
||||
state.enabled = app.getFaceSelection().length >= amount;
|
||||
if (!state.enabled) {
|
||||
state.hint = amount == 1 ? 'requires a solid to be selected' : 'requires ' + amount + ' solids to be selected';
|
||||
state.hint = amount === 1 ? 'requires a solid to be selected' : 'requires ' + amount + ' solids to be selected';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ ActionManager.prototype.notify = function(event) {
|
|||
if (actions != undefined) {
|
||||
for (let action of actions) {
|
||||
this.updateAction(action);
|
||||
this.app.bus.notify('action.update.' + action.id, action.state);
|
||||
this.app.bus.dispatch('action.update.' + action.id, action.state);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import * as math from '../math/math'
|
|||
import {Matrix3, AXIS, ORIGIN} from '../math/l3space'
|
||||
import Counters from './counters'
|
||||
import {MeshSceneSolid} from './scene/wrappers/meshSceneObject'
|
||||
import DPR from '../utils/dpr'
|
||||
|
||||
export const FACE_COLOR = 0xB0C4DE;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export class PreviewWizard extends Wizard {
|
|||
}
|
||||
|
||||
dispose() {
|
||||
this.app.bus.unsubscribe('refreshSketch', this.onSketchUpdate);
|
||||
this.app.bus.unSubscribe('refreshSketch', this.onSketchUpdate);
|
||||
this.destroyPreviewObject();
|
||||
this.app.viewer.workGroup.remove(this.previewGroup);
|
||||
this.app.viewer.render();
|
||||
|
|
|
|||
|
|
@ -122,31 +122,31 @@ export class Wizard {
|
|||
}
|
||||
|
||||
createFormField(name, label, type, params, initValue) {
|
||||
if (type == 'number') {
|
||||
if (type === 'number') {
|
||||
const number = tk.config(new tk.Number(label, initValue, params.step, params.round), params);
|
||||
number.input.on('t-change', () => this.onUIChange(name));
|
||||
return Field.fromInput(number, Field.TEXT_TO_NUMBER_COERCION);
|
||||
} else if (type == 'choice') {
|
||||
} else if (type === 'choice') {
|
||||
const ops = params.options;
|
||||
const radio = new tk.InlineRadio(ops, ops, ops.indexOf(initValue));
|
||||
radio.root.find('input[type=radio]').on('change', () => {
|
||||
this.onUIChange(name);
|
||||
});
|
||||
return new Field(radio, () => radio.getValue(), (v) => radio.setValue(v));
|
||||
} else if (type == 'face') {
|
||||
return selectionWidget(name, label, initValue, this.app.viewer.selectionMgr, (selection) => selection.id);
|
||||
} else if (type == 'sketch.segment') {
|
||||
return selectionWidget(name, label, initValue, this.app.viewer.sketchSelectionMgr, (selection) => selection.__TCAD_SketchObject.id);
|
||||
} else if (type === 'face') {
|
||||
return selectionWidget(name, label, initValue, this.app.context.bus, 'selection:face',(selection) => selection.id);
|
||||
} else if (type === 'sketch.segment') {
|
||||
return selectionWidget(name, label, initValue, this.app.context.bus, 'selection:sketchObject', (selection) => selection.__TCAD_SketchObject.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function selectionWidget(name, label, initValue, selectionManager, toId) {
|
||||
function selectionWidget(name, label, initValue, bus, selectionKey, toId) {
|
||||
const obj = new tk.Text(label, initValue);
|
||||
obj.input.on('change', () => this.onUIChange(name));
|
||||
return Field.fromInput(obj, undefined, (objId) => {
|
||||
if (objId === CURRENT_SELECTION) {
|
||||
let selection = selectionManager.selection[0];
|
||||
let selection = bus.state[selectionKey][0];
|
||||
return selection ? toId(selection) : '';
|
||||
} else {
|
||||
return objId;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ export function Craft(app) {
|
|||
if (this._historyPointer === value) return;
|
||||
this._historyPointer = value;
|
||||
this.reset(this.history.slice(0, this._historyPointer));
|
||||
this.app.bus.notify('craft');
|
||||
this.app.bus.notify('historyPointer');
|
||||
this.app.bus.dispatch('craft');
|
||||
this.app.bus.dispatch('historyPointer');
|
||||
this.app.viewer.render();
|
||||
}
|
||||
});
|
||||
|
|
@ -30,7 +30,7 @@ Craft.prototype.remove = function(modificationIndex) {
|
|||
if (this.historyPointer >= history.length) {
|
||||
this.finishHistoryEditing();
|
||||
} else {
|
||||
this.app.bus.notify('historyShrink');
|
||||
this.app.bus.dispatch('historyShrink');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -38,8 +38,8 @@ Craft.prototype.loadHistory = function(history) {
|
|||
this.history = history;
|
||||
this._historyPointer = history.length;
|
||||
this.reset(history);
|
||||
this.app.bus.notify('craft');
|
||||
this.app.bus.notify('historyPointer');
|
||||
this.app.bus.dispatch('craft');
|
||||
this.app.bus.dispatch('historyPointer');
|
||||
this.app.viewer.render();
|
||||
};
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ Craft.prototype.modifyInternal = function(request) {
|
|||
this.app.viewer.workGroup.add(solid.cadGroup);
|
||||
}
|
||||
|
||||
this.app.bus.notify('solid-list', {
|
||||
this.app.bus.dispatch('solid-list', {
|
||||
solids: this.solids,
|
||||
needRefresh: result.created
|
||||
});
|
||||
|
|
@ -103,7 +103,7 @@ Craft.prototype.modify = function(request, overriding) {
|
|||
}
|
||||
this.history[this._historyPointer] = request;
|
||||
this._historyPointer ++;
|
||||
this.app.bus.notify('craft');
|
||||
this.app.bus.notify('historyPointer');
|
||||
this.app.bus.dispatch('craft');
|
||||
this.app.bus.dispatch('historyPointer');
|
||||
this.app.viewer.render();
|
||||
};
|
||||
|
|
@ -16,7 +16,7 @@ export function PlaneWizard(app, initParams) {
|
|||
relativeToFaceId: ''
|
||||
};
|
||||
this.selectionListener = () => {
|
||||
const face = this.app.viewer.selectionMgr.selection[0];
|
||||
const face = this.getFirstSelectedFace();
|
||||
if (face) {
|
||||
this.ui.relativeToFace.input.val(face.id);
|
||||
this.synch();
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ RevolveWizard.prototype.createRequest = function(done) {
|
|||
};
|
||||
|
||||
RevolveWizard.prototype.dispose = function() {
|
||||
this.app.bus.unsubscribe('selection-sketch-object', this.selectionListener);
|
||||
this.app.bus.unSubscribe('selection-sketch-object', this.selectionListener);
|
||||
OpWizard.prototype.dispose.call(this);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import DPR from '../../../../utils/dpr'
|
||||
import DPR from 'dpr'
|
||||
import * as tk from '../../../../ui/toolkit'
|
||||
|
||||
const IMAGINE_MATERIAL = new THREE.LineBasicMaterial({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {checkForSelectedFaces} from './actions/action-helpers'
|
||||
import {nurbsToThreeGeom, triangulateToThree} from './scene/wrappers/brepSceneObject'
|
||||
import {createSolidMaterial} from './scene/wrappers/sceneObject'
|
||||
import DPR from '../utils/dpr'
|
||||
import DPR from 'dpr'
|
||||
import Vector from 'math/vector';
|
||||
import {NurbsCurve} from "../brep/geom/impl/nurbs";
|
||||
import * as ui from '../ui/ui';
|
||||
|
|
@ -242,7 +242,7 @@ const DebugActions = {
|
|||
listens: ['selection'],
|
||||
update: checkForSelectedFaces(1),
|
||||
invoke: (app) => {
|
||||
var s = app.viewer.selectionMgr.selection[0];
|
||||
var s = app.getFirstSelectedFace();
|
||||
console.log(JSON.stringify({
|
||||
polygons: s.csgGroup.polygons,
|
||||
basis: s._basis
|
||||
|
|
@ -257,7 +257,7 @@ const DebugActions = {
|
|||
listens: ['selection'],
|
||||
update: checkForSelectedFaces(1),
|
||||
invoke: (app) => {
|
||||
console.log(app.viewer.selectionMgr.selection[0].id);
|
||||
console.log(app.getFirstSelectedFace().id);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -268,7 +268,7 @@ const DebugActions = {
|
|||
listens: ['selection'],
|
||||
update: checkForSelectedFaces(1),
|
||||
invoke: (app) => {
|
||||
const faceId = app.viewer.selectionMgr.selection[0].id;
|
||||
const faceId = app.getFirstSelectedFace().id;
|
||||
const sketch = JSON.parse(localStorage.getItem(app.faceStorageKey(faceId)));
|
||||
const layers = sketch.layers.filter(l => l.name != '__bounds__');
|
||||
const data = [];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import '../../../modules/scene/utils/vectorThreeEnhancement'
|
||||
import '../utils/three-loader'
|
||||
import {Bus} from '../ui/toolkit'
|
||||
import Bus from 'bus'
|
||||
import {Viewer} from './scene/viewer'
|
||||
import {UI} from './ui/ctrl'
|
||||
import TabSwitcher from './ui/tab-switcher'
|
||||
|
|
@ -31,6 +31,7 @@ import {Circle} from "./craft/sketch/sketch-model";
|
|||
import {Plane} from "../brep/geom/impl/plane";
|
||||
import {enclose} from "../brep/brep-enclose";
|
||||
// import {createSphere, rayMarchOntoCanvas, sdfIntersection, sdfSolid, sdfSubtract, sdfTransform, sdfUnion} from "../hds/sdf";
|
||||
import Plugins from './plugins';
|
||||
|
||||
function App() {
|
||||
this.id = this.processHints();
|
||||
|
|
@ -38,7 +39,11 @@ function App() {
|
|||
this.actionManager = new ActionManager(this);
|
||||
this.inputManager = new InputManager(this);
|
||||
this.state = this.createState();
|
||||
this.viewer = new Viewer(this.bus, document.getElementById('viewer-container'));
|
||||
this.context = this.createPluginContext();
|
||||
this.initPlugins();
|
||||
this.createViewer();
|
||||
this.viewer = this.context.services.viewer;
|
||||
this.viewer.workGroup = this.context.services.cadScene.workGroup;
|
||||
this.actionManager.registerActions(AllActions);
|
||||
this.tabSwitcher = new TabSwitcher($('#tab-switcher'), $('#view-3d'));
|
||||
this.controlBar = new ControlBar(this, $('#control-bar'));
|
||||
|
|
@ -67,7 +72,7 @@ function App() {
|
|||
var sketchFace = app.findFace(sketchFaceId);
|
||||
if (sketchFace != null) {
|
||||
app.refreshSketchOnFace(sketchFace);
|
||||
app.bus.notify('refreshSketch');
|
||||
app.bus.dispatch('refreshSketch');
|
||||
app.viewer.render();
|
||||
}
|
||||
}
|
||||
|
|
@ -82,6 +87,32 @@ function App() {
|
|||
});
|
||||
}
|
||||
|
||||
App.prototype.createPluginContext = function() {
|
||||
return {
|
||||
bus: this.bus,
|
||||
services: {}
|
||||
};
|
||||
};
|
||||
|
||||
App.prototype.initPlugins = function() {
|
||||
for (let plugin of Plugins) {
|
||||
plugin.activate(this.context);
|
||||
}
|
||||
};
|
||||
|
||||
App.prototype.createViewer = function() {
|
||||
this.context.bus.dispatch('dom:viewerContainer', document.getElementById('viewer-container'));
|
||||
};
|
||||
|
||||
App.prototype.getFaceSelection = function() {
|
||||
let selection = this.context.bus.state['selection:face'];
|
||||
return selection;
|
||||
};
|
||||
|
||||
App.prototype.getFirstSelectedFace = function() {
|
||||
return this.getSelection()[0];
|
||||
};
|
||||
|
||||
App.prototype.addShellOnScene = function(shell, skin) {
|
||||
const sceneSolid = new BREPSceneSolid(shell, undefined, skin);
|
||||
this.viewer.workGroup.add(sceneSolid.cadGroup);
|
||||
|
|
@ -320,7 +351,7 @@ App.prototype.lookAtSolid = function(solidId) {
|
|||
|
||||
App.prototype.createState = function() {
|
||||
const state = {};
|
||||
this.bus.defineObservable(state, 'showSketches', true);
|
||||
// this.bus.defineObservable(state, 'showSketches', true);
|
||||
return state;
|
||||
};
|
||||
|
||||
|
|
@ -604,7 +635,7 @@ App.prototype.cut = function() {
|
|||
|
||||
App.prototype.refreshSketches = function() {
|
||||
this._refreshSketches();
|
||||
this.bus.notify('refreshSketch');
|
||||
this.bus.dispatch('refreshSketch');
|
||||
this.viewer.render();
|
||||
};
|
||||
|
||||
|
|
|
|||
6
web/app/3d/plugins.js
Normal file
6
web/app/3d/plugins.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import * as ScenePlugin from './scene/scenePlugin';
|
||||
import * as SelectionMarkerPlugin from './scene/selectionMarker/selectionMarkerPlugin';
|
||||
|
||||
export default [
|
||||
ScenePlugin, SelectionMarkerPlugin
|
||||
]
|
||||
66
web/app/3d/scene/cadScene.js
Normal file
66
web/app/3d/scene/cadScene.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import {AXIS} from '../../math/l3space'
|
||||
import {createArrow} from 'scene/objects/auxiliary';
|
||||
import Vector from 'math/vector';
|
||||
import {OnTopOfAll} from 'scene/materialMixins';
|
||||
import {moveObject3D, setBasisToObject3D} from 'scene/objects/transform';
|
||||
|
||||
import * as SceneGraph from 'scene/sceneGraph';
|
||||
|
||||
export default class CadScene {
|
||||
|
||||
constructor(rootGroup) {
|
||||
this.workGroup = SceneGraph.createGroup();
|
||||
this.auxGroup = SceneGraph.createGroup();
|
||||
SceneGraph.addToGroup(rootGroup, this.workGroup);
|
||||
SceneGraph.addToGroup(rootGroup, this.auxGroup);
|
||||
|
||||
this.setUpAxises();
|
||||
this.setUpBasisGroup();
|
||||
}
|
||||
|
||||
setUpAxises() {
|
||||
let arrowLength = 1500;
|
||||
let createAxisArrow = createArrow.bind(null, arrowLength, 40, 16);
|
||||
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);
|
||||
}
|
||||
|
||||
setUpBasisGroup() {
|
||||
let length = 200;
|
||||
let arrowLength = length * 0.2;
|
||||
let arrowHead = arrowLength * 0.4;
|
||||
|
||||
let _createArrow = createArrow.bind(null, length, arrowLength, arrowHead);
|
||||
|
||||
function createBasisArrow(axis, color) {
|
||||
return _createArrow(axis, color, 0.4, [OnTopOfAll]);
|
||||
}
|
||||
|
||||
this.basisGroup = SceneGraph.createGroup();
|
||||
let xAxis = createBasisArrow(new Vector(1, 0, 0), 0xFF0000);
|
||||
let yAxis = createBasisArrow(new Vector(0, 1, 0), 0x00FF00);
|
||||
SceneGraph.addToGroup(this.basisGroup, xAxis);
|
||||
SceneGraph.addToGroup(this.basisGroup, yAxis);
|
||||
}
|
||||
|
||||
updateBasis(basis, depth) {
|
||||
setBasisToObject3D(this.basisGroup, basis, depth);
|
||||
}
|
||||
|
||||
showBasis() {
|
||||
this.workGroup.add(this.basisGroup);
|
||||
}
|
||||
|
||||
hideBasis() {
|
||||
if (this.basisGroup.parent !== null) {
|
||||
this.basisGroup.parent.remove(this.basisGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
0
web/app/3d/scene/controls/controlsManager.js
Normal file
0
web/app/3d/scene/controls/controlsManager.js
Normal file
118
web/app/3d/scene/controls/pickControl.js
Normal file
118
web/app/3d/scene/controls/pickControl.js
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import * as mask from 'gems/mask'
|
||||
|
||||
export const PICK_KIND = {
|
||||
FACE: mask.type(1),
|
||||
SKETCH: mask.type(2),
|
||||
EDGE: mask.type(3),
|
||||
VERTEX: mask.type(4)
|
||||
};
|
||||
|
||||
|
||||
export default class PickControl {
|
||||
constructor(context) {
|
||||
this.context = context;
|
||||
let {bus} = context;
|
||||
let domElement = context.services.viewer.sceneSetup.domElement();
|
||||
bus.enableState('selection:solid', []);
|
||||
bus.enableState('selection:face', []);
|
||||
bus.enableState('selection:edge', []);
|
||||
bus.enableState('selection:sketchObject', []);
|
||||
|
||||
this.mouseState = {
|
||||
startX: 0,
|
||||
startY: 0
|
||||
};
|
||||
|
||||
domElement.addEventListener('mousedown', this.mousedown, false);
|
||||
domElement.addEventListener('mouseup', this.mouseup, false);
|
||||
}
|
||||
|
||||
mousedown = e => {
|
||||
this.mouseState.startX = e.offsetX;
|
||||
this.mouseState.startY = e.offsetY;
|
||||
};
|
||||
|
||||
mouseup = e => {
|
||||
let dx = Math.abs(this.mouseState.startX - e.offsetX);
|
||||
let dy = Math.abs(this.mouseState.startY - e.offsetY);
|
||||
let TOL = 1;
|
||||
if (dx < TOL && dy < TOL) {
|
||||
if (e.button !== 0) {
|
||||
this.handleSolidPick(e);
|
||||
} else {
|
||||
this.handlePick(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
selected(key, object) {
|
||||
let selection = this.context.bus.state[key];
|
||||
return selection !== undefined && selection.indexOf(object) !== -1;
|
||||
}
|
||||
|
||||
handlePick(event) {
|
||||
this.raycastObjects(event, PICK_KIND.FACE | PICK_KIND.SKETCH | PICK_KIND.EDGE, (object, kind) => {
|
||||
if (kind === PICK_KIND.FACE) {
|
||||
if (!this.selected('selection:face', object)) {
|
||||
this.context.bus.dispatch('selection:face', [object]);
|
||||
return false;
|
||||
}
|
||||
} else if (kind === PICK_KIND.SKETCH) {
|
||||
if (!this.selected('selection:sketchObject', object)) {
|
||||
this.context.bus.dispatch('selection:sketchObject', [object]);
|
||||
return false;
|
||||
}
|
||||
} else if (kind === PICK_KIND.EDGE) {
|
||||
if (!this.selected('selection:edge', object)) {
|
||||
this.context.bus.dispatch('selection:edge', [object]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
handleSolidPick(e) {
|
||||
this.raycastObjects(e, PICK_KIND.FACE, (sketchFace) => {
|
||||
this.context.bus.dispatch('selection:solid', sketchFace.solid);
|
||||
this.context.services.viewer.render();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
raycastObjects(event, kind, visitor) {
|
||||
let pickResults = this.context.services.viewer.raycast(event, this.context.services.cadScene.workGroup);
|
||||
const pickers = [
|
||||
(pickResult) => {
|
||||
if (mask.is(kind, PICK_KIND.SKETCH) && pickResult.object instanceof THREE.Line &&
|
||||
pickResult.object.__TCAD_SketchObject !== undefined) {
|
||||
return !visitor(pickResult.object, PICK_KIND.SKETCH);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(pickResult) => {
|
||||
if (mask.is(kind, PICK_KIND.EDGE) && pickResult.object.__TCAD_EDGE !== undefined) {
|
||||
return !visitor(pickResult.object, PICK_KIND.EDGE);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(pickResult) => {
|
||||
if (mask.is(kind, PICK_KIND.FACE) && !!pickResult.face && pickResult.face.__TCAD_SceneFace !== undefined) {
|
||||
const sketchFace = pickResult.face.__TCAD_SceneFace;
|
||||
return !visitor(sketchFace, PICK_KIND.FACE);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
];
|
||||
for (let i = 0; i < pickResults.length; i++) {
|
||||
const pickResult = pickResults[i];
|
||||
for (let picker of pickers) {
|
||||
if (picker(pickResult)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
|
||||
|
||||
export default class PickControl {
|
||||
constructor(bus) {
|
||||
this.bus = bus;
|
||||
}
|
||||
}
|
||||
|
||||
export function initPickControl(domElement, onPick) {
|
||||
let mouseState = {
|
||||
startX: 0,
|
||||
startY: 0
|
||||
};
|
||||
|
||||
//fix for FireFox
|
||||
function fixOffsetAPI(event) {
|
||||
if (event.offsetX === undefined) {
|
||||
event.offsetX = event.layerX;
|
||||
event.offsetY = event.layerY;
|
||||
}
|
||||
}
|
||||
|
||||
domElement.addEventListener('mousedown',
|
||||
function (e) {
|
||||
fixOffsetAPI(e);
|
||||
mouseState.startX = e.offsetX;
|
||||
mouseState.startY = e.offsetY;
|
||||
}, false);
|
||||
|
||||
domElement.addEventListener('mouseup',
|
||||
function (e) {
|
||||
fixOffsetAPI(e);
|
||||
let dx = Math.abs(mouseState.startX - e.offsetX);
|
||||
let dy = Math.abs(mouseState.startY - e.offsetY);
|
||||
let TOL = 1;
|
||||
if (dx < TOL && dy < TOL) {
|
||||
onPick(e);
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
20
web/app/3d/scene/scenePlugin.js
Normal file
20
web/app/3d/scene/scenePlugin.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import Viewer from './viewer';
|
||||
import CadScene from "./cadScene";
|
||||
import PickControl from "./controls/pickControl";
|
||||
|
||||
export function activate(context) {
|
||||
context.bus.subscribe('dom:viewerContainer', (container) => {
|
||||
initScene(context, container);
|
||||
});
|
||||
}
|
||||
|
||||
function initScene(context, container) {
|
||||
let viewer = new Viewer(container);
|
||||
context.services.viewer = viewer;
|
||||
|
||||
context.services.cadScene = new CadScene(viewer.sceneSetup.rootGroup);
|
||||
|
||||
let pickControl = new PickControl(context);
|
||||
|
||||
context.bus.subscribe('scene:update', () => viewer.render());
|
||||
}
|
||||
53
web/app/3d/scene/selectionMarker/abstractSelectionMarker.js
Normal file
53
web/app/3d/scene/selectionMarker/abstractSelectionMarker.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import {findDiff} from 'gems/iterables';
|
||||
|
||||
export class AbstractSelectionMarker {
|
||||
|
||||
constructor(bus, event) {
|
||||
this.bus = bus;
|
||||
this.selection = [];
|
||||
this.bus.subscribe(event, this.update);
|
||||
}
|
||||
|
||||
update = selection => {
|
||||
if (!selection) {
|
||||
if (this.selection.length !== 0) {
|
||||
for (let obj of this.selection) {
|
||||
this.unMark(obj);
|
||||
}
|
||||
this.selection = [];
|
||||
}
|
||||
this.bus.dispatch('scene:update');
|
||||
return;
|
||||
}
|
||||
|
||||
let [, toMark, toWithdraw] = findDiff(selection, this.selection);
|
||||
for (let obj of toMark) {
|
||||
this.selection.push(obj);
|
||||
this.mark(obj);
|
||||
}
|
||||
|
||||
for (let obj of toWithdraw) {
|
||||
this.selection.splice(this.selection.indexOf(obj), 1);
|
||||
this.unMark(obj);
|
||||
}
|
||||
this.bus.dispatch('scene:update');
|
||||
};
|
||||
|
||||
mark(obj) {
|
||||
throw 'abstract';
|
||||
}
|
||||
|
||||
unMark(obj) {
|
||||
throw 'abstract';
|
||||
}
|
||||
}
|
||||
|
||||
export function setFacesColor(faces, color) {
|
||||
for (let face of faces) {
|
||||
if (color === null) {
|
||||
face.color.set(new THREE.Color());
|
||||
} else {
|
||||
face.color.set( color );
|
||||
}
|
||||
}
|
||||
}
|
||||
9
web/app/3d/scene/selectionMarker/edgeSelectionMarker.js
Normal file
9
web/app/3d/scene/selectionMarker/edgeSelectionMarker.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import {AbstractSelectionMarker} from "./abstractSelectionMarker";
|
||||
import {LineMarker} from "./lineMarker";
|
||||
|
||||
export class EdgeSelectionMarker extends LineMarker {
|
||||
|
||||
constructor (bus, selectionMaterial) {
|
||||
super(bus, 'selection:edge', selectionMaterial);
|
||||
}
|
||||
}
|
||||
20
web/app/3d/scene/selectionMarker/lineMarker.js
Normal file
20
web/app/3d/scene/selectionMarker/lineMarker.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import {AbstractSelectionMarker} from "./abstractSelectionMarker";
|
||||
import {setAttribute, getAttribute} from 'scene/objectData';
|
||||
|
||||
export class LineMarker extends AbstractSelectionMarker {
|
||||
|
||||
constructor(bus, event, selectionMaterial) {
|
||||
super(bus, event);
|
||||
this.selectionMaterial = selectionMaterial;
|
||||
}
|
||||
|
||||
mark(obj) {
|
||||
setAttribute(obj, 'selection:defaultMaterial', obj.material);
|
||||
obj.material = this.selectionMaterial;
|
||||
}
|
||||
|
||||
unMark(obj) {
|
||||
obj.material = getAttribute(obj, 'selection:defaultMaterial');
|
||||
obj.material = this.selectionMaterial;
|
||||
}
|
||||
}
|
||||
47
web/app/3d/scene/selectionMarker/selectionMarker.js
Normal file
47
web/app/3d/scene/selectionMarker/selectionMarker.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import * as stitching from '../../../brep/stitching'
|
||||
import {AbstractSelectionMarker, setFacesColor} from "./abstractSelectionMarker";
|
||||
|
||||
export class SelectionMarker extends AbstractSelectionMarker {
|
||||
|
||||
constructor(bus, selectionColor, readOnlyColor, defaultColor) {
|
||||
super(bus, 'selection:face');
|
||||
this.selectionColor = selectionColor;
|
||||
this.defaultColor = defaultColor;
|
||||
this.readOnlyColor = readOnlyColor;
|
||||
}
|
||||
|
||||
mark(sceneFace) {
|
||||
this.setColor(sceneFace, this.selectionColor, this.readOnlyColor);
|
||||
}
|
||||
|
||||
unMark(sceneFace) {
|
||||
this.setColor(sceneFace, this.defaultColor, this.defaultColor);
|
||||
}
|
||||
|
||||
setColor(sceneFace, color, groupColor) {
|
||||
const group = this.findGroup(sceneFace);
|
||||
if (group) {
|
||||
for (let i = 0; i < group.length; i++) {
|
||||
let face = group[i];
|
||||
setFacesColor(face.meshFaces, groupColor);
|
||||
face.solid.mesh.geometry.colorsNeedUpdate = true;
|
||||
}
|
||||
} else {
|
||||
setFacesColor(sceneFace.meshFaces, color);
|
||||
sceneFace.solid.mesh.geometry.colorsNeedUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
findGroup(sceneFace) {
|
||||
if (sceneFace.curvedSurfaces) {
|
||||
return sceneFace.curvedSurfaces;
|
||||
}
|
||||
if (sceneFace.brepFace) {
|
||||
const stitchedFace = sceneFace.brepFace.data[stitching.FACE_CHUNK];
|
||||
if (stitchedFace) {
|
||||
return stitchedFace.faces.map(f => f.data['scene.face']);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
13
web/app/3d/scene/selectionMarker/selectionMarkerPlugin.js
Normal file
13
web/app/3d/scene/selectionMarker/selectionMarkerPlugin.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import DPR from 'dpr';
|
||||
import {SelectionMarker} from './selectionMarker';
|
||||
import {SketchSelectionMarker} from './sketchSelectionMarker';
|
||||
import {EdgeSelectionMarker} from './edgeSelectionMarker';
|
||||
import {createLineMaterial} from 'scene/materials';
|
||||
|
||||
export function activate(context) {
|
||||
let {bus} = context;
|
||||
new SelectionMarker(bus, 0xFAFAD2, 0xFF0000, null);
|
||||
new SketchSelectionMarker(bus, createLineMaterial(0xFF0000, 6 / DPR));
|
||||
new EdgeSelectionMarker(bus, createLineMaterial(0xFA8072, 12 / DPR));
|
||||
}
|
||||
|
||||
11
web/app/3d/scene/selectionMarker/sketchSelectionMarker.js
Normal file
11
web/app/3d/scene/selectionMarker/sketchSelectionMarker.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import {AbstractSelectionMarker} from "./abstractSelectionMarker";
|
||||
import {setAttribute} from 'scene/objectData';
|
||||
import {getAttribute} from "../../../../../modules/scene/objectData";
|
||||
import {LineMarker} from "./lineMarker";
|
||||
|
||||
export class SketchSelectionMarker extends LineMarker {
|
||||
|
||||
constructor(bus, selectionMaterial) {
|
||||
super(bus, 'selection:sketchObject', selectionMaterial);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,175 +1,26 @@
|
|||
import {AXIS} from '../../math/l3space'
|
||||
import DPR from 'dpr'
|
||||
import * as mask from '../../utils/mask';
|
||||
import {EdgeSelectionManager, SelectionManager, SketchSelectionManager} from '../selection'
|
||||
import {createArrow} from 'scene/objects/auxiliary';
|
||||
import Vector from 'math/vector';
|
||||
import {OnTopOfAll} from 'scene/materialMixins';
|
||||
import SceneSetup from 'scene/sceneSetup';
|
||||
import * as SceneGraph from 'scene/sceneGraph';
|
||||
import {moveObject3D, setBasisToObject3D} from 'scene/objects/transform';
|
||||
import {initPickControl} from "./pickControl";
|
||||
|
||||
export class Viewer {
|
||||
export default class Viewer {
|
||||
|
||||
constructor(bus, container) {
|
||||
this.bus = bus;
|
||||
constructor(container) {
|
||||
this.sceneSetup = new SceneSetup(container);
|
||||
initPickControl(this.sceneSetup.domElement(), this.onPick);
|
||||
|
||||
this.workGroup = SceneGraph.createGroup();
|
||||
this.auxGroup = SceneGraph.createGroup();
|
||||
SceneGraph.addToGroup(this.sceneSetup.rootGroup, this.workGroup);
|
||||
SceneGraph.addToGroup(this.sceneSetup.rootGroup, this.auxGroup);
|
||||
|
||||
this.setUpAxises();
|
||||
this.setUpBasisGroup();
|
||||
this.setUpSelectionManager();
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.sceneSetup.render();
|
||||
}
|
||||
|
||||
setUpAxises() {
|
||||
let arrowLength = 1500;
|
||||
let createAxisArrow = createArrow.bind(null, arrowLength, 40, 16);
|
||||
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);
|
||||
}
|
||||
|
||||
setUpSelectionManager() {
|
||||
this.selectionMgr = new SelectionManager(this, 0xFAFAD2, 0xFF0000, null);
|
||||
this.sketchSelectionMgr = new SketchSelectionManager(this, new THREE.LineBasicMaterial({
|
||||
color: 0xFF0000,
|
||||
linewidth: 6 / DPR
|
||||
}));
|
||||
this.edgeSelectionMgr = new EdgeSelectionManager(this, new THREE.LineBasicMaterial({
|
||||
color: 0xFA8072,
|
||||
linewidth: 12 / DPR
|
||||
}));
|
||||
}
|
||||
|
||||
setUpBasisGroup() {
|
||||
let length = 200;
|
||||
let arrowLength = length * 0.2;
|
||||
let arrowHead = arrowLength * 0.4;
|
||||
|
||||
let _createArrow = createArrow.bind(null, length, arrowLength, arrowHead);
|
||||
|
||||
function createBasisArrow(axis, color) {
|
||||
return _createArrow(axis, color, 0.4, [OnTopOfAll]);
|
||||
}
|
||||
|
||||
this.basisGroup = SceneGraph.createGroup();
|
||||
let xAxis = createBasisArrow(new Vector(1, 0, 0), 0xFF0000);
|
||||
let yAxis = createBasisArrow(new Vector(0, 1, 0), 0x00FF00);
|
||||
SceneGraph.addToGroup(this.basisGroup, xAxis);
|
||||
SceneGraph.addToGroup(this.basisGroup, yAxis);
|
||||
}
|
||||
|
||||
updateBasis(basis, depth) {
|
||||
setBasisToObject3D(this.basisGroup, basis, depth);
|
||||
}
|
||||
|
||||
showBasis() {
|
||||
this.workGroup.add(this.basisGroup);
|
||||
}
|
||||
|
||||
hideBasis() {
|
||||
if (this.basisGroup.parent !== null) {
|
||||
this.basisGroup.parent.remove(this.basisGroup);
|
||||
}
|
||||
}
|
||||
|
||||
lookAt(obj) {
|
||||
this.sceneSetup.lookAt(obj);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onPick = e => {
|
||||
if (e.button !== 0) {
|
||||
this.handleSolidPick(e);
|
||||
} else {
|
||||
this.handlePick(e);
|
||||
}
|
||||
};
|
||||
|
||||
handlePick(event) {
|
||||
this.raycastObjects(event, PICK_KIND.FACE | PICK_KIND.SKETCH | PICK_KIND.EDGE, (object, kind) => {
|
||||
if (kind === PICK_KIND.FACE) {
|
||||
if (this.selectionMgr.pick(object)) {
|
||||
return false;
|
||||
}
|
||||
} else if (kind === PICK_KIND.SKETCH) {
|
||||
if (this.sketchSelectionMgr.pick(object)) {
|
||||
return false;
|
||||
}
|
||||
} else if (kind === PICK_KIND.EDGE) {
|
||||
if (this.edgeSelectionMgr.pick(object)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
raycast(event, group) {
|
||||
return this.sceneSetup.raycast(event, group);
|
||||
}
|
||||
|
||||
handleSolidPick(e) {
|
||||
this.raycastObjects(event, PICK_KIND.FACE, (sketchFace) => {
|
||||
this.selectionMgr.clear();
|
||||
this.bus.notify("solid-pick", sketchFace.solid);
|
||||
this.render();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
raycastObjects(event, kind, visitor) {
|
||||
let pickResults = this.sceneSetup.raycast(event, this.workGroup);
|
||||
const pickers = [
|
||||
(pickResult) => {
|
||||
if (mask.is(kind, PICK_KIND.SKETCH) && pickResult.object instanceof THREE.Line &&
|
||||
pickResult.object.__TCAD_SketchObject !== undefined) {
|
||||
return !visitor(pickResult.object, PICK_KIND.SKETCH);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(pickResult) => {
|
||||
if (mask.is(kind, PICK_KIND.EDGE) && pickResult.object.__TCAD_EDGE !== undefined) {
|
||||
return !visitor(pickResult.object, PICK_KIND.EDGE);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(pickResult) => {
|
||||
if (mask.is(kind, PICK_KIND.FACE) && !!pickResult.face && pickResult.face.__TCAD_SceneFace !== undefined) {
|
||||
const sketchFace = pickResult.face.__TCAD_SceneFace;
|
||||
return !visitor(sketchFace, PICK_KIND.FACE);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
];
|
||||
for (let i = 0; i < pickResults.length; i++) {
|
||||
const pickResult = pickResults[i];
|
||||
for (let picker of pickers) {
|
||||
if (picker(pickResult)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setCameraMode() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const PICK_KIND = {
|
||||
FACE: mask.type(1),
|
||||
SKETCH: mask.type(2),
|
||||
EDGE: mask.type(3),
|
||||
VERTEX: mask.type(4)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,193 +0,0 @@
|
|||
import DPR from '../utils/dpr'
|
||||
import * as stitching from '../brep/stitching'
|
||||
|
||||
class AbstractSelectionManager {
|
||||
|
||||
constructor(viewer) {
|
||||
this.viewer = viewer;
|
||||
this.selection = [];
|
||||
this.viewer.bus.subscribe('craft', () => this.deselectAll());
|
||||
}
|
||||
|
||||
contains(face) {
|
||||
return this.selection.indexOf(face) != -1;
|
||||
}
|
||||
|
||||
pick(object) {
|
||||
if (!this.contains(object)) {
|
||||
this.select(object);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
select() {
|
||||
throw "AbstractFunctionCall";
|
||||
}
|
||||
|
||||
deselectAll() {
|
||||
throw "AbstractFunctionCall";
|
||||
}
|
||||
}
|
||||
|
||||
export class SketchSelectionManager extends AbstractSelectionManager {
|
||||
|
||||
constructor (viewer, selectionMaterial) {
|
||||
super(viewer);
|
||||
this.selectionMaterial = selectionMaterial;
|
||||
this.defaultMaterials = [];
|
||||
}
|
||||
|
||||
select(line) {
|
||||
this._clearSilent();
|
||||
this.defaultMaterials.push(line.material);
|
||||
this.selection.push(line);
|
||||
line.material = this.selectionMaterial;
|
||||
this.notify();
|
||||
this.viewer.render();
|
||||
}
|
||||
|
||||
deselectAll() {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._clearSilent();
|
||||
this.notify();
|
||||
this.viewer.render();
|
||||
}
|
||||
|
||||
_clearSilent() {
|
||||
for (let i = 0; i < this.selection.length; i++) {
|
||||
this.selection[i].material = this.defaultMaterials[i];
|
||||
}
|
||||
this.defaultMaterials.length = 0;
|
||||
this.selection.length = 0;
|
||||
}
|
||||
|
||||
notify() {
|
||||
this.viewer.bus.notify('selection-sketch-object');
|
||||
}
|
||||
}
|
||||
|
||||
export class EdgeSelectionManager extends AbstractSelectionManager {
|
||||
|
||||
constructor (viewer, selectionMaterial) {
|
||||
super(viewer);
|
||||
this.selectionMaterial = selectionMaterial;
|
||||
this.defaultMaterials = [];
|
||||
}
|
||||
|
||||
select(line) {
|
||||
this._clearSilent();
|
||||
const edge = line.__TCAD_EDGE;
|
||||
const stitchedCurve = edge.data[stitching.EDGE_CHUNK];
|
||||
if (stitchedCurve) {
|
||||
for (let edgeChunk of stitchedCurve.edges) {
|
||||
this.mark(edgeChunk.data['scene.edge']);
|
||||
}
|
||||
} else {
|
||||
this.mark(line);
|
||||
}
|
||||
this.notify();
|
||||
this.viewer.render();
|
||||
}
|
||||
|
||||
mark(line) {
|
||||
this.defaultMaterials.push(line.material);
|
||||
this.selection.push(line);
|
||||
line.material = this.selectionMaterial;
|
||||
}
|
||||
|
||||
deselectAll() {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._clearSilent();
|
||||
this.notify();
|
||||
this.viewer.render();
|
||||
}
|
||||
|
||||
_clearSilent() {
|
||||
for (let i = 0; i < this.selection.length; i++) {
|
||||
this.selection[i].material = this.defaultMaterials[i];
|
||||
}
|
||||
this.defaultMaterials.length = 0;
|
||||
this.selection.length = 0;
|
||||
}
|
||||
|
||||
notify() {
|
||||
//this.viewer.bus.notify('selection-edge');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class SelectionManager extends AbstractSelectionManager {
|
||||
|
||||
constructor(viewer, selectionColor, readOnlyColor, defaultColor) {
|
||||
super(viewer);
|
||||
this.selectionColor = selectionColor;
|
||||
this.defaultColor = defaultColor;
|
||||
this.readOnlyColor = readOnlyColor;
|
||||
this.planeSelection = [];
|
||||
}
|
||||
|
||||
select(sceneFace) {
|
||||
this.clear();
|
||||
const group = this.findGroup(sceneFace);
|
||||
if (group) {
|
||||
for (var i = 0; i < group.length; i++) {
|
||||
var face = group[i];
|
||||
this.selection.push(face);
|
||||
setFacesColor(face.meshFaces, this.readOnlyColor);
|
||||
}
|
||||
} else {
|
||||
this.selection.push(sceneFace);
|
||||
this.viewer.updateBasis(sceneFace.basis(), sceneFace.depth());
|
||||
this.viewer.showBasis();
|
||||
setFacesColor(sceneFace.meshFaces, this.selectionColor);
|
||||
}
|
||||
sceneFace.solid.mesh.geometry.colorsNeedUpdate = true;
|
||||
this.viewer.bus.notify('selection', sceneFace);
|
||||
this.viewer.render();
|
||||
}
|
||||
|
||||
findGroup(sceneFace) {
|
||||
if (sceneFace.curvedSurfaces) {
|
||||
return sceneFace.curvedSurfaces;
|
||||
}
|
||||
if (sceneFace.brepFace) {
|
||||
const stitchedFace = sceneFace.brepFace.data[stitching.FACE_CHUNK];
|
||||
if (stitchedFace) {
|
||||
return stitchedFace.faces.map(f => f.data['scene.face']);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
deselectAll() {
|
||||
this.clear();
|
||||
this.viewer.bus.notify('selection', null);
|
||||
this.viewer.render();
|
||||
}
|
||||
|
||||
clear() {
|
||||
for (let selectee of this.selection) {
|
||||
setFacesColor(selectee.meshFaces, this.defaultColor);
|
||||
selectee.solid.mesh.geometry.colorsNeedUpdate = true;
|
||||
}
|
||||
this.viewer.hideBasis();
|
||||
this.selection.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function setFacesColor(faces, color) {
|
||||
for (let face of faces) {
|
||||
if (color == null) {
|
||||
face.color.set(new THREE.Color());
|
||||
} else {
|
||||
face.color.set( color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -153,7 +153,7 @@ UI.prototype.getInfoForOp = function(op) {
|
|||
};
|
||||
|
||||
UI.prototype.initOperation = function(op) {
|
||||
var selection = this.app.viewer.selectionMgr.selection;
|
||||
var selection = this.app.getFaceSelection();
|
||||
return this.createWizard(op, false, undefined, selection[0]);
|
||||
};
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ UI.prototype.createWizardForOperation = function(op) {
|
|||
var initParams = op.params;
|
||||
var face = op.face !== undefined ? this.app.findFace(op.face) : null;
|
||||
if (face != null) {
|
||||
this.app.viewer.selectionMgr.select(face);
|
||||
this.app.context.bus.dispatch('selection:face', face);
|
||||
}
|
||||
return this.createWizard(op.type, true, initParams, face);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ export class ToolManager {
|
|||
|
||||
switchTool(tool) {
|
||||
this.tool = tool;
|
||||
this.viewer.bus.notify("tool-change");
|
||||
this.viewer.bus.dispatch("tool-change");
|
||||
}
|
||||
|
||||
releaseControl() {
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ export class Tool {
|
|||
keyup(e) {};
|
||||
|
||||
sendMessage(text) {
|
||||
this.viewer.bus.notify('tool-message', text);
|
||||
this.viewer.bus.dispatch('tool-message', text);
|
||||
};
|
||||
|
||||
sendHint(hint) {
|
||||
this.viewer.bus.notify('tool-hint', hint);
|
||||
this.viewer.bus.dispatch('tool-hint', hint);
|
||||
};
|
||||
|
||||
sendSpecifyPointHint() {
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ Viewer.prototype.getActiveLayer = function() {
|
|||
Viewer.prototype.setActiveLayer = function(layer) {
|
||||
if (!layer.readOnly) {
|
||||
this._activeLayer = layer;
|
||||
this.bus.notify("activeLayer");
|
||||
this.bus.dispatch("activeLayer");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ Bus.prototype.unsubscribe = function(event, callback) {
|
|||
}
|
||||
};
|
||||
|
||||
Bus.prototype.notify = function(event, data, sender) {
|
||||
Bus.prototype.dispatch = function(event, data, sender) {
|
||||
var listenerList = this.listeners[event];
|
||||
if (listenerList !== undefined) {
|
||||
for (var i = 0; i < listenerList.length; i++) {
|
||||
|
|
@ -326,7 +326,7 @@ Bus.prototype.defineObservable = function(scope, name, initValue, eventName) {
|
|||
get: function() { return observable.value;},
|
||||
set: function(value) {
|
||||
observable.value = value;
|
||||
bus.notify(eventName, value);
|
||||
bus.dispatch(eventName, value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
export default (window.devicePixelRatio) ? window.devicePixelRatio : 1;
|
||||
Loading…
Reference in a new issue