mirror of
https://github.com/xibyte/jsketcher
synced 2026-01-03 22:35:10 +01:00
introducing model object and decoupling from 3d
This commit is contained in:
parent
e226d416ee
commit
60878ad77c
67 changed files with 11525 additions and 373 deletions
1
modules/gems/func.js
Normal file
1
modules/gems/func.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const NOOP = () => {};
|
||||
|
|
@ -26,4 +26,6 @@ export function findDiff(arr1, arr2) {
|
|||
}
|
||||
|
||||
return [both, firstOnly, secondOnly]
|
||||
}
|
||||
}
|
||||
|
||||
export const EMPTY_ARRAY = Object.freeze([]);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
|
||||
export class StreamBase {
|
||||
|
||||
attach() {}
|
||||
|
||||
next(value) {}
|
||||
|
||||
|
||||
attach(observer) {}
|
||||
|
||||
map(fn) {
|
||||
return new MapStream(this, fn);
|
||||
}
|
||||
|
|
@ -12,39 +9,20 @@ export class StreamBase {
|
|||
filter(predicate) {
|
||||
return new FilterStream(this, predicate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class MapStream extends StreamBase {
|
||||
|
||||
constructor(stream, fn) {
|
||||
super();
|
||||
this.stream = stream;
|
||||
this.fn = fn;
|
||||
}
|
||||
|
||||
attach(observer) {
|
||||
return this.stream.attach(val => observer(this.fn(val)));
|
||||
}
|
||||
|
||||
static create = (stream, fn) => new MapStream(stream, fn);
|
||||
}
|
||||
|
||||
export class FilterStream extends StreamBase {
|
||||
|
||||
constructor(stream, predicate) {
|
||||
super();
|
||||
this.stream = stream;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
attach(observer) {
|
||||
return this.stream.attach(val => {
|
||||
if (this.predicate(val)) {
|
||||
observer(val);
|
||||
}
|
||||
});
|
||||
pairwise(first) {
|
||||
return new PairwiseStream(this, first);
|
||||
}
|
||||
|
||||
static create = (stream, predicate) => new FilterStream(stream, predicate);
|
||||
keep() {
|
||||
let stateStream = new StateStream(undefined);
|
||||
this.attach(v => stateStream.next(v));
|
||||
return stateStream;
|
||||
}
|
||||
}
|
||||
|
||||
const {MapStream} = require('./map');
|
||||
const {FilterStream} = require('./filter');
|
||||
const {StateStream} = require('./state');
|
||||
const {PairwiseStream} = require('./pairwise');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import {StreamBase} from './base';
|
||||
import {NOT_INITIALIZED} from './utils';
|
||||
|
||||
export class CombineStream extends StreamBase {
|
||||
|
||||
|
|
@ -15,8 +16,8 @@ export class CombineStream extends StreamBase {
|
|||
detachers[i] = s.attach(value => {
|
||||
this.values[i] = value;
|
||||
if (!this.ready) {
|
||||
this.ready = this.isReady();
|
||||
}
|
||||
this.ready = this.isReady();
|
||||
}
|
||||
if (this.ready) {
|
||||
observer(this.values);
|
||||
}
|
||||
|
|
@ -29,10 +30,8 @@ export class CombineStream extends StreamBase {
|
|||
for (let val of this.values) {
|
||||
if (val === NOT_INITIALIZED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const NOT_INITIALIZED = {};
|
||||
39
modules/lstream/external.js
Normal file
39
modules/lstream/external.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import {Emitter} from './emitter';
|
||||
|
||||
export class ExternalStateStream extends Emitter {
|
||||
|
||||
constructor(get, set) {
|
||||
super();
|
||||
this.get = get;
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.get();
|
||||
}
|
||||
|
||||
set value(v) {
|
||||
this.next(v);
|
||||
}
|
||||
|
||||
next(v) {
|
||||
this.set(v);
|
||||
super.next(v);
|
||||
}
|
||||
|
||||
update(updater) {
|
||||
this.value = updater(this.value);
|
||||
}
|
||||
|
||||
mutate(mutator) {
|
||||
mutator(this.value);
|
||||
this.next(this.value);
|
||||
}
|
||||
|
||||
attach(observer) {
|
||||
observer(this.value);
|
||||
return super.attach(observer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
modules/lstream/filter.js
Normal file
18
modules/lstream/filter.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import {StreamBase} from './base';
|
||||
|
||||
export class FilterStream extends StreamBase {
|
||||
|
||||
constructor(stream, predicate) {
|
||||
super();
|
||||
this.stream = stream;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
attach(observer) {
|
||||
return this.stream.attach(val => {
|
||||
if (this.predicate(val)) {
|
||||
observer(val);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -2,21 +2,31 @@ import {CombineStream} from './combine';
|
|||
import {StateStream} from './state';
|
||||
import {Emitter} from './emitter';
|
||||
import {FilterStream, MapStream} from './base';
|
||||
import {ExternalStateStream} from './external';
|
||||
import {MergeStream} from './merge';
|
||||
|
||||
export function stream() {
|
||||
return new Emitter();
|
||||
}
|
||||
|
||||
export function combine(...streams) {
|
||||
return new CombineStream(streams);
|
||||
}
|
||||
|
||||
export function stream() {
|
||||
return new Emitter();
|
||||
export function merge(...streams) {
|
||||
return new MergeStream(streams);
|
||||
}
|
||||
|
||||
export function state(initialValue) {
|
||||
return new StateStream(initialValue);
|
||||
}
|
||||
|
||||
export const map = MapStream.create;
|
||||
export function externalState(get, set) {
|
||||
return new ExternalStateStream(get, set);
|
||||
}
|
||||
|
||||
export const filter = FilterStream.create;
|
||||
export const map = (stream, fn) => new MapStream(stream, fn);
|
||||
|
||||
export const filter = (stream, predicate) => new FilterStream(stream, predicate);
|
||||
|
||||
export const merger = states => states.reduce((acc, v) => Object.assign(acc, v), {});
|
||||
|
|
|
|||
14
modules/lstream/map.js
Normal file
14
modules/lstream/map.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import {StreamBase} from './base';
|
||||
|
||||
export class MapStream extends StreamBase {
|
||||
|
||||
constructor(stream, fn) {
|
||||
super();
|
||||
this.stream = stream;
|
||||
this.fn = fn;
|
||||
}
|
||||
|
||||
attach(observer) {
|
||||
return this.stream.attach(v => observer(this.fn(v)));
|
||||
}
|
||||
}
|
||||
13
modules/lstream/memoize.js
Normal file
13
modules/lstream/memoize.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import {NOT_INITIALIZED} from './utils';
|
||||
|
||||
export function memoize(fn) {
|
||||
let value;
|
||||
let lastArg = NOT_INITIALIZED;
|
||||
return arg => {
|
||||
if (arg !== lastArg) {
|
||||
lastArg = arg;
|
||||
value = fn(arg);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
18
modules/lstream/merge.js
Normal file
18
modules/lstream/merge.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import {StreamBase} from './base';
|
||||
|
||||
export class MergeStream extends StreamBase {
|
||||
|
||||
constructor(streams) {
|
||||
super();
|
||||
this.streams = streams;
|
||||
}
|
||||
|
||||
attach(observer) {
|
||||
let detachers = new Array(this.streams.length);
|
||||
this.streams.forEach((s, i) => {
|
||||
detachers[i] = s.attach(observer);
|
||||
});
|
||||
return () => detachers.forEach(d => d());
|
||||
}
|
||||
}
|
||||
|
||||
17
modules/lstream/pairwise.js
Normal file
17
modules/lstream/pairwise.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import {StreamBase} from './base';
|
||||
|
||||
export class PairwiseStream extends StreamBase {
|
||||
|
||||
constructor(stream, first) {
|
||||
super();
|
||||
this.stream = stream;
|
||||
this.latest = first;
|
||||
}
|
||||
|
||||
attach(observer) {
|
||||
return this.stream.attach(v => {
|
||||
observer([this.latest, v]);
|
||||
this.latest = v;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -35,4 +35,3 @@ export class StateStream extends Emitter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
2
modules/lstream/utils.js
Normal file
2
modules/lstream/utils.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
export const NOT_INITIALIZED = Object.freeze({});
|
||||
11
modules/scene/staticResource.js
Normal file
11
modules/scene/staticResource.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
const STATIC_RESOURCES = [];
|
||||
|
||||
export default function staticResource(resource) {
|
||||
STATIC_RESOURCES.push(resource);
|
||||
return resource;
|
||||
}
|
||||
|
||||
window.addEventListener("beforeunload", function() {
|
||||
STATIC_RESOURCES.forEach(r => r.dispose());
|
||||
}, false);
|
||||
10279
package-lock.json
generated
Normal file
10279
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
10
web/app/api/model/modelObject.js
Normal file
10
web/app/api/model/modelObject.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
interface IMObject {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
IMFace
|
||||
|
||||
IMVertex
|
||||
|
|
@ -47,7 +47,7 @@ export class Line {
|
|||
return new Line(this.p0.plus(vector), this.v);
|
||||
}
|
||||
|
||||
approximate(resolution, from, to, path) {
|
||||
tessellate(resolution, from, to, path) {
|
||||
}
|
||||
|
||||
offset() {};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import * as stream from 'lstream';
|
|||
|
||||
export function activate(context) {
|
||||
|
||||
let {bus, streams} = context;
|
||||
let {streams} = context;
|
||||
|
||||
streams.action = {
|
||||
appearance: {},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import {subtract, union, intersect} from '../../brep/operations/boolean'
|
||||
import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'
|
||||
import {update as updateStitching} from '../../brep/stitching'
|
||||
import {BREPValidator} from '../../brep/brep-validator'
|
||||
import {Shell} from '../../brep/topo/shell'
|
||||
import {intersect, subtract, union} from '../../brep/operations/boolean';
|
||||
import {update as updateStitching} from '../../brep/stitching';
|
||||
import {BREPValidator} from '../../brep/brep-validator';
|
||||
import {Shell} from '../../brep/topo/shell';
|
||||
import {MBrepShell} from '../model/mshell';
|
||||
|
||||
const BoolOpMap = {
|
||||
'subtract': subtract,
|
||||
|
|
@ -12,7 +12,7 @@ const BoolOpMap = {
|
|||
|
||||
export function BooleanOperation(face, solid, operand, operationType) {
|
||||
let result;
|
||||
if (solid instanceof BREPSceneSolid) {
|
||||
if (solid instanceof MBrepShell) {
|
||||
const op = BoolOpMap[operationType];
|
||||
result = op(solid.shell, operand);
|
||||
for (let newFace of result.faces) {
|
||||
|
|
@ -25,7 +25,7 @@ export function BooleanOperation(face, solid, operand, operationType) {
|
|||
result = operand;
|
||||
}
|
||||
updateStitching(result);
|
||||
const newSolid = new BREPSceneSolid(result);
|
||||
const newSolid = new MBrepShell(result);
|
||||
return {
|
||||
outdated: [solid],
|
||||
created: [newSolid]
|
||||
|
|
|
|||
|
|
@ -1,43 +1,22 @@
|
|||
import {createToken} from "bus";
|
||||
import * as SceneGraph from 'scene/sceneGraph';
|
||||
import {EDGE, FACE, SKETCH_OBJECT} from '../scene/entites';
|
||||
|
||||
|
||||
export function activate({bus, services}) {
|
||||
export function activate({streams, services}) {
|
||||
|
||||
let registry = new Map();
|
||||
streams.cadRegistry = {
|
||||
shellIndex: streams.craft.models.map(models => models.reduce((i, v)=> i.set(v.id, v), new Map())).keep()
|
||||
};
|
||||
|
||||
streams.cadRegistry.update = streams.cadRegistry.shellIndex;
|
||||
|
||||
function getAllShells() {
|
||||
return Array.from(registry.values());
|
||||
}
|
||||
|
||||
function update(toRemove, toAdd) {
|
||||
if (toRemove) {
|
||||
toRemove.forEach(shell => {
|
||||
registry.delete(shell.tCadId);
|
||||
SceneGraph.removeFromGroup(services.cadScene.workGroup, shell.cadGroup);
|
||||
shell.dispose();
|
||||
});
|
||||
}
|
||||
if (toAdd) {
|
||||
toAdd.forEach(shell => {
|
||||
registry.set(shell.tCadId, shell);
|
||||
SceneGraph.addToGroup(services.cadScene.workGroup, shell.cadGroup);
|
||||
});
|
||||
}
|
||||
services.viewer.render();
|
||||
bus.dispatch(TOKENS.SHELLS, registry);
|
||||
return streams.craft.models.value;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
SOLIDS_COUNTER = 0;
|
||||
update(getAllShells());
|
||||
}
|
||||
|
||||
function findFace(faceId) {
|
||||
let shells = getAllShells();
|
||||
for (let shell of shells) {
|
||||
for (let face of shell.sceneFaces) {
|
||||
for (let face of shell.faces) {
|
||||
if (face.id === faceId) {
|
||||
return face;
|
||||
}
|
||||
|
|
@ -49,7 +28,7 @@ export function activate({bus, services}) {
|
|||
function findEdge(edgeId) {
|
||||
let shells = getAllShells();
|
||||
for (let shell of shells) {
|
||||
for (let edge of shell.sceneEdges) {
|
||||
for (let edge of shell.edges) {
|
||||
if (edge.id === edgeId) {
|
||||
return edge;
|
||||
}
|
||||
|
|
@ -59,10 +38,10 @@ export function activate({bus, services}) {
|
|||
}
|
||||
|
||||
function findSketchObject(sketchObjectGlobalId) {
|
||||
let [faceId, sketchObjectId] = sketchObjectGlobalId.split('/');
|
||||
let face = findFace(faceId);
|
||||
let [shellId, faceId, sketchObjectId] = sketchObjectGlobalId.split('/');
|
||||
let face = findFace(shellId+'/'+faceId);
|
||||
if (face) {
|
||||
return face.findById(sketchObjectGlobalId);
|
||||
return face.findSketchObjectById(sketchObjectGlobalId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -77,16 +56,11 @@ export function activate({bus, services}) {
|
|||
}
|
||||
|
||||
services.cadRegistry = {
|
||||
getAllShells, update, reset, findFace, findEdge, findSketchObject, findEntity
|
||||
getAllShells, findFace, findEdge, findSketchObject, findEntity,
|
||||
get shellIndex() {
|
||||
return streams.cadRegistry.shellIndex.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const TOKENS = {
|
||||
SHELLS: createToken('cadRegistry', 'shells'),
|
||||
};
|
||||
|
||||
let SOLIDS_COUNTER = 0;
|
||||
export function genSolidId() {
|
||||
return SOLIDS_COUNTER ++
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +1,78 @@
|
|||
import {createToken} from "bus";
|
||||
import {addModification} from './craftHistoryUtils';
|
||||
import {state, stream} from 'lstream';
|
||||
import {MShell} from '../model/mshell';
|
||||
|
||||
export function activate({bus, services}) {
|
||||
export function activate({streams, services}) {
|
||||
|
||||
bus.enableState(TOKENS.MODIFICATIONS, {
|
||||
let initialState = {
|
||||
history: [],
|
||||
pointer: -1
|
||||
});
|
||||
};
|
||||
|
||||
function isAdditiveChange({history, pointer}, {history:oldHistory, pointer:oldPointer}) {
|
||||
if (pointer < oldPointer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i <= oldPointer; i++) {
|
||||
let modCurr = history[i];
|
||||
let modPrev = oldHistory[i];
|
||||
if (modCurr !== modPrev) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bus.subscribe(TOKENS.MODIFICATIONS, (curr, prev) => {
|
||||
let beginIndex;
|
||||
if (isAdditiveChange(curr, prev)) {
|
||||
beginIndex = prev.pointer + 1;
|
||||
} else {
|
||||
services.cadRegistry.reset();
|
||||
beginIndex = 0;
|
||||
}
|
||||
let {history, pointer} = curr;
|
||||
for (let i = beginIndex; i <= pointer; i++) {
|
||||
modifyInternal(history[i]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function modifyInternal(request) {
|
||||
let op = services.operation.registry[request.type];
|
||||
if (!op) return `unknown operation ${request.type}`;
|
||||
|
||||
let result = op.run(request.params, services);
|
||||
|
||||
services.cadRegistry.update(result.outdated, result.created);
|
||||
}
|
||||
streams.craft = {
|
||||
modifications: state(initialState),
|
||||
models: state([]),
|
||||
update: stream()
|
||||
};
|
||||
|
||||
function modify(request) {
|
||||
bus.updateState(TOKENS.MODIFICATIONS, modifications => addModification(modifications, request));
|
||||
streams.craft.modifications.update(modifications => addModification(modifications, request));
|
||||
}
|
||||
|
||||
function reset(modifications) {
|
||||
bus.dispatch(TOKENS.MODIFICATIONS, {
|
||||
streams.craft.modifications.next({
|
||||
history: modifications,
|
||||
pointer: modifications.length - 1
|
||||
});
|
||||
}
|
||||
|
||||
services.craft = {
|
||||
modify, reset, TOKENS
|
||||
}
|
||||
modify, reset
|
||||
};
|
||||
|
||||
streams.craft.modifications.pairwise(initialState).attach(([prev, curr]) => {
|
||||
let models;
|
||||
let beginIndex;
|
||||
if (isAdditiveChange(prev, curr)) {
|
||||
beginIndex = prev.pointer + 1;
|
||||
models = new Set(streams.craft.models.value);
|
||||
} else {
|
||||
MShell.ID_COUNTER = 0;
|
||||
beginIndex = 0;
|
||||
models = new Set()
|
||||
}
|
||||
if (prev === curr) {
|
||||
return Array.from(models);
|
||||
}
|
||||
let {history, pointer} = curr;
|
||||
for (let i = beginIndex; i <= pointer; i++) {
|
||||
let request = history[i];
|
||||
|
||||
let op = services.operation.registry[request.type];
|
||||
if (!op) {
|
||||
console.log(`unknown operation ${request.type}`);
|
||||
}
|
||||
|
||||
let {outdated, created} = op.run(request.params, services);
|
||||
|
||||
outdated.forEach(m => models.delete(m));
|
||||
created.forEach(m => models.add(m));
|
||||
streams.craft.models.next(Array.from(models).sort(m => m.id));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const TOKENS = {
|
||||
MODIFICATIONS: createToken('craft', 'modifications')
|
||||
};
|
||||
function isAdditiveChange({history:oldHistory, pointer:oldPointer}, {history, pointer}) {
|
||||
if (pointer < oldPointer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i <= oldPointer; i++) {
|
||||
let modCurr = history[i];
|
||||
let modPrev = oldHistory[i];
|
||||
if (modCurr !== modPrev) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export function doOperation(params, {cadRegistry, sketcher}, cut) {
|
|||
let sketch = sketcher.readSketch(face.id);
|
||||
if (!sketch) throw 'illegal state';
|
||||
|
||||
let plane = face.surface().tangentPlane(0, 0);
|
||||
let plane = face.surface.tangentPlane(0, 0);
|
||||
const details = getEncloseDetails(params, sketch.fetchContours(), plane, !cut, false);
|
||||
const operand = combineShells(details.map(d => enclose(d.basePath, d.lidPath, d.baseSurface, d.lidSurface)));
|
||||
return BooleanOperation(face, solid, operand, cut ? 'subtract' : 'union');
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export function createPreviewGeomProvider(inversed) {
|
|||
if (!face) return null;
|
||||
let sketch = face.sketch.fetchContours();
|
||||
|
||||
const encloseDetails = getEncloseDetails(params, sketch, face.surface().tangentPlane(0, 0), !inversed);
|
||||
const encloseDetails = getEncloseDetails(params, sketch, face.surface.tangentPlane(0, 0), !inversed);
|
||||
const triangles = [];
|
||||
|
||||
for (let {basePath, lidPath, baseSurface, lidSurface} of encloseDetails) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import {createToken} from 'bus';
|
||||
import {TOKENS as WIZARD_TOKENS} from './wizard/wizardPlugin'
|
||||
|
||||
export function activate(context) {
|
||||
let {bus, services} = context;
|
||||
let {services} = context;
|
||||
|
||||
let registry = {};
|
||||
|
||||
let registry = {};
|
||||
|
||||
function addOperation(descriptor, actions) {
|
||||
let {id, label, info, icon, actionParams} = descriptor;
|
||||
|
||||
|
|
@ -17,7 +15,7 @@ export function activate(context) {
|
|||
icon32: icon + '32.png',
|
||||
icon96: icon + '96.png',
|
||||
},
|
||||
invoke: () => bus.dispatch(WIZARD_TOKENS.OPEN, {type: id}),
|
||||
invoke: () => services.wizard.open({type: id}),
|
||||
...actionParams
|
||||
};
|
||||
actions.push(opAction);
|
||||
|
|
@ -34,7 +32,7 @@ export function activate(context) {
|
|||
}
|
||||
services.action.registerActions(actions);
|
||||
}
|
||||
|
||||
|
||||
function get(id) {
|
||||
let op = registry[id];
|
||||
if (!op) {
|
||||
|
|
@ -42,12 +40,12 @@ export function activate(context) {
|
|||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
|
||||
services.operation = {
|
||||
registerOperations,
|
||||
registry,
|
||||
get
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function runOperation(request, descriptor, services) {
|
||||
|
|
@ -57,5 +55,5 @@ function runOperation(request, descriptor, services) {
|
|||
return result;
|
||||
}
|
||||
}
|
||||
return descriptor.run(request, services)
|
||||
return descriptor.run(request, services);
|
||||
}
|
||||
|
|
@ -1,15 +1,9 @@
|
|||
// import {MESH_OPERATIONS} from './mesh/workbench'
|
||||
// import {Extrude, Cut} from './brep/cut-extrude'
|
||||
// import {Revolve} from './brep/revolve'
|
||||
import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'
|
||||
// import {PlaneSceneObject} from '../scene/wrappers/planeSceneObject'
|
||||
import {box} from '../../brep/brep-primitives'
|
||||
|
||||
export const REVOLVE = {
|
||||
icon: 'img/cad/revolve',
|
||||
label: 'Revolve',
|
||||
info: (p) => '(' + p.angle + ')',
|
||||
action: (app, params) => Revolve(app, params)
|
||||
action: (app, params) => console.log(app, params)
|
||||
};
|
||||
|
||||
export const SHELL = {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import {box} from '../../../brep/brep-primitives'
|
||||
import {BREPSceneSolid} from '../../scene/wrappers/brepSceneObject';
|
||||
import {createBoxGeometry} from "scene/geoms";
|
||||
import {box} from '../../../brep/brep-primitives';
|
||||
import {createBoxGeometry} from 'scene/geoms';
|
||||
import BoxWizard from './BoxWizard';
|
||||
import {MBrepShell} from '../../model/mshell';
|
||||
|
||||
function createBox({width, height, depth}) {
|
||||
return {
|
||||
outdated: [],
|
||||
created: [new BREPSceneSolid(box(width, height, depth))]
|
||||
created: [new MBrepShell(box(width, height, depth))]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import {createMeshGeometry} from 'scene/geoms';
|
||||
import {STANDARD_BASES} from '../../../math/l3space';
|
||||
import {Plane} from '../../../brep/geom/impl/plane';
|
||||
import {PlaneSceneObject} from '../../scene/wrappers/planeSceneObject';
|
||||
import Vector from 'math/vector';
|
||||
import PlaneWizard from './PlaneWizard';
|
||||
import {MOpenFaceShell} from '../../model/mopenFace';
|
||||
import {createBoundingSurfaceFrom2DPoints} from '../../../brep/brep-builder';
|
||||
|
||||
function paramsToPlane({orientation, parallelTo, depth}, cadRegistry) {
|
||||
let face = null;
|
||||
|
|
@ -15,15 +16,18 @@ function paramsToPlane({orientation, parallelTo, depth}, cadRegistry) {
|
|||
const normal = STANDARD_BASES[orientation][2];
|
||||
plane = new Plane(normal, depth);
|
||||
} else {
|
||||
plane = new Plane(face.surface().normalInMiddle(), depth);
|
||||
plane = new Plane(face.surface.normalInMiddle(), depth);
|
||||
}
|
||||
return plane;
|
||||
}
|
||||
|
||||
function createPlane(params, {cadRegistry}) {
|
||||
let surface = createBoundingSurfaceFrom2DPoints([
|
||||
new Vector(0,0,0), new Vector(0,100,0), new Vector(100,100,0), new Vector(100,0,0)
|
||||
], paramsToPlane(params, cadRegistry));
|
||||
return {
|
||||
outdated: [],
|
||||
created: [new PlaneSceneObject(paramsToPlane(params, cadRegistry))]
|
||||
created: [new MOpenFaceShell(surface)]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import React from 'react';
|
||||
import connect from '../../../../../../modules/ui/connectLegacy';
|
||||
import {TOKENS as CRAFT_TOKENS} from '../../craftPlugin';
|
||||
import connect from 'ui/connect';
|
||||
import Wizard from './Wizard';
|
||||
import {finishHistoryEditing, stepOverridingParams} from '../../craftHistoryUtils';
|
||||
import {NOOP} from 'gems/func';
|
||||
import decoratorChain from 'ui/decoratorChain';
|
||||
import mapContext from 'ui/mapContext';
|
||||
|
||||
function HistoryWizard({history, pointer, step, cancel, offset}) {
|
||||
if (pointer === history.length - 1) {
|
||||
|
|
@ -16,11 +18,10 @@ function HistoryWizard({history, pointer, step, cancel, offset}) {
|
|||
|
||||
}
|
||||
|
||||
export default connect(HistoryWizard, CRAFT_TOKENS.MODIFICATIONS, {
|
||||
mapActions: ({updateState}) => ({
|
||||
step: (params) => updateState(CRAFT_TOKENS.MODIFICATIONS, modifications => stepOverridingParams(modifications, params)),
|
||||
cancel: () => updateState(CRAFT_TOKENS.MODIFICATIONS, modifications => finishHistoryEditing(modifications)),
|
||||
})
|
||||
});
|
||||
|
||||
const NOOP = () => {};
|
||||
export default decoratorChain(
|
||||
connect(streams => streams.craft.modifications),
|
||||
mapContext(({streams}) => ({
|
||||
step: params => streams.craft.modifications.update(modifications => stepOverridingParams(modifications, params)),
|
||||
cancel: () => streams.craft.modifications.update(modifications => finishHistoryEditing(modifications)),
|
||||
}))
|
||||
)(HistoryWizard);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import Window from 'ui/components/Window';
|
|||
import Stack from 'ui/components/Stack';
|
||||
import Button from 'ui/components/controls/Button';
|
||||
import ButtonGroup from 'ui/components/controls/ButtonGroup';
|
||||
import {CURRENT_SELECTION} from '../wizardPlugin';
|
||||
|
||||
import ls from './Wizard.less';
|
||||
import CadError from '../../../../utils/errors';
|
||||
|
|
@ -42,7 +41,15 @@ export default class Wizard extends React.Component {
|
|||
this.formContext.onChange = noop;
|
||||
}
|
||||
|
||||
componentDidCatch() {
|
||||
this.setState({hasInternalError: true});
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
if (this.state.hasInternalError) {
|
||||
return <span>operation error</span>;
|
||||
}
|
||||
let {type, left} = this.props;
|
||||
let {wizard: WizardImpl} = this.context.services.operation.get(type);
|
||||
|
||||
|
|
@ -62,7 +69,7 @@ export default class Wizard extends React.Component {
|
|||
</ButtonGroup>
|
||||
{this.state.hasError && <div className={ls.errorMessage}>
|
||||
performing operation with current parameters leads to an invalid object
|
||||
(manifold / self-intersecting / zero-thickness / complete degeneration or unsupported cases)
|
||||
(self-intersecting / zero-thickness / complete degeneration or unsupported cases)
|
||||
{this.state.code && <div className={ls.errorCode}>{this.state.code}</div>}
|
||||
{this.state.userMessage && <div className={ls.userErrorMessage}>{this.state.userMessage}</div>}
|
||||
</div>}
|
||||
|
|
@ -131,8 +138,7 @@ export default class Wizard extends React.Component {
|
|||
|
||||
|
||||
static contextTypes = {
|
||||
services: PropTypes.object,
|
||||
bus: PropTypes.object
|
||||
services: PropTypes.object
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,51 @@
|
|||
import React, {Fragment} from 'react';
|
||||
import {TOKENS as WIZARD_TOKENS} from '../wizardPlugin';
|
||||
import connect from 'ui/connectLegacy';
|
||||
import Wizard from './Wizard';
|
||||
import HistoryWizard from './HistoryWizard';
|
||||
import connect from '../../../../../../modules/ui/connect';
|
||||
import decoratorChain from '../../../../../../modules/ui/decoratorChain';
|
||||
import mapContext from '../../../../../../modules/ui/mapContext';
|
||||
import {finishHistoryEditing} from '../../craftHistoryUtils';
|
||||
|
||||
function WizardManager({wizards, close}) {
|
||||
return <Fragment>
|
||||
{wizards.map((wizardRef, wizardIndex) => {
|
||||
let {type, initialState} = wizardRef;
|
||||
class WizardManager extends React.Component {
|
||||
|
||||
render() {
|
||||
if (this.hasError) {
|
||||
return null;
|
||||
}
|
||||
let {wizards, close} = this.props;
|
||||
return <Fragment>
|
||||
{wizards.map((wizardRef, wizardIndex) => {
|
||||
let {type, initialState} = wizardRef;
|
||||
|
||||
const closeInstance = () => close(wizardRef);
|
||||
return <Wizard key={wizardIndex}
|
||||
type={type}
|
||||
close={closeInstance}
|
||||
initialState={initialState} left={offset(wizardIndex)} />
|
||||
})}
|
||||
<HistoryWizard offset={offset(wizards.length)}/>
|
||||
</Fragment>
|
||||
const closeInstance = () => close(wizardRef);
|
||||
return <Wizard key={wizardIndex}
|
||||
type={type}
|
||||
close={closeInstance}
|
||||
initialState={initialState} left={offset(wizardIndex)} />
|
||||
})}
|
||||
<HistoryWizard offset={offset(wizards.length)}/>
|
||||
</Fragment>;
|
||||
}
|
||||
|
||||
componentDidCatch() {
|
||||
this.hasError = true;
|
||||
this.props.reset();
|
||||
this.hasError = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function offset(wizardIndex) {
|
||||
return 70 + (wizardIndex * (250 + 20));
|
||||
}
|
||||
|
||||
export default connect(WizardManager, WIZARD_TOKENS.WIZARDS, {
|
||||
mapProps: ([wizards]) => ({wizards}),
|
||||
mapActions: ({dispatch}) => ({
|
||||
close: wizard => dispatch(WIZARD_TOKENS.CLOSE, wizard)
|
||||
})
|
||||
});
|
||||
export default decoratorChain(
|
||||
connect(streams => streams.wizards.map(wizards => ({wizards}))),
|
||||
mapContext(({services, streams}) => ({
|
||||
close: wizard => services.wizard.close(wizard),
|
||||
reset: () => {
|
||||
streams.wizards.value = [];
|
||||
streams.craft.modifications.update(modifications => finishHistoryEditing(modifications));
|
||||
}
|
||||
}))
|
||||
)
|
||||
(WizardManager);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {entitySelectionToken} from '../../../../scene/controls/pickControlPlugin';
|
||||
import {attachToForm} from './Form';
|
||||
import Stack from 'ui/components/Stack';
|
||||
import {FormContext} from '../form/Form';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {entitySelectionToken} from '../../../../scene/controls/pickControlPlugin';
|
||||
import {attachToForm} from './Form';
|
||||
import mapContext from 'ui/mapContext';
|
||||
|
||||
|
|
@ -17,8 +16,10 @@ class SingleEntityImpl extends React.Component {
|
|||
|
||||
selectionChanged = selection => {
|
||||
let selectedItem = selection[0];
|
||||
this.setState({selectedItem});
|
||||
this.props.onChange(selectedItem);
|
||||
if (selectedItem) {
|
||||
this.setState({selectedItem});
|
||||
this.props.onChange(selectedItem);
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
|
|
|
|||
|
|
@ -1,27 +1,25 @@
|
|||
import {createToken} from 'bus';
|
||||
import {state} from '../../../../../modules/lstream';
|
||||
|
||||
export function activate({bus, services}) {
|
||||
export function activate({streams, services}) {
|
||||
|
||||
bus.enableState(TOKENS.WIZARDS, []);
|
||||
bus.subscribe(TOKENS.OPEN, ({type, initialState, overridingHistory}) => {
|
||||
|
||||
let wizard = {
|
||||
type,
|
||||
initialState,
|
||||
overridingHistory,
|
||||
};
|
||||
|
||||
bus.updateState(TOKENS.WIZARDS, opened => [...opened, wizard])
|
||||
});
|
||||
streams.wizards = state([]);
|
||||
|
||||
bus.subscribe(TOKENS.CLOSE, wizard => {
|
||||
bus.updateState(TOKENS.WIZARDS, opened => opened.filter(w => w !== wizard));
|
||||
});
|
||||
services.wizard = {
|
||||
|
||||
open: ({type, initialState, overridingHistory}) => {
|
||||
|
||||
let wizard = {
|
||||
type,
|
||||
initialState,
|
||||
overridingHistory,
|
||||
};
|
||||
|
||||
streams.wizards.update(opened => [...opened, wizard]);
|
||||
},
|
||||
|
||||
close: wizard => {
|
||||
streams.wizards.update(opened => opened.filter(w => w !== wizard));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const TOKENS = {
|
||||
WIZARDS: createToken('wizards'),
|
||||
OPEN: createToken('wizards', 'open'),
|
||||
CLOSE: createToken('wizards', 'close'),
|
||||
PARAMS: createToken('wizardParams')
|
||||
};
|
||||
|
|
@ -1,24 +1,23 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Stack from 'ui/components/Stack';
|
||||
import connect from 'ui/connectLegacy';
|
||||
import connect from 'ui/connect';
|
||||
import Fa from 'ui/components/Fa';
|
||||
import ImgIcon from 'ui/components/ImgIcon';
|
||||
import ls from './OperationHistory.less';
|
||||
import cx from 'classnames';
|
||||
import ButtonGroup from 'ui/components/controls/ButtonGroup';
|
||||
import Button from 'ui/components/controls/Button';
|
||||
import {finishHistoryEditing, removeAndDropDependants} from '../../craft/craftHistoryUtils';
|
||||
import mapContext from 'ui/mapContext';
|
||||
import decoratorChain from 'ui/decoratorChain';
|
||||
|
||||
import {TOKENS as CRAFT_TOKENS} from '../../craft/craftPlugin';
|
||||
import ButtonGroup from '../../../../../modules/ui/components/controls/ButtonGroup';
|
||||
import Button from '../../../../../modules/ui/components/controls/Button';
|
||||
import {removeAndDropDependants} from '../../craft/craftHistoryUtils';
|
||||
|
||||
function OperationHistory({history, pointer, setHistoryPointer, remove}, {services: {operation: operationService}}) {
|
||||
function OperationHistory({history, pointer, setHistoryPointer, remove, operationRegistry}) {
|
||||
let lastMod = history.length - 1;
|
||||
return <Stack>
|
||||
|
||||
{history.map(({type, params}, index) => {
|
||||
|
||||
let {appearance, paramsInfo} = getDescriptor(type, operationService.registry);
|
||||
let {appearance, paramsInfo} = getDescriptor(type, operationRegistry);
|
||||
return <div key={index} onClick={() => setHistoryPointer(index - 1)}
|
||||
className={cx(ls.item, pointer + 1 === index && ls.selected)}>
|
||||
{appearance && <ImgIcon url={appearance.icon32} size={16}/>}
|
||||
|
|
@ -45,13 +44,12 @@ function getDescriptor(type, registry) {
|
|||
return descriptor;
|
||||
}
|
||||
|
||||
OperationHistory.contextTypes = {
|
||||
services: PropTypes.object
|
||||
};
|
||||
export default decoratorChain(
|
||||
connect(streams => streams.craft.modifications),
|
||||
mapContext(({streams, services}) => ({
|
||||
remove: atIndex => streams.craft.modifications.update(modifications => removeAndDropDependants(modifications, atIndex)),
|
||||
cancel: () => streams.craft.modifications.update(modifications => finishHistoryEditing(modifications)),
|
||||
operationRegistry: services.operation.registry
|
||||
}))
|
||||
)(OperationHistory);
|
||||
|
||||
export default connect(OperationHistory, CRAFT_TOKENS.MODIFICATIONS, {
|
||||
mapActions: ({setState, updateState}) => ({
|
||||
setHistoryPointer: pointer => setState(CRAFT_TOKENS.MODIFICATIONS, {pointer}),
|
||||
remove: atIndex => updateState(CRAFT_TOKENS.MODIFICATIONS, modifications => removeAndDropDependants(modifications, atIndex))
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {state} from '../../../../../modules/lstream';
|
||||
|
||||
export function activate({bus, services, streams}) {
|
||||
export function activate({services, streams}) {
|
||||
|
||||
streams.ui.menu = {
|
||||
all: state([]),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {state} from 'lstream';
|
||||
|
||||
export function activate({bus, streams}) {
|
||||
export function activate({streams}) {
|
||||
|
||||
streams.ui = {
|
||||
controlBars: {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,23 @@
|
|||
import {createToken} from '../../../../modules/bus';
|
||||
import {state} from '../../../../modules/lstream';
|
||||
|
||||
export function activate({bus, services}) {
|
||||
export function activate({streams, services}) {
|
||||
const startTime = performance.now();
|
||||
bus.enableState(APP_READY_TOKEN, false);
|
||||
bus.enableState(APP_PROJECT_LOADED, false);
|
||||
streams.lifecycle = {
|
||||
appReady: state(false),
|
||||
projectLoaded: state(false)
|
||||
};
|
||||
services.lifecycle = {
|
||||
loadProjectRequest: () => {
|
||||
if (bus.state[APP_READY_TOKEN] && !bus.state[APP_PROJECT_LOADED] && services.craftEngines.allEnginesReady()) {
|
||||
if (streams.lifecycle.appReady.value &&
|
||||
!streams.lifecycle.projectLoaded.value &&
|
||||
services.craftEngines.allEnginesReady()) {
|
||||
|
||||
services.project.load();
|
||||
bus.dispatch(APP_PROJECT_LOADED, true);
|
||||
streams.lifecycle.projectLoaded.value = true;
|
||||
const onLoadTime = performance.now();
|
||||
console.log("project loaded, took: " + ((onLoadTime - startTime) / 1000).toFixed(2) + ' sec');
|
||||
}
|
||||
}
|
||||
},
|
||||
declareAppReady: () => streams.lifecycle.appReady.value = true
|
||||
}
|
||||
}
|
||||
|
||||
export const APP_READY_TOKEN = createToken('app', 'ready');
|
||||
export const APP_PROJECT_LOADED = createToken('app', 'projectLoaded');
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ import * as SketcherPlugin from '../sketch/sketcherPlugin';
|
|||
import * as tpiPlugin from '../tpi/tpiPlugin';
|
||||
|
||||
import * as PartModellerPlugin from '../part/partModellerPlugin';
|
||||
import * as ViewSyncPlugin from '../scene/viewSyncPlugin';
|
||||
|
||||
import context from 'context';
|
||||
|
||||
import startReact from "../dom/startReact";
|
||||
import {APP_READY_TOKEN} from './lifecyclePlugin';
|
||||
|
||||
export default function startApplication(callback) {
|
||||
|
||||
|
|
@ -41,9 +41,8 @@ export default function startApplication(callback) {
|
|||
WizardPlugin,
|
||||
CraftEnginesPlugin,
|
||||
OperationPlugin,
|
||||
CadRegistryPlugin,
|
||||
CraftPlugin,
|
||||
SketcherPlugin,
|
||||
CadRegistryPlugin,
|
||||
tpiPlugin
|
||||
];
|
||||
|
||||
|
|
@ -52,14 +51,16 @@ export default function startApplication(callback) {
|
|||
ScenePlugin,
|
||||
PickControlPlugin,
|
||||
SelectionMarkerPlugin,
|
||||
SketcherPlugin,
|
||||
...applicationPlugins,
|
||||
ViewSyncPlugin
|
||||
];
|
||||
|
||||
activatePlugins(preUIPlugins, context);
|
||||
|
||||
startReact(context, () => {
|
||||
activatePlugins(plugins, context);
|
||||
context.bus.dispatch(APP_READY_TOKEN, true);
|
||||
context.services.lifecycle.declareAppReady();
|
||||
context.services.viewer.render();
|
||||
callback(context);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export function Revolve(app, params) {
|
|||
|
||||
const face = app.findFace(params.face);
|
||||
const solid = face.solid;
|
||||
const surface = face.surface();
|
||||
const surface = face.surface;
|
||||
|
||||
const sketch = ReadSketchFromFace(app, face);
|
||||
const pivot = evalPivot(params.pivot, sketch, surface);
|
||||
|
|
|
|||
14
web/app/cad/model/medge.js
Normal file
14
web/app/cad/model/medge.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import {MObject} from './mobject';
|
||||
|
||||
export class MEdge extends MObject {
|
||||
|
||||
static TYPE = 'edge';
|
||||
|
||||
constructor(id, shell, brepEdge) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.shell = shell;
|
||||
this.brepEdge = brepEdge;
|
||||
}
|
||||
|
||||
}
|
||||
97
web/app/cad/model/mface.js
Normal file
97
web/app/cad/model/mface.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import {MObject} from './mobject';
|
||||
import Vector from 'math/vector';
|
||||
import {BasisForPlane} from '../../math/l3space';
|
||||
import {MSketchObject} from './msketchObject';
|
||||
import {EMPTY_ARRAY} from 'gems/iterables';
|
||||
|
||||
export class MFace extends MObject {
|
||||
|
||||
static TYPE = 'face';
|
||||
|
||||
constructor(id, shell, surface) {
|
||||
super(id);
|
||||
this.id = id;
|
||||
this.shell = shell;
|
||||
this.surface = surface;
|
||||
this.sketchObjects = [];
|
||||
}
|
||||
|
||||
normal() {
|
||||
return this.surface.normalInMiddle();
|
||||
}
|
||||
|
||||
depth() {
|
||||
return this.surface.tangentPlaneInMiddle().w;
|
||||
}
|
||||
|
||||
calcBasis() {
|
||||
return BasisForPlane(this.normal());
|
||||
};
|
||||
|
||||
basis() {
|
||||
if (!this._basis) {
|
||||
this._basis = this.calcBasis();
|
||||
}
|
||||
return this._basis;
|
||||
}
|
||||
|
||||
setSketch(sketch) {
|
||||
this.sketch = sketch;
|
||||
this.sketchObjects = [];
|
||||
|
||||
const addSketchObjects = sketchObjects => {
|
||||
let isConstruction = sketchObjects === sketch.constructionSegments;
|
||||
for (let sketchObject of sketchObjects) {
|
||||
let mSketchObject = new MSketchObject(this, sketchObject);
|
||||
mSketchObject.construction = isConstruction;
|
||||
this.sketchObjects.push(mSketchObject);
|
||||
}
|
||||
};
|
||||
addSketchObjects(sketch.constructionSegments);
|
||||
addSketchObjects(sketch.connections);
|
||||
addSketchObjects(sketch.loops);
|
||||
}
|
||||
|
||||
findSketchObjectById(sketchObjectId) {
|
||||
for (let o of this.sketchObjects) {
|
||||
if (o.id === sketchObjectId) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBounds() {
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
get sketchToWorldTransformation() {
|
||||
if (!this._sketchToWorldTransformation) {
|
||||
this._sketchToWorldTransformation = this.surface.tangentPlaneInMiddle().get3DTransformation();
|
||||
}
|
||||
return this._sketchToWorldTransformation;
|
||||
}
|
||||
|
||||
get worldToSketchTransformation() {
|
||||
if (!this._worldToSketchTransformation) {
|
||||
this._worldToSketchTransformation = this.sketchToWorldTransformation.invert();
|
||||
}
|
||||
return this._worldToSketchTransformation;
|
||||
}
|
||||
}
|
||||
|
||||
export class MBrepFace extends MFace {
|
||||
|
||||
constructor(id, shell, brepFace) {
|
||||
super(id, shell, brepFace.surface);
|
||||
this.id = id;
|
||||
this.brepFace = brepFace;
|
||||
}
|
||||
|
||||
getBounds() {
|
||||
const bounds = [];
|
||||
for (let loop of this.brepFace.loops) {
|
||||
bounds.push(loop.asPolygon().map(p => new Vector().setV(p)));
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
8
web/app/cad/model/mobject.js
Normal file
8
web/app/cad/model/mobject.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
export class MObject {
|
||||
|
||||
id;
|
||||
ext = {}
|
||||
|
||||
}
|
||||
|
||||
15
web/app/cad/model/mopenFace.js
Normal file
15
web/app/cad/model/mopenFace.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import {MShell} from './mshell';
|
||||
import {MFace} from './mface';
|
||||
|
||||
export class MOpenFaceShell extends MShell {
|
||||
|
||||
constructor(surface) {
|
||||
super();
|
||||
this.faces.push(new MFace(this.id + '/SURFACE', this, surface))
|
||||
}
|
||||
|
||||
get face() {
|
||||
return this.faces[0];
|
||||
}
|
||||
|
||||
}
|
||||
44
web/app/cad/model/mshell.js
Normal file
44
web/app/cad/model/mshell.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import {MObject} from './mobject';
|
||||
import {MBrepFace, MFace} from './mface';
|
||||
import {MEdge} from './medge';
|
||||
import {MVertex} from './mvertex';
|
||||
|
||||
export class MShell extends MObject {
|
||||
|
||||
static TYPE = 'shell';
|
||||
|
||||
static ID_COUNTER = 0;
|
||||
|
||||
id = 'S:' + (MShell.ID_COUNTER++);
|
||||
shell;
|
||||
faces = [];
|
||||
edges = [];
|
||||
vertices = [];
|
||||
}
|
||||
|
||||
export class MBrepShell extends MShell {
|
||||
|
||||
constructor(shell) {
|
||||
super();
|
||||
this.brepShell = shell;
|
||||
|
||||
let faceCounter = 0;
|
||||
let edgeCounter = 0;
|
||||
let vertexCounter = 0;
|
||||
|
||||
for (let brepFace of this.brepShell.faces) {
|
||||
const mFace = new MBrepFace(this.id + '/F:' + faceCounter++, this, brepFace);
|
||||
this.faces.push(mFace);
|
||||
}
|
||||
|
||||
for (let brepEdge of this.brepShell.edges) {
|
||||
const mEdge = new MEdge(this.id + '/E:' + edgeCounter++, this, brepEdge);
|
||||
this.edges.push(mEdge);
|
||||
}
|
||||
|
||||
for (let brepVertex of this.brepShell.vertices) {
|
||||
const mVertex = new MVertex(this.id + '/V:' + vertexCounter++, this, brepVertex);
|
||||
this.vertices.push(mVertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
web/app/cad/model/msketchObject.js
Normal file
15
web/app/cad/model/msketchObject.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import {MObject} from './mobject';
|
||||
|
||||
export class MSketchObject extends MObject {
|
||||
|
||||
static TYPE = 'sketchObject';
|
||||
|
||||
constructor(face, sketchPrimitive) {
|
||||
super();
|
||||
this.id = sketchPrimitive.id;
|
||||
this.face = face;
|
||||
this.sketchPrimitive = sketchPrimitive;
|
||||
this.construction = false;
|
||||
}
|
||||
|
||||
}
|
||||
14
web/app/cad/model/mvertex.js
Normal file
14
web/app/cad/model/mvertex.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import {MObject} from './mobject';
|
||||
|
||||
export class MVertex extends MObject {
|
||||
|
||||
static TYPE = 'vertex';
|
||||
|
||||
constructor(id, shell, brepVertex) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.shell = shell;
|
||||
this.brepVertex = brepVertex;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import OperationActions from '../actions/operationActions';
|
|||
import HistoryActions from '../actions/historyActions';
|
||||
import menuConfig from './menuConfig';
|
||||
|
||||
export function activate({bus, services, streams}) {
|
||||
export function activate({services, streams}) {
|
||||
streams.ui.controlBars.left.value = ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'Donate', 'GitHub'];
|
||||
streams.ui.controlBars.right.value = [
|
||||
['Info', {label: null}],
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const STORAGE_PREFIX = `${STORAGE_GLOBAL_PREFIX}.projects.`;
|
|||
|
||||
export function activate(context) {
|
||||
|
||||
const {services, bus} = context;
|
||||
const {streams, services} = context;
|
||||
|
||||
const [id, params] = parseHintsFromLocation();
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ export function activate(context) {
|
|||
|
||||
function save() {
|
||||
let data = {};
|
||||
data.history = bus.state[services.craft.TOKENS.MODIFICATIONS].history;
|
||||
data.history = streams.craft.modifications.value.history;
|
||||
services.storage.set(projectStorageKey(), JSON.stringify(data));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,8 +210,8 @@ export function runSandbox({bus, services: { viewer, cadScene, cadRegistry, tpi,
|
|||
addShellOnScene(cylinder);
|
||||
addShellOnScene(box);
|
||||
|
||||
let surfaceA = cadRegistry.findFace('0:0').surface();
|
||||
let surfaceB = cadRegistry.findFace('1:4').surface();
|
||||
let surfaceA = cadRegistry.findFace('0:0').surface;
|
||||
let surfaceB = cadRegistry.findFace('1:4').surface;
|
||||
|
||||
|
||||
let curves = surfaceIntersect(surfaceA.data, surfaceB.data);
|
||||
|
|
|
|||
|
|
@ -47,20 +47,19 @@ export function activate(context) {
|
|||
}
|
||||
|
||||
function handlePick(event) {
|
||||
raycastObjects(event, PICK_KIND.FACE | PICK_KIND.SKETCH | PICK_KIND.EDGE, (object, kind) => {
|
||||
raycastObjects(event, PICK_KIND.FACE | PICK_KIND.SKETCH | PICK_KIND.EDGE, (view, kind) => {
|
||||
let modelId = view.model.id;
|
||||
if (kind === PICK_KIND.FACE) {
|
||||
if (!selected(streams.selection.face, object.id)) {
|
||||
services.cadScene.showBasis(object.basis(), object.depth());
|
||||
streams.selection.face.next([object.id]);
|
||||
if (dispatchSelection(streams.selection.face, modelId, event)) {
|
||||
services.cadScene.showBasis(view.model.basis(), view.model.depth());
|
||||
return false;
|
||||
}
|
||||
} else if (kind === PICK_KIND.SKETCH) {
|
||||
if (!selected(streams.selection.sketchObject, object.id)) {
|
||||
streams.selection.sketchObject.next([object.id]);
|
||||
if (dispatchSelection(streams.selection.sketchObject, modelId, event)) {
|
||||
return false;
|
||||
}
|
||||
} else if (kind === PICK_KIND.EDGE) {
|
||||
if (dispatchSelection(streams.selection.edge, object.id, event)) {
|
||||
if (dispatchSelection(streams.selection.edge, modelId, event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -90,27 +89,27 @@ export function activate(context) {
|
|||
const pickers = [
|
||||
(pickResult) => {
|
||||
if (mask.is(kind, PICK_KIND.SKETCH) && pickResult.object instanceof THREE.Line) {
|
||||
let sketchObject = getAttribute(pickResult.object, 'sketchObject');
|
||||
if (sketchObject) {
|
||||
return !visitor(sketchObject, PICK_KIND.SKETCH);
|
||||
let sketchObjectV = getAttribute(pickResult.object, SKETCH_OBJECT);
|
||||
if (sketchObjectV) {
|
||||
return !visitor(sketchObjectV, PICK_KIND.SKETCH);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(pickResult) => {
|
||||
if (mask.is(kind, PICK_KIND.EDGE)) {
|
||||
let cadEdge = getAttribute(pickResult.object, 'edge');
|
||||
if (cadEdge) {
|
||||
return !visitor(cadEdge, PICK_KIND.EDGE);
|
||||
let edgeV = getAttribute(pickResult.object, EDGE);
|
||||
if (edgeV) {
|
||||
return !visitor(edgeV, PICK_KIND.EDGE);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(pickResult) => {
|
||||
if (mask.is(kind, PICK_KIND.FACE) && !!pickResult.face) {
|
||||
let sketchFace = getAttribute(pickResult.face, 'face');
|
||||
if (sketchFace) {
|
||||
return !visitor(sketchFace, PICK_KIND.FACE);
|
||||
let faceV = getAttribute(pickResult.face, FACE);
|
||||
if (faceV) {
|
||||
return !visitor(faceV, PICK_KIND.FACE);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
@ -149,6 +148,11 @@ function initStateAndServices({streams, services}) {
|
|||
});
|
||||
entitySelectApi.select = selection => selectionState.value = selection;
|
||||
});
|
||||
|
||||
//withdraw all
|
||||
streams.craft.models.attach(() => {
|
||||
Object.values(streams.selection).forEach(ss => ss.next([]))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
export const SOLID = 'solid';
|
||||
export const FACE = 'face';
|
||||
export const EDGE = 'edge';
|
||||
export const VERTEX = 'vertex';
|
||||
export const SKETCH_OBJECT = 'sketchObject';
|
||||
import {MShell} from '../model/mshell';
|
||||
import {MFace} from '../model/mface';
|
||||
import {MEdge} from '../model/medge';
|
||||
import {MVertex} from '../model/mvertex';
|
||||
import {MSketchObject} from '../model/msketchObject';
|
||||
|
||||
const ENTITIES = [SOLID, FACE, EDGE, VERTEX, SKETCH_OBJECT];
|
||||
export const SHELL = MShell.TYPE;
|
||||
export const FACE = MFace.TYPE;
|
||||
export const EDGE = MEdge.TYPE;
|
||||
export const VERTEX = MVertex.TYPE;
|
||||
export const SKETCH_OBJECT = MSketchObject.TYPE;
|
||||
|
||||
export default ENTITIES;
|
||||
export const PART_MODELING_ENTITIES = [SHELL, FACE, EDGE, VERTEX, SKETCH_OBJECT];
|
||||
export const ASSEMBLY_ENTITIES = [SHELL, FACE, EDGE, VERTEX];
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
import Viewer from './viewer';
|
||||
import CadScene from "./cadScene";
|
||||
import CadScene from './cadScene';
|
||||
import {externalState} from '../../../../modules/lstream';
|
||||
|
||||
export function activate(context) {
|
||||
let {dom} = context.services;
|
||||
export function activate({streams, services}) {
|
||||
let {dom} = services;
|
||||
|
||||
let viewer = new Viewer(context.bus, dom.viewerContainer);
|
||||
let viewer = new Viewer(dom.viewerContainer);
|
||||
|
||||
context.services.viewer = viewer;
|
||||
context.services.cadScene = new CadScene(viewer.sceneSetup.rootGroup);
|
||||
services.viewer = viewer;
|
||||
services.cadScene = new CadScene(viewer.sceneSetup.rootGroup);
|
||||
|
||||
context.bus.subscribe('scene:update', () => viewer.render());
|
||||
streams.cadScene = {
|
||||
cameraMode: externalState(() => viewer.getCameraMode(), mode => viewer.setCameraMode(mode))
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,29 @@
|
|||
import DPR from 'dpr';
|
||||
import {SelectionMarker} from './selectionMarker';
|
||||
import {SketchSelectionMarker} from './sketchSelectionMarker';
|
||||
import {EdgeSelectionMarker} from './edgeSelectionMarker';
|
||||
import {createLineMaterial} from 'scene/materials';
|
||||
import {EDGE, FACE, SKETCH_OBJECT} from '../entites';
|
||||
import {findDiff} from '../../../../../modules/gems/iterables';
|
||||
|
||||
export function activate(context) {
|
||||
new SelectionMarker(context, 0xFAFAD2, 0xFF0000, null);
|
||||
new SketchSelectionMarker(context, createLineMaterial(0xFF0000, 6 / DPR));
|
||||
new EdgeSelectionMarker(context, 0xFA8072);
|
||||
export function activate({streams, services}) {
|
||||
let selectionSync = entity => ([old, curr]) => {
|
||||
let [, toWithdraw, toMark] = findDiff(old, curr);
|
||||
toWithdraw.forEach(id => {
|
||||
let model = services.cadRegistry.findEntity(entity, id);
|
||||
if (model) {
|
||||
model.ext.view.withdraw();
|
||||
}
|
||||
});
|
||||
toMark.forEach(id => {
|
||||
let model = services.cadRegistry.findEntity(entity, id);
|
||||
if (model) {
|
||||
model.ext.view.mark();
|
||||
}
|
||||
});
|
||||
services.viewer.render();
|
||||
};
|
||||
|
||||
streams.selection.face.pairwise([]).attach(selectionSync(FACE));
|
||||
streams.selection.edge.pairwise([]).attach(selectionSync(EDGE));
|
||||
streams.selection.sketchObject.pairwise([]).attach(selectionSync(SKETCH_OBJECT));
|
||||
// new SelectionMarker(context, 0xFAFAD2, 0xFF0000, null);
|
||||
// new SketchSelectionMarker(context, createLineMaterial(0xFF0000, 6 / DPR));
|
||||
// new EdgeSelectionMarker(context, 0xFA8072);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
47
web/app/cad/scene/viewSyncPlugin.js
Normal file
47
web/app/cad/scene/viewSyncPlugin.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import * as SceneGraph from '../../../../modules/scene/sceneGraph';
|
||||
import {ShellView} from './views/shellView';
|
||||
import {getAttribute} from '../../../../modules/scene/objectData';
|
||||
import {MOpenFaceShell} from '../model/mopenFace';
|
||||
import {EDGE, FACE, SHELL, SKETCH_OBJECT} from './entites';
|
||||
import {OpenFaceShellView} from './views/openFaceView';
|
||||
import {findDiff} from '../../../../modules/gems/iterables';
|
||||
|
||||
export function activate(context) {
|
||||
let {streams} = context;
|
||||
streams.cadRegistry.update.attach(sceneSynchronizer(context));
|
||||
streams.sketcher.update.attach(mFace => mFace.ext.view.updateSketch());
|
||||
}
|
||||
|
||||
function sceneSynchronizer({services: {cadScene, cadRegistry}}) {
|
||||
return function() {
|
||||
let wgChildren = cadScene.workGroup.children;
|
||||
let existent = new Set();
|
||||
for (let i = wgChildren.length - 1; i >= 0; --i) {
|
||||
let obj = wgChildren[i];
|
||||
let shellView = getAttribute(obj, SHELL);
|
||||
if (shellView) {
|
||||
let exists = cadRegistry.shellIndex.has(shellView.model.id);
|
||||
if (!exists) {
|
||||
SceneGraph.removeFromGroup(cadScene.workGroup, obj);
|
||||
shellView.dispose();
|
||||
} else {
|
||||
existent.add(shellView.shell.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let allShells = cadRegistry.getAllShells();
|
||||
|
||||
for (let shell of allShells) {
|
||||
if (!existent.has(shell.id)) {
|
||||
let shellView;
|
||||
if (shell instanceof MOpenFaceShell) {
|
||||
shellView = new OpenFaceShellView(shell);
|
||||
} else {
|
||||
shellView = new ShellView(shell);
|
||||
}
|
||||
SceneGraph.addToGroup(cadScene.workGroup, shellView.rootGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,7 @@ import SceneSetup from 'scene/sceneSetup';
|
|||
|
||||
export default class Viewer {
|
||||
|
||||
constructor(bus, container) {
|
||||
this.bus = bus;
|
||||
constructor(container) {
|
||||
this.sceneSetup = new SceneSetup(container);
|
||||
this.renderRequested = false;
|
||||
}
|
||||
|
|
@ -31,13 +30,14 @@ export default class Viewer {
|
|||
}
|
||||
|
||||
setCameraMode(mode) {
|
||||
if (this.getCameraMode() === mode) {
|
||||
return;
|
||||
}
|
||||
if (mode === CAMERA_MODE.PERSPECTIVE) {
|
||||
|
||||
this.sceneSetup.setCamera(this.sceneSetup.pCamera);
|
||||
} else {
|
||||
this.sceneSetup.setCamera(this.sceneSetup.oCamera);
|
||||
}
|
||||
this.bus.dispatch('scene_cameraMode', this.getCameraMode());
|
||||
}
|
||||
|
||||
getCameraMode() {
|
||||
|
|
|
|||
109
web/app/cad/scene/views/edgeView.js
Normal file
109
web/app/cad/scene/views/edgeView.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import {View} from './view';
|
||||
import * as vec from '../../../math/vec';
|
||||
import {setAttribute} from '../../../../../modules/scene/objectData';
|
||||
import {perpendicularVector} from '../../../math/math';
|
||||
import * as SceneGraph from '../../../../../modules/scene/sceneGraph';
|
||||
import {EDGE} from '../entites';
|
||||
|
||||
export class EdgeView extends View {
|
||||
|
||||
constructor(edge) {
|
||||
super(edge);
|
||||
this.rootGroup = SceneGraph.createGroup();
|
||||
|
||||
const doEdge = (edge, aux, width, color, opacity) => {
|
||||
const geometry = new THREE.Geometry();
|
||||
const scaleTargets = [];
|
||||
geometry.dynamic = true;
|
||||
let materialParams = {
|
||||
color,
|
||||
vertexColors: THREE.FaceColors,
|
||||
shininess: 0,
|
||||
visible: !aux,
|
||||
morphTargets: true
|
||||
};
|
||||
if (opacity !== undefined) {
|
||||
materialParams.transparent = true;
|
||||
materialParams.opacity = opacity;
|
||||
}
|
||||
let tess = edge.data.tesselation ? edge.data.tesselation : edge.curve.tessellateToData();
|
||||
let base = null;
|
||||
for (let i = 1; i < tess.length; i++) {
|
||||
|
||||
let a = tess[i - 1];
|
||||
let b = tess[i];
|
||||
let ab = vec._normalize(vec.sub(b, a));
|
||||
|
||||
let dirs = [];
|
||||
dirs[0] = perpendicularVector(ab);
|
||||
dirs[1] = vec.cross(ab, dirs[0]);
|
||||
dirs[2] = vec.negate(dirs[0]);
|
||||
dirs[3] = vec.negate(dirs[1]);
|
||||
|
||||
dirs.forEach(d => vec._mul(d, width));
|
||||
if (base === null) {
|
||||
base = dirs.map(d => vec.add(a, d));
|
||||
}
|
||||
let lid = dirs.map(d => vec.add(b, d));
|
||||
|
||||
let off = geometry.vertices.length;
|
||||
base.forEach(p => geometry.vertices.push(vThree(p)));
|
||||
lid.forEach(p => geometry.vertices.push(vThree(p)));
|
||||
|
||||
function addScaleTargets(points, origin) {
|
||||
points.forEach(p => scaleTargets.push(vThree(vec._add(vec._mul(vec.sub(p, origin), 10), origin))));
|
||||
}
|
||||
addScaleTargets(base, a);
|
||||
addScaleTargets(lid, b);
|
||||
|
||||
|
||||
base = lid;
|
||||
|
||||
[
|
||||
[0, 4, 3],
|
||||
[3, 4, 7],
|
||||
[2, 3, 7],
|
||||
[7, 6, 2],
|
||||
[0, 1, 5],
|
||||
[5, 4, 0],
|
||||
[1, 2, 6],
|
||||
[6, 5, 1],
|
||||
].forEach(([a, b, c]) => geometry.faces.push(new THREE.Face3(a + off, b + off, c + off)));
|
||||
}
|
||||
geometry.morphTargets.push( { name: "scaleTargets", vertices: scaleTargets } );
|
||||
geometry.computeFaceNormals();
|
||||
|
||||
let mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial(materialParams));
|
||||
this.rootGroup.add(mesh);
|
||||
|
||||
// mesh.morphTargetInfluences[ 0 ] = 0.2;
|
||||
return mesh;
|
||||
};
|
||||
|
||||
this.representation = doEdge(edge.brepEdge, false, 1, 0x2B3856);
|
||||
this.marker = doEdge(edge.brepEdge, true, 3, 0xFA8072, 0.8);
|
||||
|
||||
setAttribute(this.representation, EDGE, this);
|
||||
setAttribute(this.marker, EDGE, this);
|
||||
}
|
||||
|
||||
mark(color) {
|
||||
this.marker.material.visible = true;
|
||||
}
|
||||
|
||||
withdraw(color) {
|
||||
this.marker.material.visible = false;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.representation.geometry.dispose();
|
||||
this.representation.material.dispose();
|
||||
|
||||
this.marker.geometry.dispose();
|
||||
this.marker.material.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
const vThree = arr => new THREE.Vector3().fromArray(arr);
|
||||
87
web/app/cad/scene/views/faceView.js
Normal file
87
web/app/cad/scene/views/faceView.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import {setAttribute} from '../../../../../modules/scene/objectData';
|
||||
import {brepFaceToGeom, tessDataToGeom} from '../wrappers/brepSceneObject';
|
||||
import {FACE} from '../entites';
|
||||
import * as SceneGraph from '../../../../../modules/scene/sceneGraph';
|
||||
import {SketchObjectView} from './sketchObjectView';
|
||||
import {View} from './view';
|
||||
|
||||
export class SketchingView extends View {
|
||||
|
||||
constructor(face) {
|
||||
super(face);
|
||||
this.sketchGroup = SceneGraph.createGroup();
|
||||
this.sketchObjectViews = [];
|
||||
this.rootGroup = SceneGraph.createGroup();
|
||||
SceneGraph.addToGroup(this.rootGroup, this.sketchGroup);
|
||||
}
|
||||
|
||||
updateSketch() {
|
||||
SceneGraph.clearGroup(this.sketchGroup);
|
||||
this.disposeSketch();
|
||||
this.sketchObjectViews = [];
|
||||
|
||||
const sketchTr = this.model.sketchToWorldTransformation;
|
||||
for (let sketchObject of this.model.sketchObjects) {
|
||||
let sov = new SketchObjectView(sketchObject, sketchTr);
|
||||
SceneGraph.addToGroup(this.sketchGroup, sov.rootGroup);
|
||||
}
|
||||
}
|
||||
|
||||
disposeSketch() {
|
||||
for (let sov of this.sketchObjectViews) {
|
||||
sov.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.disposeSketch();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class FaceView extends SketchingView {
|
||||
|
||||
constructor(face, geometry) {
|
||||
super(face);
|
||||
this.geometry = geometry;
|
||||
this.meshFaces = [];
|
||||
let off = geometry.faces.length;
|
||||
if (face.brepFace.data.tesselation) {
|
||||
tessDataToGeom(face.brepFace.data.tesselation.data, geometry)
|
||||
} else {
|
||||
brepFaceToGeom(face, geometry);
|
||||
}
|
||||
for (let i = off; i < geometry.faces.length; i++) {
|
||||
const meshFace = geometry.faces[i];
|
||||
this.meshFaces.push(meshFace);
|
||||
setAttribute(meshFace, FACE, this);
|
||||
}
|
||||
}
|
||||
|
||||
mark(color) {
|
||||
this.setColor(color || SELECTION_COLOR);
|
||||
}
|
||||
|
||||
withdraw(color) {
|
||||
this.setColor(null);
|
||||
}
|
||||
|
||||
setColor(color) {
|
||||
setFacesColor(this.meshFaces, color);
|
||||
this.geometry.colorsNeedUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function setFacesColor(faces, color) {
|
||||
for (let face of faces) {
|
||||
if (color === null) {
|
||||
face.color.set(NULL_COLOR);
|
||||
} else {
|
||||
face.color.set( color );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const NULL_COLOR = new THREE.Color();
|
||||
export const SELECTION_COLOR = 0xFAFAD2;
|
||||
106
web/app/cad/scene/views/openFaceView.js
Normal file
106
web/app/cad/scene/views/openFaceView.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import Vector from '../../../../../modules/math/vector';
|
||||
import {setAttribute} from '../../../../../modules/scene/objectData';
|
||||
import {FACE, SHELL} from '../entites';
|
||||
import {SELECTION_COLOR, setFacesColor, SketchingView} from './faceView';
|
||||
import {View} from './view';
|
||||
|
||||
const INIT_WIDTH_H = 750 * 0.5;
|
||||
const INIT_HEIGHT_H = 750 * 0.5;
|
||||
|
||||
export const INIT_BOUNDS = [
|
||||
new Vector(-INIT_WIDTH_H, -INIT_HEIGHT_H, 0),
|
||||
new Vector( INIT_WIDTH_H, -INIT_HEIGHT_H, 0),
|
||||
new Vector( INIT_WIDTH_H, INIT_HEIGHT_H, 0),
|
||||
new Vector(-INIT_WIDTH_H, INIT_HEIGHT_H, 0)
|
||||
];
|
||||
|
||||
|
||||
export class OpenFaceShellView extends View {
|
||||
|
||||
constructor(shell) {
|
||||
super(shell);
|
||||
this.openFace = new OpenFaceView(shell.face);
|
||||
setAttribute(this.rootGroup, SHELL, this)
|
||||
}
|
||||
|
||||
get rootGroup() {
|
||||
return this.openFace.rootGroup
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.openFace.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenFaceView extends SketchingView {
|
||||
|
||||
constructor(mFace) {
|
||||
super(mFace);
|
||||
this.material = new THREE.MeshPhongMaterial({
|
||||
vertexColors: THREE.FaceColors,
|
||||
color: 0xB0C4DE,
|
||||
shininess: 0,
|
||||
polygonOffset : true,
|
||||
polygonOffsetFactor : 1,
|
||||
polygonOffsetUnits : 2,
|
||||
side : THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.5
|
||||
});
|
||||
this.updateBounds(INIT_BOUNDS);
|
||||
}
|
||||
|
||||
dropGeometry() {
|
||||
if (this.mesh) {
|
||||
this.rootGroup.remove( this.mesh );
|
||||
this.mesh.geometry.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
createGeometry() {
|
||||
const geometry = new THREE.Geometry();
|
||||
geometry.dynamic = true;
|
||||
this.bounds.forEach(v => geometry.vertices.push(v.three()));
|
||||
geometry.faces.push(new THREE.Face3(0, 1, 2));
|
||||
geometry.faces.push(new THREE.Face3(0, 2, 3));
|
||||
geometry.faces.forEach(f => setAttribute(f, FACE, this));
|
||||
geometry.computeFaceNormals();
|
||||
this.mesh = new THREE.Mesh(geometry, this.material);
|
||||
this.rootGroup.add(this.mesh);
|
||||
}
|
||||
|
||||
updateBounds(bounds2d) {
|
||||
this.dropGeometry();
|
||||
const tr = this.model.sketchToWorldTransformation;
|
||||
this.bounds = bounds2d.map(v => tr.apply(v));
|
||||
this.createGeometry();
|
||||
}
|
||||
|
||||
updateSketch() {
|
||||
super.updateSketch();
|
||||
// let bounds2d = ...
|
||||
// for (let mSketchObject of this.model.sketchObjects) {
|
||||
// mSketchObject.sketchPrimitive.tessellate(...to bounds2d)
|
||||
// }
|
||||
// this.updateBounds(bounds2d)
|
||||
}
|
||||
|
||||
mark(color) {
|
||||
this.setColor(color || SELECTION_COLOR);
|
||||
}
|
||||
|
||||
withdraw(color) {
|
||||
this.setColor(null);
|
||||
}
|
||||
|
||||
setColor(color) {
|
||||
setFacesColor(this.mesh.geometry.faces, color);
|
||||
this.mesh.geometry.colorsNeedUpdate = true;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.dropGeometry();
|
||||
this.material.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
61
web/app/cad/scene/views/shellView.js
Normal file
61
web/app/cad/scene/views/shellView.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import {View} from './view';
|
||||
import * as SceneGraph from '../../../../../modules/scene/sceneGraph';
|
||||
import {genSolidId} from '../../craft/cadRegistryPlugin';
|
||||
import {setAttribute} from '../../../../../modules/scene/objectData';
|
||||
import {createSolidMaterial} from '../wrappers/sceneObject';
|
||||
import {FaceView} from './faceView';
|
||||
import {SHELL} from '../entites';
|
||||
import {EdgeView} from './edgeView';
|
||||
|
||||
export class ShellView extends View {
|
||||
|
||||
constructor(shell, skin) {
|
||||
super(shell);
|
||||
|
||||
this.material = createSolidMaterial(skin);
|
||||
this.rootGroup = SceneGraph.createGroup();
|
||||
this.edgeGroup = SceneGraph.createGroup();
|
||||
this.vertexGroup = SceneGraph.createGroup();
|
||||
this.faceViews = [];
|
||||
this.edgeViews = [];
|
||||
this.vertexViews = [];
|
||||
|
||||
SceneGraph.addToGroup(this.rootGroup, this.edgeGroup);
|
||||
SceneGraph.addToGroup(this.rootGroup, this.vertexGroup);
|
||||
|
||||
setAttribute(this.rootGroup, SHELL, this);
|
||||
|
||||
const geometry = new THREE.Geometry();
|
||||
geometry.dynamic = true;
|
||||
this.mesh = new THREE.Mesh(geometry, this.material);
|
||||
this.rootGroup.add(this.mesh);
|
||||
|
||||
|
||||
const geom = this.mesh.geometry;
|
||||
for (let face of shell.faces) {
|
||||
const faceView = new FaceView(face, geom);
|
||||
this.faceViews.push(faceView);
|
||||
this.rootGroup.add(faceView.rootGroup);
|
||||
}
|
||||
geom.mergeVertices();
|
||||
|
||||
for (let edge of shell.edges) {
|
||||
const edgeView = new EdgeView(edge);
|
||||
SceneGraph.addToGroup(this.edgeGroup, edgeView.rootGroup);
|
||||
this.edgeViews.push(edgeView);
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
for (let faceView of this.faceViews) {
|
||||
faceView.dispose();
|
||||
}
|
||||
for (let edgeView of this.edgeViews) {
|
||||
edgeView.dispose();
|
||||
}
|
||||
for (let vertexView of this.vertexViews) {
|
||||
vertexView.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
53
web/app/cad/scene/views/sketchObjectView.js
Normal file
53
web/app/cad/scene/views/sketchObjectView.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import {View} from './view';
|
||||
import {getAttribute, setAttribute} from 'scene/objectData';
|
||||
import staticResource from 'scene/staticResource';
|
||||
import {SKETCH_OBJECT} from '../entites';
|
||||
import Vector from 'math/vector';
|
||||
import {createLineMaterial} from 'scene/materials';
|
||||
|
||||
export class SketchObjectView extends View {
|
||||
|
||||
constructor(mSketchObject, _3dTransformation) {
|
||||
super(mSketchObject);
|
||||
|
||||
let material = mSketchObject.construction ? SKETCH_CONSTRUCTION_MATERIAL : SKETCH_MATERIAL;
|
||||
let line = new THREE.Line(new THREE.Geometry(), material);
|
||||
setAttribute(line, SKETCH_OBJECT, this);
|
||||
const chunks = mSketchObject.sketchPrimitive.tessellate(10);
|
||||
function addLine(p, q) {
|
||||
const lg = line.geometry;
|
||||
const a = _3dTransformation.apply(chunks[p]);
|
||||
const b = _3dTransformation.apply(chunks[q]);
|
||||
|
||||
lg.vertices.push(a._plus(OFF_LINES_VECTOR).three());
|
||||
lg.vertices.push(b._plus(OFF_LINES_VECTOR).three());
|
||||
}
|
||||
for (let q = 1; q < chunks.length; q ++) {
|
||||
addLine(q - 1, q);
|
||||
}
|
||||
|
||||
this.rootGroup = line;
|
||||
}
|
||||
|
||||
mark(color) {
|
||||
let line = this.rootGroup;
|
||||
setAttribute(line, 'selection.defaultMaterial', line.material);
|
||||
line.material = SKETCH_SELECTION_MATERIAL;
|
||||
}
|
||||
|
||||
withdraw(color) {
|
||||
let line = this.rootGroup;
|
||||
line.material = getAttribute(line, 'selection.defaultMaterial');
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.rootGroup.geometry.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
const OFF_LINES_VECTOR = new Vector();//normal.multiply(0); // disable it. use polygon offset feature of material
|
||||
|
||||
const SKETCH_MATERIAL = staticResource(createLineMaterial(0xFFFFFF, 3));
|
||||
const SKETCH_CONSTRUCTION_MATERIAL = staticResource(createLineMaterial(0x777777, 2));
|
||||
const SKETCH_SELECTION_MATERIAL = staticResource(createLineMaterial(0xFF0000, 6));
|
||||
10
web/app/cad/scene/views/vertexView.js
Normal file
10
web/app/cad/scene/views/vertexView.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import {View} from './view';
|
||||
|
||||
export class VertexView extends View {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
23
web/app/cad/scene/views/view.js
Normal file
23
web/app/cad/scene/views/view.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
export class View {
|
||||
|
||||
constructor(model) {
|
||||
this.model = model;
|
||||
model.ext.view = this;
|
||||
}
|
||||
|
||||
setVisible(value) {
|
||||
}
|
||||
|
||||
mark(color) {
|
||||
|
||||
}
|
||||
|
||||
withdraw() {
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.model.ext.view = null;
|
||||
this.model = null;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import {readBrep} from '../../../brep/io/brepIO';
|
||||
import {BREPSceneSolid} from './brepSceneObject';
|
||||
import {MBrepShell} from '../../model/mshell';
|
||||
|
||||
export function readShellEntityFromJson(data) {
|
||||
return new BREPSceneSolid(readBrep(data));
|
||||
return new MBrepShell(readBrep(data));
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import {getAttribute} from '../../../../../modules/scene/objectData';
|
|||
export class SceneSolid {
|
||||
|
||||
constructor(type, id, skin) {
|
||||
this.tCadType = type || 'SOLID';
|
||||
this.tCadType = type || 'SHELL';
|
||||
|
||||
this.cadGroup = new THREE.Object3D();
|
||||
setAttribute(this.cadGroup, 'shell', this);
|
||||
|
|
@ -132,7 +132,7 @@ export class SceneFace {
|
|||
let line = new THREE.Line(new THREE.Geometry(), material);
|
||||
let sceneSketchObject = new SceneSketchObject(sketchObject, line);
|
||||
setAttribute(line, 'sketchObject', sceneSketchObject);
|
||||
const chunks = sketchObject.approximate(10);
|
||||
const chunks = sketchObject.tessellate(10);
|
||||
function addLine(p, q) {
|
||||
const lg = line.geometry;
|
||||
const a = _3dTransformation.apply(chunks[p]);
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@ class SketchPrimitive {
|
|||
this.inverted = !this.inverted;
|
||||
}
|
||||
|
||||
approximate(resolution) {
|
||||
const approximation = this.approximateImpl(resolution);
|
||||
tessellate(resolution) {
|
||||
const tessellation = this.tessellateImpl(resolution);
|
||||
if (this.inverted) {
|
||||
approximation.reverse();
|
||||
tessellation.reverse();
|
||||
}
|
||||
return approximation;
|
||||
return tessellation;
|
||||
}
|
||||
|
||||
isCurve() {
|
||||
|
|
@ -45,6 +45,10 @@ class SketchPrimitive {
|
|||
toVerbNurbs(plane, _3dtr) {
|
||||
throw 'not implemented'
|
||||
}
|
||||
|
||||
tessellateImpl() {
|
||||
throw 'not implemented'
|
||||
}
|
||||
}
|
||||
|
||||
export class Segment extends SketchPrimitive {
|
||||
|
|
@ -54,7 +58,7 @@ export class Segment extends SketchPrimitive {
|
|||
this.b = b;
|
||||
}
|
||||
|
||||
approximateImpl(resolution) {
|
||||
tessellateImpl(resolution) {
|
||||
return [this.a, this.b];
|
||||
}
|
||||
|
||||
|
|
@ -71,11 +75,11 @@ export class Arc extends SketchPrimitive {
|
|||
this.c = c;
|
||||
}
|
||||
|
||||
approximateImpl(resolution) {
|
||||
return Arc.approximateArc(this.a, this.b, this.c, resolution);
|
||||
tessellateImpl(resolution) {
|
||||
return Arc.tessellateArc(this.a, this.b, this.c, resolution);
|
||||
}
|
||||
|
||||
static approximateArc(ao, bo, c, resolution) {
|
||||
static tessellateArc(ao, bo, c, resolution) {
|
||||
var a = ao.minus(c);
|
||||
var b = bo.minus(c);
|
||||
var points = [ao];
|
||||
|
|
@ -129,7 +133,7 @@ export class BezierCurve extends SketchPrimitive {
|
|||
this.cp2 = cp2;
|
||||
}
|
||||
|
||||
approximateImpl(resolution) {
|
||||
tessellateImpl(resolution) {
|
||||
return LUT(this.a, this.b, this.cp1, this.cp2, 10);
|
||||
}
|
||||
}
|
||||
|
|
@ -144,11 +148,11 @@ export class EllipticalArc extends SketchPrimitive {
|
|||
this.r = r;
|
||||
}
|
||||
|
||||
approximateImpl(resolution) {
|
||||
return EllipticalArc.approxEllipticalArc(this.ep1, this.ep2, this.a, this.b, this.r, resolution);
|
||||
tessellateImpl(resolution) {
|
||||
return EllipticalArc.tessEllipticalArc(this.ep1, this.ep2, this.a, this.b, this.r, resolution);
|
||||
}
|
||||
|
||||
static approxEllipticalArc(ep1, ep2, ao, bo, radiusY, resolution) {
|
||||
static tessEllipticalArc(ep1, ep2, ao, bo, radiusY, resolution) {
|
||||
const axisX = ep2.minus(ep1);
|
||||
const radiusX = axisX.length() * 0.5;
|
||||
axisX._normalize();
|
||||
|
|
@ -187,11 +191,11 @@ export class Circle extends SketchPrimitive {
|
|||
this.r = r;
|
||||
}
|
||||
|
||||
approximateImpl(resolution) {
|
||||
return Circle.approxCircle(this.c, this.r, resolution);
|
||||
tessellateImpl(resolution) {
|
||||
return Circle.tessCircle(this.c, this.r, resolution);
|
||||
}
|
||||
|
||||
static approxCircle(c, r, resolution) {
|
||||
static tessCircle(c, r, resolution) {
|
||||
var points = [];
|
||||
resolution = 1;
|
||||
//var step = Math.acos(1 - ((resolution * resolution) / (2 * r * r)));
|
||||
|
|
@ -220,8 +224,8 @@ export class Ellipse extends SketchPrimitive {
|
|||
this.r = r;
|
||||
}
|
||||
|
||||
approximateImpl(resolution) {
|
||||
return EllipticalArc.approxEllipticalArc(this.ep1, this.ep2, this.ep1, this.ep1, this.r, resolution);
|
||||
tessellateImpl(resolution) {
|
||||
return EllipticalArc.tessEllipticalArc(this.ep1, this.ep2, this.ep1, this.ep1, this.r, resolution);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +239,7 @@ export class Contour {
|
|||
this.segments.push(obj);
|
||||
}
|
||||
|
||||
approximateOnSurface(surface) {
|
||||
tessellateOnSurface(surface) {
|
||||
const cc = new CompositeCurve();
|
||||
const tr = to3DTrFunc(surface);
|
||||
|
||||
|
|
@ -243,25 +247,25 @@ export class Contour {
|
|||
let firstPoint = null;
|
||||
for (let segIdx = 0; segIdx < this.segments.length; ++segIdx) {
|
||||
let segment = this.segments[segIdx];
|
||||
let approximation = segment.approximate(RESOLUTION);
|
||||
let tessellation = segment.tessellate(RESOLUTION);
|
||||
|
||||
approximation = approximation.map(p => tr(p));
|
||||
tessellation = tessellation.map(p => tr(p));
|
||||
|
||||
const n = approximation.length;
|
||||
prev = prev == null ? approximation[0] : prev;
|
||||
approximation[0] = prev; // this magic is to keep identity of same vectors
|
||||
if (firstPoint == null) firstPoint = approximation[0];
|
||||
const n = tessellation.length;
|
||||
prev = prev == null ? tessellation[0] : prev;
|
||||
tessellation[0] = prev; // this magic is to keep identity of same vectors
|
||||
if (firstPoint == null) firstPoint = tessellation[0];
|
||||
|
||||
if (segIdx == this.segments.length - 1) {
|
||||
approximation[n - 1] = firstPoint;
|
||||
tessellation[n - 1] = firstPoint;
|
||||
}
|
||||
|
||||
cc.add(segment.toNurbs(surface), prev, segment);
|
||||
prev = approximation[n - 1];
|
||||
prev = tessellation[n - 1];
|
||||
|
||||
//It might be an optimization for segments
|
||||
// for (let i = 1; i < n; ++i) {
|
||||
// const curr = approximation[i];
|
||||
// const curr = tessellation[i];
|
||||
// cc.add(new Line.fromSegment(prev, curr), prev, segment);
|
||||
// prev = curr;
|
||||
// }
|
||||
|
|
@ -281,20 +285,20 @@ export class Contour {
|
|||
return cc;
|
||||
}
|
||||
|
||||
approximate(resolution) {
|
||||
const approximation = [];
|
||||
tessellate(resolution) {
|
||||
const tessellation = [];
|
||||
for (let segment of this.segments) {
|
||||
const segmentApproximation = segment.approximate(resolution);
|
||||
const segmentTessellation = segment.tessellate(resolution);
|
||||
//skip last one cuz it's guaranteed to be closed
|
||||
for (let i = 0; i < segmentApproximation.length - 1; ++i) {
|
||||
approximation.push(segmentApproximation[i]);
|
||||
for (let i = 0; i < segmentTessellation.length - 1; ++i) {
|
||||
tessellation.push(segmentTessellation[i]);
|
||||
}
|
||||
}
|
||||
return approximation;
|
||||
return tessellation;
|
||||
}
|
||||
|
||||
isCCW() {
|
||||
return isCCW(this.approximate(10));
|
||||
return isCCW(this.tessellate(10));
|
||||
}
|
||||
|
||||
reverse() {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
|
||||
import {createToken} from 'bus';
|
||||
import {ReadSketch} from './sketchReader';
|
||||
import {getSketchBoundaries} from './sketchBoundaries';
|
||||
import {TOKENS as CRAFT_TOKENS} from '../craft/craftPlugin';
|
||||
import {stream} from 'lstream';
|
||||
|
||||
export function activate({bus, services}) {
|
||||
export function activate({streams, services}) {
|
||||
|
||||
streams.sketcher = {
|
||||
update: stream()
|
||||
};
|
||||
|
||||
services.storage.addListener(evt => {
|
||||
let prefix = services.project.sketchStorageNamespace;
|
||||
if (evt.key.indexOf(prefix) < 0) return;
|
||||
|
|
@ -33,17 +37,17 @@ export function activate({bus, services}) {
|
|||
return ReadSketch(JSON.parse(savedSketch), sketchId, true);
|
||||
}
|
||||
|
||||
function updateSketchForFace(sketchFace) {
|
||||
let sketch = readSketch(sketchFace.id);
|
||||
function updateSketchForFace(mFace) {
|
||||
let sketch = readSketch(mFace.id);
|
||||
if (sketch !== null) {
|
||||
sketchFace.updateSketch(sketch);
|
||||
bus.dispatch(TOKENS.SKETCH_UPDATE, sketchFace.id);
|
||||
mFace.setSketch(sketch);
|
||||
streams.sketcher.update.next(mFace);
|
||||
}
|
||||
}
|
||||
|
||||
function updateAllSketches() {
|
||||
let allShells = services.cadRegistry.getAllShells();
|
||||
allShells.forEach(sceneShell => sceneShell.sceneFaces.forEach(sceneFace => updateSketchForFace(sceneFace)));
|
||||
allShells.forEach(mShell => mShell.faces.forEach(mFace => updateSketchForFace(mFace)));
|
||||
services.viewer.requestRender();
|
||||
}
|
||||
|
||||
|
|
@ -66,13 +70,9 @@ export function activate({bus, services}) {
|
|||
services.appTabs.show(sceneFace.id, 'Sketch ' + sceneFace.id, 'sketcher.html#' + sketchURL);
|
||||
}
|
||||
|
||||
bus.subscribe(CRAFT_TOKENS.MODIFICATIONS, updateAllSketches);
|
||||
streams.craft.modifications.attach(updateAllSketches);
|
||||
|
||||
services.sketcher = {
|
||||
sketchFace, updateAllSketches, getAllSketches, readSketch
|
||||
}
|
||||
}
|
||||
|
||||
export const TOKENS = {
|
||||
SKETCH_UPDATE: createToken('sketcher', 'sketchUpdate')
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import TPI from './tpi';
|
||||
import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject';
|
||||
import {MBrepShell} from '../model/mshell';
|
||||
|
||||
/*
|
||||
* TPI stands for the Test Program Interface
|
||||
|
|
@ -7,7 +7,7 @@ import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject';
|
|||
export function activate({bus, services}) {
|
||||
|
||||
function addShellOnScene(shell, skin) {
|
||||
const sceneSolid = new BREPSceneSolid(shell, undefined, skin);
|
||||
const sceneSolid = new MBrepShell(shell);
|
||||
addOnScene(sceneSolid, skin);
|
||||
return sceneSolid;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue