From 8b8142db767c4b6b632cb5c865c66d4b37d3c23f Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 27 Apr 2018 18:56:15 -0700 Subject: [PATCH] external engines support --- web/app/cad/craft/enginesPlugin.js | 45 ++++++++ web/app/cad/craft/operationPlugin.js | 13 ++- web/app/cad/debugPlugin.js | 29 +++++ web/app/cad/init/startApplication.js | 2 + web/app/cad/projectPlugin.js | 3 +- web/app/cad/scene/wrappers/brepSceneObject.js | 1 - web/app/cad/scene/wrappers/sceneObject.js | 18 +-- .../scene/wrappers/unmanagedSceneObject.js | 108 ++++++++++++++++++ web/app/cad/tpi/tpi.js | 4 + web/app/cad/tpi/tpiPlugin.js | 11 +- 10 files changed, 214 insertions(+), 20 deletions(-) create mode 100644 web/app/cad/craft/enginesPlugin.js create mode 100644 web/app/cad/scene/wrappers/unmanagedSceneObject.js diff --git a/web/app/cad/craft/enginesPlugin.js b/web/app/cad/craft/enginesPlugin.js new file mode 100644 index 00000000..22607e65 --- /dev/null +++ b/web/app/cad/craft/enginesPlugin.js @@ -0,0 +1,45 @@ +import {STORAGE_GLOBAL_PREFIX} from '../projectPlugin'; + +const ENGINES_STORAGE_PREFIX = `${STORAGE_GLOBAL_PREFIX}.CraftEngines`; + +let engines = []; + +export function activate({services}) { + let enginesStr = services.storage.get(ENGINES_STORAGE_PREFIX); + if (enginesStr) { + engines = JSON.parse(enginesStr); + } + + function registerEngine(id, url){ + engines.push({id, url}); + services.storage.set(ENGINES_STORAGE_PREFIX, JSON.stringify(engines)); + startEngine(engines[engines.length - 1]); + } + + function engineReady(id, handler) { + let engine = engines.find(e => e.id === id); + if (!engine) { + console.warn(`engine "${id}" not registered`); + } else { + engine.handler = handler; + console.info(`engine "${id}" is ready`); + } + } + + services.craftEngines = { + registerEngine, + getRegisteredEngines: () => engines, + engineReady + }; + + engines.forEach(e => e.handler = NO_OP_HANDLER); + engines.forEach(startEngine); +} + +function startEngine({id, url}) { + let engineScript = document.createElement('script'); + engineScript.setAttribute('src', url); + document.head.appendChild(engineScript); +} + +const NO_OP_HANDLER = () => null; \ No newline at end of file diff --git a/web/app/cad/craft/operationPlugin.js b/web/app/cad/craft/operationPlugin.js index fadee4ed..ee2d7ae3 100644 --- a/web/app/cad/craft/operationPlugin.js +++ b/web/app/cad/craft/operationPlugin.js @@ -22,7 +22,9 @@ export function activate(context) { }; actions.push(opAction); - registry[id] = descriptor; + registry[id] = Object.assign({}, descriptor, { + run: request => runOperation(request, descriptor, context) + }); } function registerOperations(operations) { @@ -48,3 +50,12 @@ export function activate(context) { } } +function runOperation(request, descriptor, context) { + for (let engine of context.services.craftEngines.getRegisteredEngines()) { + let handler = engine.handler(descriptor.id); + if (handler) { + return handler(request); + } + } + return descriptor.run(request) +} \ No newline at end of file diff --git a/web/app/cad/debugPlugin.js b/web/app/cad/debugPlugin.js index 0594dece..5f1de62e 100644 --- a/web/app/cad/debugPlugin.js +++ b/web/app/cad/debugPlugin.js @@ -12,6 +12,7 @@ import {toLoops} from '../brep/brep-io'; import {contributeComponent} from './dom/components/ContributedComponents'; import BrepDebuggerWindow, {BREP_DEBUG_WINDOW_VISIBLE} from '../brep/debug/debugger/BrepDebuggerWindow'; import curveTess from '../brep/geom/impl/curve/curve-tess'; +import tessellateSurface from '../brep/geom/surfaces/surfaceTess'; export function activate({bus, services}) { @@ -172,6 +173,34 @@ function addGlobalDebugActions({viewer, cadScene, cadRegistry}) { AddSurfaceNormal: (surface) => { __DEBUG__.AddNormal(surface.pointInMiddle(), surface.normalInMiddle()); }, + AddTessDump: (triangles, color) => { + const vec = arr => new THREE.Vector3().fromArray(arr); + color = color || 0xffffff; + const geometry = new THREE.Geometry(); + for (let i = 0; i < triangles.length; ++i) { + let off = geometry.vertices.length; + let tr = triangles[i], normales; + if (Array.isArray(tr)) { + normales = tr[1]; + tr = tr[0]; + if (normales.find(n => n[0] === null || n[1] === null || n[2] === null)) { + normales = undefined; + } + } + tr.forEach(p => geometry.vertices.push(vec(p))); + const face = new THREE.Face3(off, off + 1, off + 2, normales && normales.map(vec)); + geometry.faces.push(face); + } + geometry.computeFaceNormals(); + const mesh = new THREE.Mesh(geometry, createSolidMaterial({ + vertexColors: THREE.FaceColors, + color: 0xB0C4DE, + shininess: 0, + side: THREE.DoubleSide + })); + debugVolumeGroup.add(mesh); + viewer.render(); + }, HideSolids: () => { cadRegistry.getAllShells().forEach(s => s.cadGroup.traverse(o => o.visible = false)); viewer.render(); diff --git a/web/app/cad/init/startApplication.js b/web/app/cad/init/startApplication.js index 3462a66f..a60445ad 100644 --- a/web/app/cad/init/startApplication.js +++ b/web/app/cad/init/startApplication.js @@ -10,6 +10,7 @@ import * as MenuPlugin from '../dom/menu/menuPlugin'; import * as KeyboardPlugin from '../keyboard/keyboardPlugin'; import * as WizardPlugin from '../craft/wizard/wizardPlugin'; import * as OperationPlugin from '../craft/operationPlugin'; +import * as CraftEnginesPlugin from '../craft/enginesPlugin'; import * as CadRegistryPlugin from '../craft/cadRegistryPlugin'; import * as CraftPlugin from '../craft/craftPlugin'; import * as StoragePlugin from '../storagePlugin'; @@ -35,6 +36,7 @@ export default function startApplication(callback) { UiEntryPointsPlugin, KeyboardPlugin, WizardPlugin, + CraftEnginesPlugin, OperationPlugin, CadRegistryPlugin, CraftPlugin, diff --git a/web/app/cad/projectPlugin.js b/web/app/cad/projectPlugin.js index f10df874..59c5b633 100644 --- a/web/app/cad/projectPlugin.js +++ b/web/app/cad/projectPlugin.js @@ -1,7 +1,8 @@ import {setSketchPrecision} from './sketch/sketchReader'; import {runSandbox} from './sandbox'; -const STORAGE_PREFIX = "TCAD.projects."; +export const STORAGE_GLOBAL_PREFIX = 'TCAD'; +const STORAGE_PREFIX = `${STORAGE_GLOBAL_PREFIX}.projects.`; export function activate(context) { diff --git a/web/app/cad/scene/wrappers/brepSceneObject.js b/web/app/cad/scene/wrappers/brepSceneObject.js index db851250..ecc8b074 100644 --- a/web/app/cad/scene/wrappers/brepSceneObject.js +++ b/web/app/cad/scene/wrappers/brepSceneObject.js @@ -96,7 +96,6 @@ export function triangulateToThree(shell, geom) { let gIdx = 0; function addFace(face) { - face.materialIndex = gIdx++; geom.faces.push(face); } diff --git a/web/app/cad/scene/wrappers/sceneObject.js b/web/app/cad/scene/wrappers/sceneObject.js index a51eb5cd..d5794cbc 100644 --- a/web/app/cad/scene/wrappers/sceneObject.js +++ b/web/app/cad/scene/wrappers/sceneObject.js @@ -28,22 +28,14 @@ export class SceneSolid { } addLineToScene(a, b) { - var lg = new THREE.Geometry(); + let lg = new THREE.Geometry(); lg.vertices.push(a); lg.vertices.push(b); - var line = new THREE.Line(lg, WIREFRAME_MATERIAL); + let line = new THREE.Line(lg, WIREFRAME_MATERIAL); this.wireframeGroup.add(line); return line; } - - createGeometry() { - throw 'not implemented'; - } - - dropGeometry() { - throw 'not implemented'; - } - + dispose() { this.material.dispose(); this.mesh.geometry.dispose(); @@ -103,8 +95,8 @@ export class SceneFace { return this._basis; } - createMeshFace(a, b, c) { - const face = new THREE.Face3(a, b, c); + createMeshFace(a, b, c, normales) { + const face = new THREE.Face3(a, b, c, normales); this.registerMeshFace(face); return face; } diff --git a/web/app/cad/scene/wrappers/unmanagedSceneObject.js b/web/app/cad/scene/wrappers/unmanagedSceneObject.js new file mode 100644 index 00000000..45f34429 --- /dev/null +++ b/web/app/cad/scene/wrappers/unmanagedSceneObject.js @@ -0,0 +1,108 @@ +import Vector from 'math/vector'; +import {EDGE_AUX, FACE_CHUNK} from '../../../brep/stitching' +import {normalOfCCWSeq} from '../../cad-utils' +import {TriangulateFace} from '../../tess/triangulation' +import {SceneSolid, SceneFace, WIREFRAME_MATERIAL} from './sceneObject' +import brepTess from '../../tess/brep-tess' +import tessellateSurface from '../../../brep/geom/surfaces/surfaceTess'; +import NurbsSurface from '../../../brep/geom/surfaces/nurbsSurface'; + +const SMOOTH_RENDERING = false; + +export class UnmanagedSceneSolid extends SceneSolid { + + constructor(data, type, skin) { + super(type, undefined, Object.assign({side : THREE.DoubleSide}, skin)); + this.createGeometry(data); + } + + createGeometry(data) { + const geometry = new THREE.Geometry(); + geometry.dynamic = true; + this.mesh = new THREE.Mesh(geometry, this.material); + this.cadGroup.add(this.mesh); + this.createFaces(data.faces); + this.createEdges(data.faces); + this.createVertices(); + } + + createFaces(faces) { + const geom = this.mesh.geometry; + for (let faceData of faces) { + const sceneFace = new UnmanagedSceneFace(faceData, this); + this.sceneFaces.push(sceneFace); + let tessellation = faceData.tess; + const vec = arr => new THREE.Vector3().fromArray(arr); + for (let i = 0; i < tessellation.length; ++i) { + let off = geom.vertices.length; + let tr = tessellation[i], normales; + if (Array.isArray(tr)) { + if (SMOOTH_RENDERING && tr[1] && !tr[1].find(n => n[0] === null || n[1] === null || n[2] === null)) { + normales = tr[1].map(vec); + } + tr = tr[0]; + } + tr.forEach(p => geom.vertices.push(vec(p))); + if (!normales && faceData.surface.TYPE === 'PLANE') { + normales = vec(faceData.surface.normal); + } + + const face = sceneFace.createMeshFace(off, off + 1, off + 2, normales); + geom.faces.push(face); + } + } + if (!SMOOTH_RENDERING) { + geom.computeFaceNormals(); + } + //geom.mergeVertices(); + } + + createEdges(faces) { + for (let faceData of faces) { + for (let edgeData of faceData.edges) { + const line = new THREE.Line(new THREE.Geometry(), WIREFRAME_MATERIAL); + if (edgeData.tess) { + edgeData.tess.forEach(p => line.geometry.vertices.push(new THREE.Vector3().fromArray(p))); + } + this.wireframeGroup.add(line); + } + } + } + + createVertices() { + } +} + +class UnmanagedSceneFace extends SceneFace { + constructor(faceData, solid) { + super(solid, faceData.id); + this.surface = faceData.surface; + // if (this.surface.TYPE === 'B-SPLINE') { + // let s = this.surface; + // let nurbs = new NurbsSurface(verb.geom.NurbsSurface.byKnotsControlPointsWeights(s.degU, s.degV, s.knotsU, s.knotsV, s.cp, s.weights)); + // __DEBUG__.AddParametricSurface(nurbs); + // } + } + + + normal() { + return this.surface.normalInMiddle(); + } + + depth() { + return this.surface.tangentPlaneInMiddle().w; + } + + surface() { + return this.surface; + } + + getBounds() { + return [ + this.surface.southEastPoint(), + this.surface.southWestPoint(), + this.surface.northWestPoint(), + this.surface.northEastPoint() + ]; + } +} diff --git a/web/app/cad/tpi/tpi.js b/web/app/cad/tpi/tpi.js index b210e601..c23ca697 100644 --- a/web/app/cad/tpi/tpi.js +++ b/web/app/cad/tpi/tpi.js @@ -13,6 +13,7 @@ import {Point} from '../../brep/geom/point'; import BrepCurve from '../../brep/geom/curves/brepCurve'; import {Plane} from '../../brep/geom/impl/plane'; import pip from '../tess/pip'; +import {UnmanagedSceneSolid} from '../scene/wrappers/unmanagedSceneObject'; export default { brep: { @@ -29,5 +30,8 @@ export default { HalfEdge, Edge, Loop, Face, Shell, Vertex }, IO, + }, + scene: { + UnmanagedSceneSolid } } \ No newline at end of file diff --git a/web/app/cad/tpi/tpiPlugin.js b/web/app/cad/tpi/tpiPlugin.js index dc19480d..122919ae 100644 --- a/web/app/cad/tpi/tpiPlugin.js +++ b/web/app/cad/tpi/tpiPlugin.js @@ -1,6 +1,5 @@ import TPI from './tpi'; -import * as SceneGraph from 'scene/sceneGraph'; -import {BREPSceneSolid} from "../scene/wrappers/brepSceneObject"; +import {BREPSceneSolid} from '../scene/wrappers/brepSceneObject'; /* * TPI stands for the Test Program Interface @@ -9,13 +8,17 @@ export function activate({bus, services}) { function addShellOnScene(shell, skin) { const sceneSolid = new BREPSceneSolid(shell, undefined, skin); + addOnScene(sceneSolid, skin); + return sceneSolid; + } + function addOnScene(sceneSolid, skin) { services.cadRegistry.update(null, [sceneSolid]); services.viewer.render(); - return sceneSolid; } services.tpi = Object.assign({ bus, services, - addShellOnScene + addShellOnScene, + addOnScene }, TPI); } \ No newline at end of file