separate loft preview from actual craft / engine layer refactoring

This commit is contained in:
Val Erastov 2019-01-15 19:18:28 -08:00
parent 6e16e39383
commit 9af681b21f
6 changed files with 356 additions and 300 deletions

View file

@ -0,0 +1,95 @@
export const BOOLEAN_TYPES = {
UNION : 1,
SUBTRACT: 2,
INTERSECT: 3
};
export const CURVE_TYPES = {
SEGMENT: 1,
B_SPLINE: 2,
CIRCLE: 3,
ARC: 4
};
export const DEFLECTION = 2;
export const E0_TOLERANCE = 1e-3;
export function singleShellRespone(oldShell, newShellData) {
if (newShellData.error) {
throw 'operation failed';
}
let consumed = [oldShell];
let created = readShellData(newShellData, consumed, oldShell.csys);
return {
consumed: consumed,
created: [created]
};
}
export function readShellData(data, consumed, csys) {
let tpi = __CAD_APP.services.tpi;
let model = new tpi.scene.readShellEntityFromJson(data, consumed, csys);
model.brepShell.data.externals.engine = 'e0';
return model;
}
export function managedByE0(mShell) {
let externals = mShell.brepShell && mShell.brepShell.data && mShell.brepShell.data.externals;
return externals && externals.engine === 'e0';
}
export function readSketchContour(contour, face) {
let tr = face.csys.outTransformation;
let path = [];
contour.segments.forEach(s => {
if (s.isCurve) {
if (s.constructor.name === 'Circle') {
const dir = face.csys.z.data();
path.push({TYPE: CURVE_TYPES.CIRCLE, c: tr.apply(s.c).data(), dir, r: s.r});
} else if (s.constructor.name === 'Arc') {
let a = s.inverted ? s.b : s.a;
let b = s.inverted ? s.a : s.b;
let tangent = tr._apply(s.c.minus(a))._cross(face.csys.z)._normalize();
if (s.inverted) {
tangent._negate();
}
path.push({
TYPE: CURVE_TYPES.ARC,
a: tr.apply(a).data(),
b: tr.apply(b).data(),
tangent: tangent.data()
});
} else {
let nurbs = s.toNurbs(face.csys).impl;
path.push(Object.assign({TYPE: CURVE_TYPES.B_SPLINE}, nurbs.serialize()));
}
} else {
let ab = [s.a, s.b];
if (s.inverted) {
ab.reverse();
}
ab = ab.map(v => tr.apply(v).data());
path.push({TYPE: CURVE_TYPES.SEGMENT, a: ab[0], b: ab[1]});
}
});
return path;
}
export function readSketch(face, request, sketcher) {
let sketch = sketcher.readSketch(face.id);
if (!sketch) throw 'illegal state';
return sketch.fetchContours().map(c => readSketchContour(c, face));
}
export function shellsToPointers(shells) {
return shells.filter(managedByE0).map(m => m.brepShell.data.externals.ptr);
}
export function writeCsys(csys, swapToY) {
return {
origin: csys.origin.data(),
normal: (swapToY ? csys.y : csys.z).data(),
xDir: csys.x.data()
};
}

View file

