mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-23 17:04:00 +01:00
324 lines
9 KiB
JavaScript
324 lines
9 KiB
JavaScript
/**
|
|
* This is an internal alternative to native engine. It overrides basic 3d part design operations
|
|
*/
|
|
|
|
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;
|
|
|
|
export function activate(ctx) {
|
|
|
|
loadWasm(ctx);
|
|
|
|
ctx.services.operation.handlers.push(operationHandler);
|
|
function shellsToPointers(shells) {
|
|
return shells.filter(managedByE0).map(m => m.brepShell.data.externals.ptr);
|
|
}
|
|
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))
|
|
}
|
|
}
|
|
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)]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (managedByE0(face.shell)) {
|
|
engineReq.boolean = {
|
|
type: request.subtract ? BOOLEAN_TYPES.SUBTRACT : BOOLEAN_TYPES.UNION,
|
|
operand: face.shell.brepShell.data.externals.ptr
|
|
}
|
|
}
|
|
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 readSketch(face, request, sketcher) {
|
|
let sketch = sketcher.readSketch(face.id);
|
|
if (!sketch) throw 'illegal state';
|
|
|
|
let tr = face.csys.outTransformation;
|
|
let paths = sketch.fetchContours().map(c => {
|
|
let path = [];
|
|
c.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;
|
|
path.push({
|
|
TYPE: CURVE_TYPES.ARC,
|
|
a: tr.apply(a).data(),
|
|
b: tr.apply(b).data(),
|
|
tangent: tr._apply(a.minus(s.c))._cross(face.csys.z)._normalize()._negate().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;
|
|
});
|
|
return paths;
|
|
}
|
|
|
|
function createExtrudeCommand(request, {cadRegistry, sketcher}, invert) {
|
|
const face = cadRegistry.findFace(request.face);
|
|
const paths = readSketch(face, request, sketcher);
|
|
|
|
let val = request.value;
|
|
if (invert) {
|
|
val *= -1;
|
|
}
|
|
return {
|
|
face,
|
|
request: {
|
|
vector: face.csys.z.multiply(val).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))
|
|
|
|
return {
|
|
face,
|
|
request: {
|
|
axisOrigin,
|
|
axisDir,
|
|
angle: request.angle / 180.0 * Math.PI,
|
|
sketch: paths,
|
|
tolerance: TOLERANCE,
|
|
deflection: DEFLECTION
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
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 => {
|
|
callback(results.instance);
|
|
});
|
|
}
|
|
|
|
function loadWasm(ctx) {
|
|
ctx.services.lifecycle.startAsyncInitializingJob('e0:loader');
|
|
|
|
window.Module = {
|
|
// locateFile: function(file) {
|
|
// return SERVER_PATH + file;
|
|
// },
|
|
onRuntimeInitialized: function() {
|
|
ctx.services.lifecycle.finishAsyncInitializingJob('e0:loader');
|
|
},
|
|
instantiateWasm: function (importObject, fncReceiveInstance) {
|
|
instantiateEngine(importObject, fncReceiveInstance);
|
|
return {};
|
|
}
|
|
};
|
|
|
|
let mainScript = document.createElement('script');
|
|
mainScript.setAttribute('src', '/wasm/e0/main.js');
|
|
mainScript.setAttribute('async', 'async');
|
|
document.head.appendChild(mainScript);
|
|
}
|
|
|
|
|
|
|
|
|
|
|