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) {
|
export function createTransparentPhongMaterial(color, opacity) {
|
||||||
return new MeshPhongMaterial({
|
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) {
|
export function checkForSelectedFaces(amount) {
|
||||||
return (state, app) => {
|
return (state, app) => {
|
||||||
state.enabled = app.viewer.selectionMgr.selection.length >= amount;
|
state.enabled = app.getFaceSelection().length >= amount;
|
||||||
if (!state.enabled) {
|
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) {
|
export function checkForSelectedSolids(amount) {
|
||||||
return (state, app) => {
|
return (state, app) => {
|
||||||
state.enabled = app.viewer.selectionMgr.selection.length >= amount;
|
state.enabled = app.getFaceSelection().length >= amount;
|
||||||
if (!state.enabled) {
|
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) {
|
if (actions != undefined) {
|
||||||
for (let action of actions) {
|
for (let action of actions) {
|
||||||
this.updateAction(action);
|
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 {Matrix3, AXIS, ORIGIN} from '../math/l3space'
|
||||||
import Counters from './counters'
|
import Counters from './counters'
|
||||||
import {MeshSceneSolid} from './scene/wrappers/meshSceneObject'
|
import {MeshSceneSolid} from './scene/wrappers/meshSceneObject'
|
||||||
import DPR from '../utils/dpr'
|
|
||||||
|
|
||||||
export const FACE_COLOR = 0xB0C4DE;
|
export const FACE_COLOR = 0xB0C4DE;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export class PreviewWizard extends Wizard {
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this.app.bus.unsubscribe('refreshSketch', this.onSketchUpdate);
|
this.app.bus.unSubscribe('refreshSketch', this.onSketchUpdate);
|
||||||
this.destroyPreviewObject();
|
this.destroyPreviewObject();
|
||||||
this.app.viewer.workGroup.remove(this.previewGroup);
|
this.app.viewer.workGroup.remove(this.previewGroup);
|
||||||
this.app.viewer.render();
|
this.app.viewer.render();
|
||||||
|
|
|
||||||
|
|
@ -122,31 +122,31 @@ export class Wizard {
|
||||||
}
|
}
|
||||||
|
|
||||||
createFormField(name, label, type, params, initValue) {
|
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);
|
const number = tk.config(new tk.Number(label, initValue, params.step, params.round), params);
|
||||||
number.input.on('t-change', () => this.onUIChange(name));
|
number.input.on('t-change', () => this.onUIChange(name));
|
||||||
return Field.fromInput(number, Field.TEXT_TO_NUMBER_COERCION);
|
return Field.fromInput(number, Field.TEXT_TO_NUMBER_COERCION);
|
||||||
} else if (type == 'choice') {
|
} else if (type === 'choice') {
|
||||||
const ops = params.options;
|
const ops = params.options;
|
||||||
const radio = new tk.InlineRadio(ops, ops, ops.indexOf(initValue));
|
const radio = new tk.InlineRadio(ops, ops, ops.indexOf(initValue));
|
||||||
radio.root.find('input[type=radio]').on('change', () => {
|
radio.root.find('input[type=radio]').on('change', () => {
|
||||||
this.onUIChange(name);
|
this.onUIChange(name);
|
||||||
});
|
});
|
||||||
return new Field(radio, () => radio.getValue(), (v) => radio.setValue(v));
|
return new Field(radio, () => radio.getValue(), (v) => radio.setValue(v));
|
||||||
} else if (type == 'face') {
|
} else if (type === 'face') {
|
||||||
return selectionWidget(name, label, initValue, this.app.viewer.selectionMgr, (selection) => selection.id);
|
return selectionWidget(name, label, initValue, this.app.context.bus, 'selection:face',(selection) => selection.id);
|
||||||
} else if (type == 'sketch.segment') {
|
} else if (type === 'sketch.segment') {
|
||||||
return selectionWidget(name, label, initValue, this.app.viewer.sketchSelectionMgr, (selection) => selection.__TCAD_SketchObject.id);
|
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);
|
const obj = new tk.Text(label, initValue);
|
||||||
obj.input.on('change', () => this.onUIChange(name));
|
obj.input.on('change', () => this.onUIChange(name));
|
||||||
return Field.fromInput(obj, undefined, (objId) => {
|
return Field.fromInput(obj, undefined, (objId) => {
|
||||||
if (objId === CURRENT_SELECTION) {
|
if (objId === CURRENT_SELECTION) {
|
||||||
let selection = selectionManager.selection[0];
|
let selection = bus.state[selectionKey][0];
|
||||||
return selection ? toId(selection) : '';
|
return selection ? toId(selection) : '';
|
||||||
} else {
|
} else {
|
||||||
return objId;
|
return objId;
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ export function Craft(app) {
|
||||||
if (this._historyPointer === value) return;
|
if (this._historyPointer === value) return;
|
||||||
this._historyPointer = value;
|
this._historyPointer = value;
|
||||||
this.reset(this.history.slice(0, this._historyPointer));
|
this.reset(this.history.slice(0, this._historyPointer));
|
||||||
this.app.bus.notify('craft');
|
this.app.bus.dispatch('craft');
|
||||||
this.app.bus.notify('historyPointer');
|
this.app.bus.dispatch('historyPointer');
|
||||||
this.app.viewer.render();
|
this.app.viewer.render();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -30,7 +30,7 @@ Craft.prototype.remove = function(modificationIndex) {
|
||||||
if (this.historyPointer >= history.length) {
|
if (this.historyPointer >= history.length) {
|
||||||
this.finishHistoryEditing();
|
this.finishHistoryEditing();
|
||||||
} else {
|
} else {
|
||||||
this.app.bus.notify('historyShrink');
|
this.app.bus.dispatch('historyShrink');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -38,8 +38,8 @@ Craft.prototype.loadHistory = function(history) {
|
||||||
this.history = history;
|
this.history = history;
|
||||||
this._historyPointer = history.length;
|
this._historyPointer = history.length;
|
||||||
this.reset(history);
|
this.reset(history);
|
||||||
this.app.bus.notify('craft');
|
this.app.bus.dispatch('craft');
|
||||||
this.app.bus.notify('historyPointer');
|
this.app.bus.dispatch('historyPointer');
|
||||||
this.app.viewer.render();
|
this.app.viewer.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ Craft.prototype.modifyInternal = function(request) {
|
||||||
this.app.viewer.workGroup.add(solid.cadGroup);
|
this.app.viewer.workGroup.add(solid.cadGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.app.bus.notify('solid-list', {
|
this.app.bus.dispatch('solid-list', {
|
||||||
solids: this.solids,
|
solids: this.solids,
|
||||||
needRefresh: result.created
|
needRefresh: result.created
|
||||||
});
|
});
|
||||||
|
|
@ -103,7 +103,7 @@ Craft.prototype.modify = function(request, overriding) {
|
||||||
}
|
}
|
||||||
this.history[this._historyPointer] = request;
|
this.history[this._historyPointer] = request;
|
||||||
this._historyPointer ++;
|
this._historyPointer ++;
|
||||||
this.app.bus.notify('craft');
|
this.app.bus.dispatch('craft');
|
||||||
this.app.bus.notify('historyPointer');
|
this.app.bus.dispatch('historyPointer');
|
||||||
this.app.viewer.render();
|
this.app.viewer.render();
|
||||||
};
|
};
|
||||||
|
|
@ -16,7 +16,7 @@ export function PlaneWizard(app, initParams) {
|
||||||
relativeToFaceId: ''
|
relativeToFaceId: ''
|
||||||
};
|
};
|
||||||
this.selectionListener = () => {
|
this.selectionListener = () => {
|
||||||
const face = this.app.viewer.selectionMgr.selection[0];
|
const face = this.getFirstSelectedFace();
|
||||||
if (face) {
|
if (face) {
|
||||||
this.ui.relativeToFace.input.val(face.id);
|
this.ui.relativeToFace.input.val(face.id);
|
||||||
this.synch();
|
this.synch();
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ RevolveWizard.prototype.createRequest = function(done) {
|
||||||
};
|
};
|
||||||
|
|
||||||
RevolveWizard.prototype.dispose = function() {
|
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);
|
OpWizard.prototype.dispose.call(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import DPR from '../../../../utils/dpr'
|
import DPR from 'dpr'
|
||||||
import * as tk from '../../../../ui/toolkit'
|
import * as tk from '../../../../ui/toolkit'
|
||||||
|
|
||||||
const IMAGINE_MATERIAL = new THREE.LineBasicMaterial({
|
const IMAGINE_MATERIAL = new THREE.LineBasicMaterial({
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import {checkForSelectedFaces} from './actions/action-helpers'
|
import {checkForSelectedFaces} from './actions/action-helpers'
|
||||||
import {nurbsToThreeGeom, triangulateToThree} from './scene/wrappers/brepSceneObject'
|
import {nurbsToThreeGeom, triangulateToThree} from './scene/wrappers/brepSceneObject'
|
||||||
import {createSolidMaterial} from './scene/wrappers/sceneObject'
|
import {createSolidMaterial} from './scene/wrappers/sceneObject'
|
||||||
import DPR from '../utils/dpr'
|
import DPR from 'dpr'
|
||||||
import Vector from 'math/vector';
|
import Vector from 'math/vector';
|
||||||
import {NurbsCurve} from "../brep/geom/impl/nurbs";
|
import {NurbsCurve} from "../brep/geom/impl/nurbs";
|
||||||
import * as ui from '../ui/ui';
|
import * as ui from '../ui/ui';
|
||||||
|
|
@ -242,7 +242,7 @@ const DebugActions = {
|
||||||
listens: ['selection'],
|
listens: ['selection'],
|
||||||
update: checkForSelectedFaces(1),
|
update: checkForSelectedFaces(1),
|
||||||
invoke: (app) => {
|
invoke: (app) => {
|
||||||
var s = app.viewer.selectionMgr.selection[0];
|
var s = app.getFirstSelectedFace();
|
||||||
console.log(JSON.stringify({
|
console.log(JSON.stringify({
|
||||||
polygons: s.csgGroup.polygons,
|
polygons: s.csgGroup.polygons,
|
||||||
basis: s._basis
|
basis: s._basis
|
||||||
|
|
@ -257,7 +257,7 @@ const DebugActions = {
|
||||||
listens: ['selection'],
|
listens: ['selection'],
|
||||||
update: checkForSelectedFaces(1),
|
update: checkForSelectedFaces(1),
|
||||||
invoke: (app) => {
|
invoke: (app) => {
|
||||||
console.log(app.viewer.selectionMgr.selection[0].id);
|
console.log(app.getFirstSelectedFace().id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -268,7 +268,7 @@ const DebugActions = {
|
||||||
listens: ['selection'],
|
listens: ['selection'],
|
||||||
update: checkForSelectedFaces(1),
|
update: checkForSelectedFaces(1),
|
||||||
invoke: (app) => {
|
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 sketch = JSON.parse(localStorage.getItem(app.faceStorageKey(faceId)));
|
||||||
const layers = sketch.layers.filter(l => l.name != '__bounds__');
|
const layers = sketch.layers.filter(l => l.name != '__bounds__');
|
||||||
const data = [];
|
const data = [];
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import '../../../modules/scene/utils/vectorThreeEnhancement'
|
import '../../../modules/scene/utils/vectorThreeEnhancement'
|
||||||
import '../utils/three-loader'
|
import '../utils/three-loader'
|
||||||
import {Bus} from '../ui/toolkit'
|
import Bus from 'bus'
|
||||||
import {Viewer} from './scene/viewer'
|
import {Viewer} from './scene/viewer'
|
||||||
import {UI} from './ui/ctrl'
|
import {UI} from './ui/ctrl'
|
||||||
import TabSwitcher from './ui/tab-switcher'
|
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 {Plane} from "../brep/geom/impl/plane";
|
||||||
import {enclose} from "../brep/brep-enclose";
|
import {enclose} from "../brep/brep-enclose";
|
||||||
// import {createSphere, rayMarchOntoCanvas, sdfIntersection, sdfSolid, sdfSubtract, sdfTransform, sdfUnion} from "../hds/sdf";
|
// import {createSphere, rayMarchOntoCanvas, sdfIntersection, sdfSolid, sdfSubtract, sdfTransform, sdfUnion} from "../hds/sdf";
|
||||||
|
import Plugins from './plugins';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
this.id = this.processHints();
|
this.id = this.processHints();
|
||||||
|
|
@ -38,7 +39,11 @@ function App() {
|
||||||
this.actionManager = new ActionManager(this);
|
this.actionManager = new ActionManager(this);
|
||||||
this.inputManager = new InputManager(this);
|
this.inputManager = new InputManager(this);
|
||||||
this.state = this.createState();
|
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.actionManager.registerActions(AllActions);
|
||||||
this.tabSwitcher = new TabSwitcher($('#tab-switcher'), $('#view-3d'));
|
this.tabSwitcher = new TabSwitcher($('#tab-switcher'), $('#view-3d'));
|
||||||
this.controlBar = new ControlBar(this, $('#control-bar'));
|
this.controlBar = new ControlBar(this, $('#control-bar'));
|
||||||
|
|
@ -67,7 +72,7 @@ function App() {
|
||||||
var sketchFace = app.findFace(sketchFaceId);
|
var sketchFace = app.findFace(sketchFaceId);
|
||||||
if (sketchFace != null) {
|
if (sketchFace != null) {
|
||||||
app.refreshSketchOnFace(sketchFace);
|
app.refreshSketchOnFace(sketchFace);
|
||||||
app.bus.notify('refreshSketch');
|
app.bus.dispatch('refreshSketch');
|
||||||
app.viewer.render();
|
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) {
|
App.prototype.addShellOnScene = function(shell, skin) {
|
||||||
const sceneSolid = new BREPSceneSolid(shell, undefined, skin);
|
const sceneSolid = new BREPSceneSolid(shell, undefined, skin);
|
||||||
this.viewer.workGroup.add(sceneSolid.cadGroup);
|
this.viewer.workGroup.add(sceneSolid.cadGroup);
|
||||||
|
|
@ -320,7 +351,7 @@ App.prototype.lookAtSolid = function(solidId) {
|
||||||
|
|
||||||
App.prototype.createState = function() {
|
App.prototype.createState = function() {
|
||||||
const state = {};
|
const state = {};
|
||||||
this.bus.defineObservable(state, 'showSketches', true);
|
// this.bus.defineObservable(state, 'showSketches', true);
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -604,7 +635,7 @@ App.prototype.cut = function() {
|
||||||
|
|
||||||
App.prototype.refreshSketches = function() {
|
App.prototype.refreshSketches = function() {
|
||||||
this._refreshSketches();
|
this._refreshSketches();
|
||||||
this.bus.notify('refreshSketch');
|
this.bus.dispatch('refreshSketch');
|
||||||
this.viewer.render();
|
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 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) {
|
constructor(container) {
|
||||||
this.bus = bus;
|
|
||||||
this.sceneSetup = new SceneSetup(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() {
|
render() {
|
||||||
this.sceneSetup.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) {
|
lookAt(obj) {
|
||||||
this.sceneSetup.lookAt(obj);
|
this.sceneSetup.lookAt(obj);
|
||||||
this.render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onPick = e => {
|
raycast(event, group) {
|
||||||
if (e.button !== 0) {
|
return this.sceneSetup.raycast(event, group);
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSolidPick(e) {
|
setCameraMode() {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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]);
|
return this.createWizard(op, false, undefined, selection[0]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -161,7 +161,7 @@ UI.prototype.createWizardForOperation = function(op) {
|
||||||
var initParams = op.params;
|
var initParams = op.params;
|
||||||
var face = op.face !== undefined ? this.app.findFace(op.face) : null;
|
var face = op.face !== undefined ? this.app.findFace(op.face) : null;
|
||||||
if (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);
|
return this.createWizard(op.type, true, initParams, face);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ export class ToolManager {
|
||||||
|
|
||||||
switchTool(tool) {
|
switchTool(tool) {
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
this.viewer.bus.notify("tool-change");
|
this.viewer.bus.dispatch("tool-change");
|
||||||
}
|
}
|
||||||
|
|
||||||
releaseControl() {
|
releaseControl() {
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ export class Tool {
|
||||||
keyup(e) {};
|
keyup(e) {};
|
||||||
|
|
||||||
sendMessage(text) {
|
sendMessage(text) {
|
||||||
this.viewer.bus.notify('tool-message', text);
|
this.viewer.bus.dispatch('tool-message', text);
|
||||||
};
|
};
|
||||||
|
|
||||||
sendHint(hint) {
|
sendHint(hint) {
|
||||||
this.viewer.bus.notify('tool-hint', hint);
|
this.viewer.bus.dispatch('tool-hint', hint);
|
||||||
};
|
};
|
||||||
|
|
||||||
sendSpecifyPointHint() {
|
sendSpecifyPointHint() {
|
||||||
|
|
|
||||||
|
|
@ -377,7 +377,7 @@ Viewer.prototype.getActiveLayer = function() {
|
||||||
Viewer.prototype.setActiveLayer = function(layer) {
|
Viewer.prototype.setActiveLayer = function(layer) {
|
||||||
if (!layer.readOnly) {
|
if (!layer.readOnly) {
|
||||||
this._activeLayer = layer;
|
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];
|
var listenerList = this.listeners[event];
|
||||||
if (listenerList !== undefined) {
|
if (listenerList !== undefined) {
|
||||||
for (var i = 0; i < listenerList.length; i++) {
|
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;},
|
get: function() { return observable.value;},
|
||||||
set: function(value) {
|
set: function(value) {
|
||||||
observable.value = 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