@ -0,0 +1,147 @@
import {
BOOLEAN_TYPES, DEFLECTION, E0_TOLERANCE, managedByE0, readShellData, readSketch, readSketchContour, shellsToPointers,
singleShellRespone,
writeCsys
} from './common';
import {callEngine} from './interact';
import {resolveExtrudeVector} from '../cutExtrude/cutExtrude';
export function boolean({type, operandsA, operandsB}) {
let engineParams = {
type: BOOLEAN_TYPES[type],
operandsA: shellsToPointers(operandsA),
operandsB: shellsToPointers(operandsB),
tolerance: E0_TOLERANCE,
deflection: DEFLECTION,
};
let data = callEngine(engineParams, Module._SPI_boolean);
let consumed = [...operandsA, ...operandsB];
return {
consumed,
created: [readShellData(data.result, consumed, operandsA[0].csys)]
}
}
export function createBox(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys),
dx: params.width,
dy: params.height,
dz: params.depth
}, params, Module._SPI_box);
}
export function createTorus(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys),
r1: params.radius,
r2: params.tube
}, params, Module._SPI_torus);
}
export function createCone(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys, true),
r1: params.radius,
r2: params.frustum,
h: params.height
}, params, Module._SPI_cone);
}
export function createCylinder(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys, true),
r: params.radius,
h: params.height,
}, params, Module._SPI_cylinder);
}
export function createSphere(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys),
r: params.radius,
}, params, Module._SPI_sphere);
}
function booleanBasedOperation(engineParams, params, impl) {
engineParams.deflection = DEFLECTION;
if (params.boolean && BOOLEAN_TYPES[params.boolean.type] > 0) {
engineParams.boolean = {
type: BOOLEAN_TYPES[params.boolean.type],
operands: shellsToPointers(params.boolean.operands),
tolerance: E0_TOLERANCE,
}
}
let data = callEngine(engineParams, impl);
let consumed = [];
if (params.boolean) {
data.consumed.forEach(ptr => {
let model = params.boolean.operands.find(m => managedByE0(m) && m.brepShell.data.externals.ptr === ptr);
if (model) {
consumed.push(model);
}
});
}
return {
consumed,
created: data.created.map(shape => readShellData(shape, consumed, params.csys))
}
}
function cutExtrude(isCut, request) {
function createExtrudeCommand(request, {cadRegistry, sketcher}, invert) {
const face = cadRegistry.findFace(request.face);
const paths = readSketch(face, request, sketcher);
return {
face,
request: {
vector: resolveExtrudeVector(cadRegistry, face, request, !invert).data(),
sketch: paths,
tolerance: E0_TOLERANCE,
deflection: DEFLECTION
}
};
}
let {request: engineReq, face} = createExtrudeCommand(request, services, isCut);
if (managedByE0(face.shell)) {
engineReq.boolean = {
type: isCut ? BOOLEAN_TYPES.SUBTRACT : BOOLEAN_TYPES.UNION,
operand: face.shell.brepShell.data.externals.ptr
}
}
let data = callEngine(engineReq, Module._SPI_extrude);
return singleShellRespone(face.shell, data);
}
export function cut(params) {
return cutExtrude(true, params);
}
export function extrude(params) {
return cutExtrude(false, params);
}
const mapLoftParams = params => ({
sections: params.sections.map(sec => readSketchContour(sec.contour, sec.face)),
tolerance: E0_TOLERANCE,
deflection: DEFLECTION
});
export function loftPreview(params) {
return callEngine(mapLoftParams(params), Module._SPI_loftPreview);
}
export function loft(params) {
let result = callEngine(mapLoftParams(params), Module._SPI_loftPreview);
throw 'unsupported';
// let consumed = [...operandsA, ...operandsB];
// return {
// consumed,
// created: [readShellData(data.result, consumed, operandsA[0].csys)]
// }
}

View file

@ -1,315 +1,19 @@
/**
* This is an internal alternative to native engine. It overrides basic 3d part design operations
*/
import {resolveExtrudeVector} from '../cutExtrude/cutExtrude';
let BOOLEAN_TYPES = {
UNION : 1,
SUBTRACT: 2,
INTERSECT: 3
};
let CURVE_TYPES = {
SEGMENT: 1,
B_SPLINE: 2,
CIRCLE: 3,
ARC: 4
};
const DEFLECTION = 2;
const TOLERANCE = 1e-3;
import * as craftMethods from './craftMethods';
import operationHandler from './operationHandler';
export function activate(ctx) {
loadWasm(ctx);
ctx.services.operation.handlers.push(operationHandler);
function booleanBasedOperation(engineParams, params, impl) {
engineParams.deflection = DEFLECTION;
if (params.boolean && BOOLEAN_TYPES[params.boolean.type] > 0) {
engineParams.boolean = {
type: BOOLEAN_TYPES[params.boolean.type],
operands: shellsToPointers(params.boolean.operands),
tolerance: TOLERANCE,
}
}
let data = callEngine(engineParams, impl);
let consumed = [];
if (params.boolean) {
data.consumed.forEach(ptr => {
let model = params.boolean.operands.find(m => managedByE0(m) && m.brepShell.data.externals.ptr === ptr);
if (model) {
consumed.push(model);
}
});
}
return {
consumed,
created: data.created.map(shape => readShellData(shape, consumed, params.csys))
}
}
ctx.services.craftEngine = {
createBox: function(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys),
dx: params.width,
dy: params.height,
dz: params.depth
}, params, Module._SPI_box);
},
createTorus: function(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys),
r1: params.radius,
r2: params.tube
}, params, Module._SPI_torus);
},
createCone: function(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys, true),
r1: params.radius,
r2: params.frustum,
h: params.height
}, params, Module._SPI_cone);
},
createCylinder: function(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys, true),
r: params.radius,
h: params.height,
}, params, Module._SPI_cylinder);
},
createSphere: function(params) {
return booleanBasedOperation({
csys: writeCsys(params.csys),
r: params.radius,
}, params, Module._SPI_sphere);
},
boolean: function({type, operandsA, operandsB}) {
let engineParams = {
type: BOOLEAN_TYPES[type],
operandsA: shellsToPointers(operandsA),
operandsB: shellsToPointers(operandsB),
tolerance: TOLERANCE,
deflection: DEFLECTION,
};
let data = callEngine(engineParams, Module._SPI_boolean);
let consumed = [...operandsA, ...operandsB];
return {
consumed,
created: [readShellData(data.result, consumed, operandsA[0].csys)]
}
},
loft: function(params) {
let engineParams = {
sections: params.sections.map(sec => readSketchContour(sec.contour, sec.face)),
preview: params.preview,
tolerance: TOLERANCE,
deflection: DEFLECTION,
};
let data = callEngine(engineParams, Module._SPI_loft);
if (params.preview) {
return data;
}
throw 'unsupported';
// let consumed = [...operandsA, ...operandsB];
// return {
// consumed,
// created: [readShellData(data.result, consumed, operandsA[0].csys)]
// }
}
...craftMethods
}
}
function shellsToPointers(shells) {
return shells.filter(managedByE0).map(m => m.brepShell.data.externals.ptr);
}
function writeCsys(csys, swapToY) {
return {
origin: csys.origin.data(),
normal: (swapToY ? csys.y : csys.z).data(),
xDir: csys.x.data()
};
}
function operationHandler(id, request, services) {
switch (id) {
case 'CUT':
case 'EXTRUDE': {
let isCut = id === 'CUT';
let {request: engineReq, face} = createExtrudeCommand(request, services, isCut);
if (managedByE0(face.shell)) {
engineReq.boolean = {
type: isCut ? BOOLEAN_TYPES.SUBTRACT : BOOLEAN_TYPES.UNION,
operand: face.shell.brepShell.data.externals.ptr
}
}
let data = callEngine(engineReq, Module._SPI_extrude);
return singleShellRespone(face.shell, data);
}
case 'REVOLVE': {
let {request: engineReq, face} = createRevolveCommand(request, services);
let data = callEngine(engineReq, Module._SPI_revolve);
return singleShellRespone(face.shell, data);
}
case 'FILLET': {
let edge = services.cadRegistry.findEdge(request.edges[0]);
let engineReq = {
deflection: DEFLECTION,
solid: edge.shell.brepShell.data.externals.ptr,
edges: request.edges.map(e => ({
edge: services.cadRegistry.findEdge(e).brepEdge.data.externals.ptr,
thickness: request.thickness
}))
};
let data = callEngine(engineReq, Module._SPI_fillet);
return singleShellRespone(edge.shell, data);
}
}
}
function singleShellRespone(oldShell, newShellData) {
if (newShellData.error) {
throw 'operation failed';
}
let consumed = [oldShell];
let created = readShellData(newShellData, consumed, oldShell.csys);
return {
consumed: consumed,
created: [created]
};
}
function readShellData(data, consumed, csys) {
let tpi = __CAD_APP.services.tpi;
let model = new tpi.scene.readShellEntityFromJson(data, consumed, csys);
model.brepShell.data.externals.engine = 'e0';
return model;
}
function managedByE0(mShell) {
let externals = mShell.brepShell && mShell.brepShell.data && mShell.brepShell.data.externals;
return externals && externals.engine === 'e0';
}
function readSketchContour(contour, face) {
let tr = face.csys.outTransformation;
let path = [];
contour.segments.forEach(s => {
if (s.isCurve) {
if (s.constructor.name === 'Circle') {
const dir = face.csys.z.data();
path.push({TYPE: CURVE_TYPES.CIRCLE, c: tr.apply(s.c).data(), dir, r: s.r});
} else if (s.constructor.name === 'Arc') {
let a = s.inverted ? s.b : s.a;
let b = s.inverted ? s.a : s.b;
let tangent = tr._apply(s.c.minus(a))._cross(face.csys.z)._normalize();
if (s.inverted) {
tangent._negate();
}
path.push({
TYPE: CURVE_TYPES.ARC,
a: tr.apply(a).data(),
b: tr.apply(b).data(),
tangent: tangent.data()
});
} else {
let nurbs = s.toNurbs(face.csys).impl;
path.push(Object.assign({TYPE: CURVE_TYPES.B_SPLINE}, nurbs.serialize()));
}
} else {
let ab = [s.a, s.b];
if (s.inverted) {
ab.reverse();
}
ab = ab.map(v => tr.apply(v).data());
path.push({TYPE: CURVE_TYPES.SEGMENT, a: ab[0], b: ab[1]});
}
});
return path;
}
function readSketch(face, request, sketcher) {
let sketch = sketcher.readSketch(face.id);
if (!sketch) throw 'illegal state';
return sketch.fetchContours().map(c => readSketchContour(c, face));
}
function createExtrudeCommand(request, {cadRegistry, sketcher}, invert) {
const face = cadRegistry.findFace(request.face);
const paths = readSketch(face, request, sketcher);
return {
face,
request: {
vector: resolveExtrudeVector(cadRegistry, face, request, !invert).data(),
sketch: paths,
tolerance: TOLERANCE,
deflection: DEFLECTION
}
};
}
function createRevolveCommand(request, {cadRegistry, sketcher}) {
const face = cadRegistry.findFace(request.face);
const paths = readSketch(face, request, sketcher);
let pivot = cadRegistry.findSketchObject(request.axis).sketchPrimitive;
let tr = face.csys.outTransformation;
let vec = __CAD_APP.services.tpi.math.vec;
let axisOrigin = tr._apply3(pivot.a.data());
let axisDir = vec._normalize(vec._sub(tr._apply3(pivot.b.data()), axisOrigin))
let res = {
face,
request: {
axisOrigin,
axisDir,
angle: request.angle / 180.0 * Math.PI,
sketch: paths,
tolerance: TOLERANCE,
deflection: DEFLECTION
}
};
if (managedByE0(face.shell) && request.boolean && BOOLEAN_TYPES[request.boolean] > 0) {
res.request.boolean = {
type: BOOLEAN_TYPES[request.boolean],
operand: face.shell.brepShell.data.externals.ptr
}
}
return res;
}
function toCString(str) {
let buffer = Module._malloc(str.length + 1);
writeAsciiToMemory(str, buffer);
return buffer;
}
function callEngine(request, engineFunc) {
let toCStringRequest = toCString(JSON.stringify(request));
engineFunc(toCStringRequest);
Module._free(toCStringRequest);
return __E0_ENGINE_EXCHANGE_VAL;
}
let __E0_ENGINE_EXCHANGE_VAL = null;
window.__E0_ENGINE_EXCHANGE = function(objStr) {
__E0_ENGINE_EXCHANGE_VAL = JSON.parse(objStr);
// let tpi = __CAD_APP.services.tpi;
// let sceneObject = new tpi.scene.UnmanagedSceneSolid(data, 'SOLID');
// tpi.addOnScene(sceneObject);
// __DEBUG__.AddTessDump(obj);
};
function instantiateEngine(importObject, callback) {
const url = '/wasm/e0/main.wasm';
WebAssembly.instantiateStreaming(fetch(url), importObject).then(results => {

View file

@ -0,0 +1,23 @@
export function toCString(str) {
let buffer = Module._malloc(str.length + 1);
writeAsciiToMemory(str, buffer);
return buffer;
}
export function callEngine(request, engineFunc) {
let toCStringRequest = toCString(JSON.stringify(request));
engineFunc(toCStringRequest);
Module._free(toCStringRequest);
return __E0_ENGINE_EXCHANGE_VAL;
}
let __E0_ENGINE_EXCHANGE_VAL = null;
window.__E0_ENGINE_EXCHANGE = function(objStr) {
__E0_ENGINE_EXCHANGE_VAL = JSON.parse(objStr);
// let tpi = __CAD_APP.services.tpi;
// let sceneObject = new tpi.scene.UnmanagedSceneSolid(data, 'SOLID');
// tpi.addOnScene(sceneObject);
// __DEBUG__.AddTessDump(obj);
};

View file

@ -0,0 +1,87 @@
import {BOOLEAN_TYPES, DEFLECTION, E0_TOLERANCE, managedByE0, readSketch, singleShellRespone} from './common';
import {callEngine} from './interact';
import {resolveExtrudeVector} from '../cutExtrude/cutExtrude';
export default function operationHandler(id, request, services) {
switch (id) {
case 'CUT':
case 'EXTRUDE': {
let isCut = id === 'CUT';
let {request: engineReq, face} = createExtrudeCommand(request, services, isCut);
if (managedByE0(face.shell)) {
engineReq.boolean = {
type: isCut ? BOOLEAN_TYPES.SUBTRACT : BOOLEAN_TYPES.UNION,
operand: face.shell.brepShell.data.externals.ptr
}
}
let data = callEngine(engineReq, Module._SPI_extrude);
return singleShellRespone(face.shell, data);
}
case 'REVOLVE': {
let {request: engineReq, face} = createRevolveCommand(request, services);
let data = callEngine(engineReq, Module._SPI_revolve);
return singleShellRespone(face.shell, data);
}
case 'FILLET': {
let edge = services.cadRegistry.findEdge(request.edges[0]);
let engineReq = {
deflection: DEFLECTION,
solid: edge.shell.brepShell.data.externals.ptr,
edges: request.edges.map(e => ({
edge: services.cadRegistry.findEdge(e).brepEdge.data.externals.ptr,
thickness: request.thickness
}))
};
let data = callEngine(engineReq, Module._SPI_fillet);
return singleShellRespone(edge.shell, data);
}
}
}
function createExtrudeCommand(request, {cadRegistry, sketcher}, invert) {
const face = cadRegistry.findFace(request.face);
const paths = readSketch(face, request, sketcher);
return {
face,
request: {
vector: resolveExtrudeVector(cadRegistry, face, request, !invert).data(),
sketch: paths,
tolerance: E0_TOLERANCE,
deflection: DEFLECTION
}
};
}
function createRevolveCommand(request, {cadRegistry, sketcher}) {
const face = cadRegistry.findFace(request.face);
const paths = readSketch(face, request, sketcher);
let pivot = cadRegistry.findSketchObject(request.axis).sketchPrimitive;
let tr = face.csys.outTransformation;
let vec = __CAD_APP.services.tpi.math.vec;
let axisOrigin = tr._apply3(pivot.a.data());
let axisDir = vec._normalize(vec._sub(tr._apply3(pivot.b.data()), axisOrigin))
let res = {
face,
request: {
axisOrigin,
axisDir,
angle: request.angle / 180.0 * Math.PI,
sketch: paths,
tolerance: E0_TOLERANCE,
deflection: DEFLECTION
}
};
if (managedByE0(face.shell) && request.boolean && BOOLEAN_TYPES[request.boolean] > 0) {
res.request.boolean = {
type: BOOLEAN_TYPES[request.boolean],
operand: face.shell.brepShell.data.externals.ptr
}
}
return res;
}

View file

@ -2,7 +2,7 @@ import {createSmoothMeshGeometryFromData} from '../../../../../modules/scene/geo
export function loftPreviewGeomProvider(params, services) {
const tessInfo = services.craftEngine.loft({
const tessInfo = services.craftEngine.loftPreview({
sections: params.sections.map(services.cadRegistry.findLoop),
preview: true
});