brepjs 18.62.1 → 18.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/2d.cjs +6 -6
- package/dist/2d.js +6 -6
- package/dist/{blueprint-C6RmibWk.js → blueprint-B-auug94.js} +5 -5
- package/dist/{blueprint-DLu8ye65.cjs → blueprint-CnGsBG6y.cjs} +5 -5
- package/dist/{blueprintFns-Dq_Pd0wn.js → blueprintFns-Bpp0oBUG.js} +2 -2
- package/dist/{blueprintFns-DV-H9feq.cjs → blueprintFns-CocKjMHz.cjs} +2 -2
- package/dist/{blueprintSketcher-LjvOXfG9.js → blueprintSketcher-BmCyc7s1.js} +3 -3
- package/dist/{blueprintSketcher-CHXFt247.cjs → blueprintSketcher-J9bAQDhD.cjs} +3 -3
- package/dist/{boolean2D-CXgu27uZ.js → boolean2D-C-tW10ip.js} +4 -4
- package/dist/{boolean2D-Yi64P8Lm.cjs → boolean2D-zhHdr4EG.cjs} +4 -4
- package/dist/{booleanFns-BDQnkWFg.js → booleanFns-4_KnvEil.js} +4 -4
- package/dist/{booleanFns-Dc6okcNf.cjs → booleanFns-9qTQX1iM.cjs} +4 -4
- package/dist/brepjs.cjs +24 -24
- package/dist/brepjs.js +24 -24
- package/dist/{cameraFns-0-J1bY6t.js → cameraFns-5FSJmH9r.js} +2 -2
- package/dist/{cameraFns-DomX3Plu.cjs → cameraFns-Bz11Ik2z.cjs} +2 -2
- package/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/{cornerFinder-CegWolhx.js → cornerFinder-C10oNGL4.js} +1 -1
- package/dist/{cornerFinder-ClMOrRzh.cjs → cornerFinder-mD9AqCfR.cjs} +1 -1
- package/dist/{curveFns-Sd40KJvV.js → curveFns-BcS0DZLj.js} +1 -1
- package/dist/{curveFns-DbKwrIkB.cjs → curveFns-DOW3Rs1A.cjs} +1 -1
- package/dist/{drawFns-DQqbUdj_.js → drawFns-0ojbwK2X.js} +12 -12
- package/dist/{drawFns-C8SC0CG1.cjs → drawFns-B4BFzsPq.cjs} +12 -12
- package/dist/{extrudeFns-g_5oi_Z7.js → extrudeFns-CrTb7bjb.js} +1 -1
- package/dist/{extrudeFns-BE5tD51l.cjs → extrudeFns-D0IrXWsB.cjs} +1 -1
- package/dist/{faceFns-Bbix8B9u.js → faceFns-DFbEW9oz.js} +2 -2
- package/dist/{faceFns-BQTKMhAC.cjs → faceFns-FVl-7nu7.cjs} +2 -2
- package/dist/{helpers-BfHmMhUR.cjs → helpers-BqrszPEI.cjs} +6 -6
- package/dist/{helpers-Dd-L1roG.js → helpers-CIucvYwe.js} +6 -6
- package/dist/{historyFns-BarNZqzL.cjs → historyFns-B-C4Cp9C.cjs} +4 -4
- package/dist/{historyFns-B9o7aDyd.js → historyFns-h6FtvY5t.js} +4 -4
- package/dist/{importFns-lLr9YlOX.js → importFns-8wNWf6zt.js} +2 -2
- package/dist/{importFns-B5lQLt1L.cjs → importFns-Q9jZUGn_.cjs} +2 -2
- package/dist/io.cjs +2 -2
- package/dist/io.js +2 -2
- package/dist/kernel/manifold/approximations.d.ts +23 -0
- package/dist/kernel/manifold/curveDesc.d.ts +54 -0
- package/dist/kernel/manifold/kernel2dNative.d.ts +26 -0
- package/dist/kernel/manifold/kernel2dOps.d.ts +1 -1
- package/dist/kernel/manifold/nativeEdges.d.ts +37 -0
- package/dist/kernel/manifold/nativeFaces.d.ts +27 -0
- package/dist/kernel/manifold/profileOps.d.ts +20 -0
- package/dist/kernel/manifold/sweepOps.d.ts +12 -0
- package/dist/{measureFns-C4vQUvbq.cjs → measureFns-DSUJdWbH.cjs} +3 -3
- package/dist/{measureFns-Bd4JAmjS.js → measureFns-DXQSqDH0.js} +3 -3
- package/dist/measurement.cjs +1 -1
- package/dist/measurement.js +1 -1
- package/dist/{meshFns-Dpfjcbrg.cjs → meshFns-D2rcmVr_.cjs} +3 -3
- package/dist/{meshFns-DykZRUnb.js → meshFns-DZA-Hsya.js} +3 -3
- package/dist/operations.cjs +2 -2
- package/dist/operations.js +2 -2
- package/dist/{primitiveFns-CW8_7RPe.cjs → primitiveFns-B7BJjuHU.cjs} +7 -7
- package/dist/{primitiveFns-vOwZZo8w.js → primitiveFns-_A-uhxX7.js} +7 -7
- package/dist/projection.cjs +1 -1
- package/dist/projection.js +1 -1
- package/dist/query.cjs +2 -2
- package/dist/query.js +2 -2
- package/dist/{shapeFns-xQvCdTPe.js → shapeFns-Cu8myB5f.js} +2 -2
- package/dist/{shapeFns-B7G5VsUD.cjs → shapeFns-Uplvk1Jx.cjs} +2 -2
- package/dist/shapeRef.cjs +1 -1
- package/dist/shapeRef.js +1 -1
- package/dist/{shapeRefFns-DNMvu2Nc.cjs → shapeRefFns-DZS2Y-8H.cjs} +4 -4
- package/dist/{shapeRefFns-CAdRRwOu.js → shapeRefFns-Drrf8qS4.js} +4 -4
- package/dist/{shapeTypes-DVlIrfVF.cjs → shapeTypes-0gU7DOBf.cjs} +2123 -90
- package/dist/{shapeTypes-DsbDmdSa.js → shapeTypes-B2tufXyI.js} +2123 -90
- package/dist/sketching.cjs +3 -3
- package/dist/sketching.js +3 -3
- package/dist/{solidBuilders-vD_StQ_i.js → solidBuilders-3Is-YjEI.js} +2 -2
- package/dist/{solidBuilders-BPfkZU5h.cjs → solidBuilders-CdWC9FZI.cjs} +2 -2
- package/dist/{surfaceBuilders-2qnBuaeF.js → surfaceBuilders-BZOjOk6T.js} +2 -2
- package/dist/{surfaceBuilders-Bj6wi_Vf.cjs → surfaceBuilders-BjiUI8f_.cjs} +2 -2
- package/dist/text.cjs +2 -2
- package/dist/text.js +2 -2
- package/dist/{textBlueprints-WI8cd6vk.js → textBlueprints-BPi0wGxq.js} +7 -7
- package/dist/{textBlueprints-CrXC83B_.cjs → textBlueprints-Dl-qQ5Hm.cjs} +7 -7
- package/dist/{textMetrics-BkLQQDnZ.cjs → textMetrics-CDEnxSK0.cjs} +1 -1
- package/dist/{textMetrics-C4zPIVKl.js → textMetrics-yWTfUPqx.js} +1 -1
- package/dist/topology.cjs +7 -7
- package/dist/topology.js +7 -7
- package/dist/{topologyQueryFns-C1iv5CpR.cjs → topologyQueryFns-CI4gmFyi.cjs} +1 -1
- package/dist/{topologyQueryFns-CCLGa2wQ.js → topologyQueryFns-CxPWWuGB.js} +1 -1
- package/package.json +1 -1
|
@@ -1055,7 +1055,7 @@ function at(arr, i) {
|
|
|
1055
1055
|
if (v === void 0) throw new Error(`Index ${i} out of bounds`);
|
|
1056
1056
|
return v;
|
|
1057
1057
|
}
|
|
1058
|
-
function extractVertices(oc, shapes, tolerance) {
|
|
1058
|
+
function extractVertices$1(oc, shapes, tolerance) {
|
|
1059
1059
|
const vertices = [];
|
|
1060
1060
|
const meshDeflection = Math.max(tolerance, 1);
|
|
1061
1061
|
for (const shape of shapes) {
|
|
@@ -1152,7 +1152,7 @@ function reconstructBrep(oc, hullResult, tolerance) {
|
|
|
1152
1152
|
*/
|
|
1153
1153
|
function hull$1(oc, shapes, tolerance) {
|
|
1154
1154
|
if (shapes.length === 0) throw new Error("hull: no shapes provided");
|
|
1155
|
-
const vertices = extractVertices(oc, shapes, tolerance);
|
|
1155
|
+
const vertices = extractVertices$1(oc, shapes, tolerance);
|
|
1156
1156
|
if (vertices.length < 4) throw new Error("hull: fewer than 4 vertices extracted from input shapes");
|
|
1157
1157
|
const hullResult = require_occtWasmAdapter.quickHull(vertices, tolerance);
|
|
1158
1158
|
if (hullResult.faces.length < 4) throw new Error("hull: degenerate hull (fewer than 4 faces)");
|
|
@@ -2167,7 +2167,7 @@ function transformBatch$2(oc, entries) {
|
|
|
2167
2167
|
/**
|
|
2168
2168
|
* Applies a transformation matrix to a shape.
|
|
2169
2169
|
*/
|
|
2170
|
-
function transform$
|
|
2170
|
+
function transform$3(oc, shape, trsf) {
|
|
2171
2171
|
const transformer = new oc.BRepBuilderAPI_Transform_2(shape, trsf, true, false);
|
|
2172
2172
|
const result = transformer.ModifiedShape(shape);
|
|
2173
2173
|
transformer.delete();
|
|
@@ -2180,7 +2180,7 @@ function translate$2(oc, shape, x, y, z) {
|
|
|
2180
2180
|
const trsf = new oc.gp_Trsf_1();
|
|
2181
2181
|
const vec = new oc.gp_Vec_4(x, y, z);
|
|
2182
2182
|
trsf.SetTranslation_1(vec);
|
|
2183
|
-
const result = transform$
|
|
2183
|
+
const result = transform$3(oc, shape, trsf);
|
|
2184
2184
|
trsf.delete();
|
|
2185
2185
|
vec.delete();
|
|
2186
2186
|
return result;
|
|
@@ -2202,7 +2202,7 @@ function rotate$2(oc, shape, angle, axis = [
|
|
|
2202
2202
|
const dir = new oc.gp_Dir_5(...axis);
|
|
2203
2203
|
const ax1 = new oc.gp_Ax1_2(origin, dir);
|
|
2204
2204
|
trsf.SetRotation_1(ax1, angle * Math.PI / 180);
|
|
2205
|
-
const result = transform$
|
|
2205
|
+
const result = transform$3(oc, shape, trsf);
|
|
2206
2206
|
trsf.delete();
|
|
2207
2207
|
ax1.delete();
|
|
2208
2208
|
origin.delete();
|
|
@@ -2218,7 +2218,7 @@ function mirror$2(oc, shape, origin, normal) {
|
|
|
2218
2218
|
const dir = new oc.gp_Dir_5(...normal);
|
|
2219
2219
|
const ax2 = new oc.gp_Ax2_4(pnt, dir);
|
|
2220
2220
|
trsf.SetMirror_3(ax2);
|
|
2221
|
-
const result = transform$
|
|
2221
|
+
const result = transform$3(oc, shape, trsf);
|
|
2222
2222
|
trsf.delete();
|
|
2223
2223
|
ax2.delete();
|
|
2224
2224
|
pnt.delete();
|
|
@@ -2232,7 +2232,7 @@ function scale$2(oc, shape, center, factor) {
|
|
|
2232
2232
|
const trsf = new oc.gp_Trsf_1();
|
|
2233
2233
|
const pnt = new oc.gp_Pnt_3(...center);
|
|
2234
2234
|
trsf.SetScale(pnt, factor);
|
|
2235
|
-
const result = transform$
|
|
2235
|
+
const result = transform$3(oc, shape, trsf);
|
|
2236
2236
|
trsf.delete();
|
|
2237
2237
|
pnt.delete();
|
|
2238
2238
|
return result;
|
|
@@ -2280,7 +2280,7 @@ function simplify$2(oc, shape) {
|
|
|
2280
2280
|
/** Co-located factory: returns the transform slice of {@link KernelAdapter} bound to `oc`. */
|
|
2281
2281
|
function makeTransformOps$2(oc) {
|
|
2282
2282
|
return {
|
|
2283
|
-
transform: (shape, trsf) => transform$
|
|
2283
|
+
transform: (shape, trsf) => transform$3(oc, shape, trsf),
|
|
2284
2284
|
translate: (shape, x, y, z) => translate$2(oc, shape, x, y, z),
|
|
2285
2285
|
rotate: (shape, angle, axis, center) => rotate$2(oc, shape, angle, axis, center),
|
|
2286
2286
|
mirror: (shape, origin, normal) => mirror$2(oc, shape, origin, normal),
|
|
@@ -6069,7 +6069,7 @@ function makeGeometryOps$1(bk) {
|
|
|
6069
6069
|
}
|
|
6070
6070
|
//#endregion
|
|
6071
6071
|
//#region src/kernel/brepkit/transformOps.ts
|
|
6072
|
-
function transform$
|
|
6072
|
+
function transform$2(bk, shape, trsf) {
|
|
6073
6073
|
if (Array.isArray(trsf) && trsf.length === 16) return applyMatrix$1(bk, shape, trsf);
|
|
6074
6074
|
throw new Error("brepkit: transform expects a 16-element matrix array");
|
|
6075
6075
|
}
|
|
@@ -6147,7 +6147,7 @@ function transformBatch$1(bk, entries) {
|
|
|
6147
6147
|
/** Co-located factory: returns the transform slice of {@link KernelAdapter} bound to `bk`. */
|
|
6148
6148
|
function makeTransformOps$1(bk) {
|
|
6149
6149
|
return {
|
|
6150
|
-
transform: (shape, trsf) => transform$
|
|
6150
|
+
transform: (shape, trsf) => transform$2(bk, shape, trsf),
|
|
6151
6151
|
translate: (shape, x, y, z) => translate$1(bk, shape, x, y, z),
|
|
6152
6152
|
rotate: (shape, angle, axis, center) => rotate$1(bk, shape, angle, axis, center),
|
|
6153
6153
|
mirror: (shape, origin, normal) => mirror$1(bk, shape, origin, normal),
|
|
@@ -9687,7 +9687,10 @@ var REPLAYABLE_OPS = new Set([
|
|
|
9687
9687
|
"reverseShape",
|
|
9688
9688
|
"hull",
|
|
9689
9689
|
"hullFromPoints",
|
|
9690
|
-
"sewAndSolidify"
|
|
9690
|
+
"sewAndSolidify",
|
|
9691
|
+
"profileEdge",
|
|
9692
|
+
"profileWire",
|
|
9693
|
+
"profileFace"
|
|
9691
9694
|
]);
|
|
9692
9695
|
function opIsReplayable(op) {
|
|
9693
9696
|
return REPLAYABLE_OPS.has(op);
|
|
@@ -10188,7 +10191,7 @@ function scale(shape, center, factor) {
|
|
|
10188
10191
|
factor
|
|
10189
10192
|
}, nodeOf(s));
|
|
10190
10193
|
}
|
|
10191
|
-
function transform(shape, trsf) {
|
|
10194
|
+
function transform$1(shape, trsf) {
|
|
10192
10195
|
if (!Array.isArray(trsf) || trsf.length !== 16) throw new Error("manifold: transform expects a 16-element column-major matrix");
|
|
10193
10196
|
const s = asShape$3(shape);
|
|
10194
10197
|
return applyMatrix(unwrap(s), trsf, "transformShape", { matrix: [...trsf] }, nodeOf(s));
|
|
@@ -10270,7 +10273,7 @@ function transformBatch(entries) {
|
|
|
10270
10273
|
function makeTransformOps(module) {
|
|
10271
10274
|
return {
|
|
10272
10275
|
composeTransform,
|
|
10273
|
-
transform,
|
|
10276
|
+
transform: transform$1,
|
|
10274
10277
|
translate,
|
|
10275
10278
|
rotate,
|
|
10276
10279
|
mirror,
|
|
@@ -10298,65 +10301,170 @@ function multiplyMatrix(a, b) {
|
|
|
10298
10301
|
return out;
|
|
10299
10302
|
}
|
|
10300
10303
|
//#endregion
|
|
10301
|
-
//#region src/kernel/manifold/
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10304
|
+
//#region src/kernel/manifold/curveDesc.ts
|
|
10305
|
+
var TAU = 2 * Math.PI;
|
|
10306
|
+
var hypot3 = (a) => Math.hypot(a[0], a[1], a[2]);
|
|
10307
|
+
var sub3 = (a, b) => [
|
|
10308
|
+
a[0] - b[0],
|
|
10309
|
+
a[1] - b[1],
|
|
10310
|
+
a[2] - b[2]
|
|
10311
|
+
];
|
|
10312
|
+
function lineLen(d) {
|
|
10313
|
+
return hypot3(sub3(d.p2, d.p1));
|
|
10314
|
+
}
|
|
10315
|
+
function bezierAt$1(points, t) {
|
|
10316
|
+
const tmp = points.map((p) => [
|
|
10317
|
+
p[0],
|
|
10318
|
+
p[1],
|
|
10319
|
+
p[2]
|
|
10320
|
+
]);
|
|
10321
|
+
for (let k = 1; k < tmp.length; k++) for (let i = 0; i < tmp.length - k; i++) {
|
|
10322
|
+
const a = tmp[i] ?? [
|
|
10323
|
+
0,
|
|
10324
|
+
0,
|
|
10325
|
+
0
|
|
10326
|
+
];
|
|
10327
|
+
const b = tmp[i + 1] ?? [
|
|
10328
|
+
0,
|
|
10329
|
+
0,
|
|
10330
|
+
0
|
|
10331
|
+
];
|
|
10332
|
+
tmp[i] = [
|
|
10333
|
+
a[0] * (1 - t) + b[0] * t,
|
|
10334
|
+
a[1] * (1 - t) + b[1] * t,
|
|
10335
|
+
a[2] * (1 - t) + b[2] * t
|
|
10336
|
+
];
|
|
10323
10337
|
}
|
|
10338
|
+
return tmp[0] ?? [
|
|
10339
|
+
0,
|
|
10340
|
+
0,
|
|
10341
|
+
0
|
|
10342
|
+
];
|
|
10343
|
+
}
|
|
10344
|
+
function descType(d) {
|
|
10345
|
+
if (d.k === "line") return "LINE";
|
|
10346
|
+
if (d.k === "bezier") return "BEZIER";
|
|
10347
|
+
if (d.k === "helix") return "BSPLINE";
|
|
10348
|
+
return Math.abs(d.rx - d.ry) < 1e-9 * Math.max(1, d.rx) ? "CIRCLE" : "ELLIPSE";
|
|
10349
|
+
}
|
|
10350
|
+
/** Parameter bounds: line → [0, length]; conic → [a0, a1]; helix → [0, 2π·turns]; bezier → [0, 1]. */
|
|
10351
|
+
function descBounds(d) {
|
|
10352
|
+
if (d.k === "line") return {
|
|
10353
|
+
first: 0,
|
|
10354
|
+
last: lineLen(d)
|
|
10355
|
+
};
|
|
10356
|
+
if (d.k === "conic") return {
|
|
10357
|
+
first: d.a0,
|
|
10358
|
+
last: d.a1
|
|
10359
|
+
};
|
|
10360
|
+
if (d.k === "helix") return {
|
|
10361
|
+
first: 0,
|
|
10362
|
+
last: TAU * d.turns
|
|
10363
|
+
};
|
|
10324
10364
|
return {
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
makeWire: () => notImplemented("makeWire"),
|
|
10328
|
-
makeFace: () => notImplemented("makeFace"),
|
|
10329
|
-
makeLineEdge: () => notImplemented("makeLineEdge"),
|
|
10330
|
-
makeCircleEdge: () => notImplemented("makeCircleEdge"),
|
|
10331
|
-
makeCircleArc: () => notImplemented("makeCircleArc"),
|
|
10332
|
-
makeArcEdge: () => notImplemented("makeArcEdge"),
|
|
10333
|
-
makeEllipseEdge: () => notImplemented("makeEllipseEdge"),
|
|
10334
|
-
makeEllipseArc: () => notImplemented("makeEllipseArc"),
|
|
10335
|
-
makeBezierEdge: () => notImplemented("makeBezierEdge"),
|
|
10336
|
-
makeTangentArc: () => notImplemented("makeTangentArc"),
|
|
10337
|
-
makeHelixWire: () => notImplemented("makeHelixWire"),
|
|
10338
|
-
makeWireFromMixed: () => notImplemented("makeWireFromMixed"),
|
|
10339
|
-
makeCompound: () => notImplemented("makeCompound"),
|
|
10340
|
-
solidFromShell: () => notImplemented("solidFromShell"),
|
|
10341
|
-
hull,
|
|
10342
|
-
hullFromPoints,
|
|
10343
|
-
buildSolidFromFaces: () => notImplemented("buildSolidFromFaces"),
|
|
10344
|
-
makeNonPlanarFace: () => notImplemented("makeNonPlanarFace"),
|
|
10345
|
-
addHolesInFace: () => notImplemented("addHolesInFace"),
|
|
10346
|
-
removeHolesFromFace: () => notImplemented("removeHolesFromFace"),
|
|
10347
|
-
makeFaceOnSurface: () => notImplemented("makeFaceOnSurface"),
|
|
10348
|
-
bsplineSurface: (points, rows, cols) => occtOrThrow("bsplineSurface").bsplineSurface(points, rows, cols),
|
|
10349
|
-
triangulatedSurface: (points, rows, cols) => occtOrThrow("triangulatedSurface").triangulatedSurface(points, rows, cols),
|
|
10350
|
-
buildTriFace: () => notImplemented("buildTriFace"),
|
|
10351
|
-
sewAndSolidify,
|
|
10352
|
-
createPoint3d: () => notImplemented("createPoint3d"),
|
|
10353
|
-
createDirection3d: () => notImplemented("createDirection3d"),
|
|
10354
|
-
createVector3d: () => notImplemented("createVector3d"),
|
|
10355
|
-
createAxis1: () => notImplemented("createAxis1"),
|
|
10356
|
-
createAxis2: () => notImplemented("createAxis2"),
|
|
10357
|
-
createAxis3: () => notImplemented("createAxis3")
|
|
10365
|
+
first: 0,
|
|
10366
|
+
last: 1
|
|
10358
10367
|
};
|
|
10359
10368
|
}
|
|
10369
|
+
function descPointAt(d, param) {
|
|
10370
|
+
if (d.k === "line") {
|
|
10371
|
+
const t = param / (lineLen(d) || 1);
|
|
10372
|
+
return [
|
|
10373
|
+
d.p1[0] + (d.p2[0] - d.p1[0]) * t,
|
|
10374
|
+
d.p1[1] + (d.p2[1] - d.p1[1]) * t,
|
|
10375
|
+
d.p1[2] + (d.p2[2] - d.p1[2]) * t
|
|
10376
|
+
];
|
|
10377
|
+
}
|
|
10378
|
+
if (d.k === "bezier") return bezierAt$1(d.points, param);
|
|
10379
|
+
if (d.k === "helix") {
|
|
10380
|
+
const ct = Math.cos(param);
|
|
10381
|
+
const st = Math.sin(param);
|
|
10382
|
+
const z = d.pitch * param / TAU;
|
|
10383
|
+
return [
|
|
10384
|
+
d.center[0] + d.radius * (ct * d.x[0] + st * d.y[0]) + z * d.axis[0],
|
|
10385
|
+
d.center[1] + d.radius * (ct * d.x[1] + st * d.y[1]) + z * d.axis[1],
|
|
10386
|
+
d.center[2] + d.radius * (ct * d.x[2] + st * d.y[2]) + z * d.axis[2]
|
|
10387
|
+
];
|
|
10388
|
+
}
|
|
10389
|
+
const ct = Math.cos(param);
|
|
10390
|
+
const st = Math.sin(param);
|
|
10391
|
+
return [
|
|
10392
|
+
d.center[0] + d.rx * ct * d.x[0] + d.ry * st * d.y[0],
|
|
10393
|
+
d.center[1] + d.rx * ct * d.x[1] + d.ry * st * d.y[1],
|
|
10394
|
+
d.center[2] + d.rx * ct * d.x[2] + d.ry * st * d.y[2]
|
|
10395
|
+
];
|
|
10396
|
+
}
|
|
10397
|
+
function descTangent(d, param) {
|
|
10398
|
+
let t;
|
|
10399
|
+
if (d.k === "line") t = sub3(d.p2, d.p1);
|
|
10400
|
+
else if (d.k === "bezier") {
|
|
10401
|
+
const a = bezierAt$1(d.points, Math.max(0, param - 1e-4));
|
|
10402
|
+
t = sub3(bezierAt$1(d.points, Math.min(1, param + 1e-4)), a);
|
|
10403
|
+
} else if (d.k === "helix") {
|
|
10404
|
+
const ct = Math.cos(param);
|
|
10405
|
+
const st = Math.sin(param);
|
|
10406
|
+
const k = d.pitch / TAU;
|
|
10407
|
+
t = [
|
|
10408
|
+
d.radius * (-st * d.x[0] + ct * d.y[0]) + k * d.axis[0],
|
|
10409
|
+
d.radius * (-st * d.x[1] + ct * d.y[1]) + k * d.axis[1],
|
|
10410
|
+
d.radius * (-st * d.x[2] + ct * d.y[2]) + k * d.axis[2]
|
|
10411
|
+
];
|
|
10412
|
+
} else {
|
|
10413
|
+
const ct = Math.cos(param);
|
|
10414
|
+
const st = Math.sin(param);
|
|
10415
|
+
t = [
|
|
10416
|
+
-d.rx * st * d.x[0] + d.ry * ct * d.y[0],
|
|
10417
|
+
-d.rx * st * d.x[1] + d.ry * ct * d.y[1],
|
|
10418
|
+
-d.rx * st * d.x[2] + d.ry * ct * d.y[2]
|
|
10419
|
+
];
|
|
10420
|
+
}
|
|
10421
|
+
const l = hypot3(t) || 1;
|
|
10422
|
+
return [
|
|
10423
|
+
t[0] / l,
|
|
10424
|
+
t[1] / l,
|
|
10425
|
+
t[2] / l
|
|
10426
|
+
];
|
|
10427
|
+
}
|
|
10428
|
+
/** Exact length where closed-form exists; Ramanujan for full ellipses; numeric otherwise. */
|
|
10429
|
+
function descLength(d) {
|
|
10430
|
+
if (d.k === "line") return lineLen(d);
|
|
10431
|
+
if (d.k === "helix") {
|
|
10432
|
+
const c = TAU * d.radius;
|
|
10433
|
+
return d.turns * Math.sqrt(c * c + d.pitch * d.pitch);
|
|
10434
|
+
}
|
|
10435
|
+
if (d.k === "conic") {
|
|
10436
|
+
const span = Math.abs(d.a1 - d.a0);
|
|
10437
|
+
if (Math.abs(d.rx - d.ry) < 1e-9 * Math.max(1, d.rx)) return d.rx * span;
|
|
10438
|
+
if (Math.abs(span - TAU) < 1e-9) {
|
|
10439
|
+
const a = d.rx;
|
|
10440
|
+
const b = d.ry;
|
|
10441
|
+
const h = (a - b) * (a - b) / ((a + b) * (a + b));
|
|
10442
|
+
return Math.PI * (a + b) * (1 + 3 * h / (10 + Math.sqrt(4 - 3 * h)));
|
|
10443
|
+
}
|
|
10444
|
+
return numericLength((t) => descPointAt(d, t), d.a0, d.a1, 256);
|
|
10445
|
+
}
|
|
10446
|
+
return numericLength((t) => descPointAt(d, t), 0, 1, 128);
|
|
10447
|
+
}
|
|
10448
|
+
function descIsClosed(d) {
|
|
10449
|
+
if (d.k === "conic") return Math.abs(Math.abs(d.a1 - d.a0) - TAU) < 1e-9;
|
|
10450
|
+
return false;
|
|
10451
|
+
}
|
|
10452
|
+
function descIsPeriodic(d) {
|
|
10453
|
+
return descIsClosed(d);
|
|
10454
|
+
}
|
|
10455
|
+
function descPeriod(d) {
|
|
10456
|
+
return d.k === "conic" ? TAU : 0;
|
|
10457
|
+
}
|
|
10458
|
+
function numericLength(at, t0, t1, n) {
|
|
10459
|
+
let len = 0;
|
|
10460
|
+
let prev = at(t0);
|
|
10461
|
+
for (let i = 1; i <= n; i++) {
|
|
10462
|
+
const p = at(t0 + (t1 - t0) * i / n);
|
|
10463
|
+
len += hypot3(sub3(p, prev));
|
|
10464
|
+
prev = p;
|
|
10465
|
+
}
|
|
10466
|
+
return len;
|
|
10467
|
+
}
|
|
10360
10468
|
//#endregion
|
|
10361
10469
|
//#region src/kernel/manifold/approximations.ts
|
|
10362
10470
|
function asShape$2(shape) {
|
|
@@ -10444,6 +10552,10 @@ function signedArea(outline) {
|
|
|
10444
10552
|
function ensureCCW(outline) {
|
|
10445
10553
|
return signedArea(outline) < 0 ? [...outline].reverse() : outline;
|
|
10446
10554
|
}
|
|
10555
|
+
/** Force CW winding (used for holes, opposite the CCW outline). */
|
|
10556
|
+
function ensureCW(outline) {
|
|
10557
|
+
return signedArea(outline) > 0 ? [...outline].reverse() : outline;
|
|
10558
|
+
}
|
|
10447
10559
|
function readNodeParams(shape) {
|
|
10448
10560
|
return shape.node?.params;
|
|
10449
10561
|
}
|
|
@@ -10466,8 +10578,10 @@ function profileCrossSection(profile) {
|
|
|
10466
10578
|
0
|
|
10467
10579
|
];
|
|
10468
10580
|
const outline = ensureCCW(recorded.map((p) => [p[0], p[1]]));
|
|
10581
|
+
const holes = (params?.["holes"])?.filter((h) => h.length >= 3).map((h) => ensureCW(h.map((p) => [p[0], p[1]])));
|
|
10469
10582
|
if (params?.["xAxis"] && params["yAxis"]) return {
|
|
10470
10583
|
outline,
|
|
10584
|
+
holes,
|
|
10471
10585
|
origin,
|
|
10472
10586
|
xAxis: params["xAxis"],
|
|
10473
10587
|
yAxis: params["yAxis"]
|
|
@@ -10479,6 +10593,7 @@ function profileCrossSection(profile) {
|
|
|
10479
10593
|
]);
|
|
10480
10594
|
return {
|
|
10481
10595
|
outline,
|
|
10596
|
+
holes,
|
|
10482
10597
|
origin,
|
|
10483
10598
|
xAxis,
|
|
10484
10599
|
yAxis
|
|
@@ -10554,6 +10669,54 @@ function fanTriangulate(vertexCount) {
|
|
|
10554
10669
|
* in correspondence with the section's outline order. `scale` rescales the
|
|
10555
10670
|
* outline about its frame origin (for tapered/draft sweeps and scale laws).
|
|
10556
10671
|
*/
|
|
10672
|
+
/**
|
|
10673
|
+
* Upsample a closed 2D outline to exactly `n` points by KEEPING every original
|
|
10674
|
+
* vertex and inserting extra points on the longest segments (proportional to
|
|
10675
|
+
* length, largest-remainder allotment). Vertex-preserving so corners aren't
|
|
10676
|
+
* rounded off — resampling a 4-point rectangle to 4 returns it unchanged. Used
|
|
10677
|
+
* to give loft sections a common vertex count so {@link skinRings} can connect
|
|
10678
|
+
* them by index; lofting profiles of different point counts (circle ↔ rounded
|
|
10679
|
+
* rect) is otherwise impossible on the mesh kernel. `n < k` is not supported
|
|
10680
|
+
* (callers pass `n = max` count), so it never downsamples.
|
|
10681
|
+
*/
|
|
10682
|
+
function resampleClosed(outline, n) {
|
|
10683
|
+
const k = outline.length;
|
|
10684
|
+
if (k < 2 || n <= k) return outline.map((p) => [p[0], p[1]]);
|
|
10685
|
+
const seg = [];
|
|
10686
|
+
let total = 0;
|
|
10687
|
+
for (let i = 0; i < k; i++) {
|
|
10688
|
+
const a = outline[i] ?? [0, 0];
|
|
10689
|
+
const b = outline[(i + 1) % k] ?? [0, 0];
|
|
10690
|
+
const l = Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
10691
|
+
seg.push(l);
|
|
10692
|
+
total += l;
|
|
10693
|
+
}
|
|
10694
|
+
if (total === 0) return outline.map((p) => [p[0], p[1]]);
|
|
10695
|
+
const extra = n - k;
|
|
10696
|
+
const quota = seg.map((l) => extra * l / total);
|
|
10697
|
+
const add = quota.map((q) => Math.floor(q));
|
|
10698
|
+
let placed = add.reduce((s, v) => s + v, 0);
|
|
10699
|
+
const rema = quota.map((q, i) => ({
|
|
10700
|
+
i,
|
|
10701
|
+
f: q - Math.floor(q)
|
|
10702
|
+
})).sort((x, y) => y.f - x.f);
|
|
10703
|
+
for (let r = 0; placed < extra; r++, placed++) {
|
|
10704
|
+
const idx = rema[r % rema.length]?.i ?? 0;
|
|
10705
|
+
add[idx] = (add[idx] ?? 0) + 1;
|
|
10706
|
+
}
|
|
10707
|
+
const out = [];
|
|
10708
|
+
for (let i = 0; i < k; i++) {
|
|
10709
|
+
const a = outline[i] ?? [0, 0];
|
|
10710
|
+
const b = outline[(i + 1) % k] ?? [0, 0];
|
|
10711
|
+
out.push([a[0], a[1]]);
|
|
10712
|
+
const inner = add[i] ?? 0;
|
|
10713
|
+
for (let j = 1; j <= inner; j++) {
|
|
10714
|
+
const t = j / (inner + 1);
|
|
10715
|
+
out.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);
|
|
10716
|
+
}
|
|
10717
|
+
}
|
|
10718
|
+
return out;
|
|
10719
|
+
}
|
|
10557
10720
|
function placeRing(section, frame, scale = 1) {
|
|
10558
10721
|
return section.outline.map((p) => {
|
|
10559
10722
|
const lx = p[0] * scale;
|
|
@@ -10588,7 +10751,32 @@ function skinRings(module, rings) {
|
|
|
10588
10751
|
vertProperties: Float32Array.from(verts),
|
|
10589
10752
|
triVerts: Uint32Array.from(tris)
|
|
10590
10753
|
});
|
|
10591
|
-
return new module.Manifold(built);
|
|
10754
|
+
return orientPositive(module, new module.Manifold(built));
|
|
10755
|
+
}
|
|
10756
|
+
/**
|
|
10757
|
+
* Normalize a built solid to outward (positive-volume) orientation. Skinning a
|
|
10758
|
+
* profile whose section order or outline winding runs "backwards" yields an
|
|
10759
|
+
* inside-out manifold (negative volume) that booleans then mishandle — a cut
|
|
10760
|
+
* tool that won't subtract, a fuse operand that cancels volume. If the volume
|
|
10761
|
+
* is negative, rebuild with reversed triangle winding so normals face outward.
|
|
10762
|
+
*/
|
|
10763
|
+
function orientPositive(module, solid) {
|
|
10764
|
+
if (typeof solid.volume !== "function" || solid.volume() >= 0) return solid;
|
|
10765
|
+
const mesh = solid.getMesh();
|
|
10766
|
+
const tv = mesh.triVerts;
|
|
10767
|
+
for (let i = 0; i + 2 < tv.length; i += 3) {
|
|
10768
|
+
const t = tv[i + 1] ?? 0;
|
|
10769
|
+
tv[i + 1] = tv[i + 2] ?? 0;
|
|
10770
|
+
tv[i + 2] = t;
|
|
10771
|
+
}
|
|
10772
|
+
const flipped = new module.Mesh({
|
|
10773
|
+
numProp: mesh.numProp,
|
|
10774
|
+
vertProperties: mesh.vertProperties,
|
|
10775
|
+
triVerts: tv
|
|
10776
|
+
});
|
|
10777
|
+
const result = new module.Manifold(flipped);
|
|
10778
|
+
if (typeof solid.delete === "function") solid.delete();
|
|
10779
|
+
return result;
|
|
10592
10780
|
}
|
|
10593
10781
|
/** Build the triangle index list for `ringCount` rings of `m` points each. */
|
|
10594
10782
|
function skinTriangles(ringCount, m) {
|
|
@@ -10678,6 +10866,477 @@ function rotationMinimizingFrames(path, seed) {
|
|
|
10678
10866
|
return frames;
|
|
10679
10867
|
}
|
|
10680
10868
|
//#endregion
|
|
10869
|
+
//#region src/kernel/manifold/profileOps.ts
|
|
10870
|
+
/** Segments used to approximate a full circle; arcs scale by angle span. */
|
|
10871
|
+
var FULL_CIRCLE_SEGMENTS = 24;
|
|
10872
|
+
/** Bezier sampling segments per edge. */
|
|
10873
|
+
var BEZIER_SEGMENTS = 24;
|
|
10874
|
+
var ZERO3 = [
|
|
10875
|
+
0,
|
|
10876
|
+
0,
|
|
10877
|
+
0
|
|
10878
|
+
];
|
|
10879
|
+
var EPS_JOIN = 1e-6;
|
|
10880
|
+
var PLACEHOLDER = {
|
|
10881
|
+
delete: () => {},
|
|
10882
|
+
isEmpty: () => false
|
|
10883
|
+
};
|
|
10884
|
+
function at3(pts, i) {
|
|
10885
|
+
return pts[i] ?? ZERO3;
|
|
10886
|
+
}
|
|
10887
|
+
function arcSegments(angleSpan) {
|
|
10888
|
+
return Math.max(2, Math.ceil(Math.abs(angleSpan) / (2 * Math.PI) * FULL_CIRCLE_SEGMENTS));
|
|
10889
|
+
}
|
|
10890
|
+
function pickPerp(n) {
|
|
10891
|
+
return normalize3(cross(n, Math.abs(n[0]) < .9 ? [
|
|
10892
|
+
1,
|
|
10893
|
+
0,
|
|
10894
|
+
0
|
|
10895
|
+
] : [
|
|
10896
|
+
0,
|
|
10897
|
+
1,
|
|
10898
|
+
0
|
|
10899
|
+
]));
|
|
10900
|
+
}
|
|
10901
|
+
/** Sample a circular arc in the plane framed by `normal` about `center`. */
|
|
10902
|
+
function sampleArc(center, normal, radius, startAngle, endAngle, xDir) {
|
|
10903
|
+
const n = normalize3(normal);
|
|
10904
|
+
const x = xDir ? normalize3(xDir) : pickPerp(n);
|
|
10905
|
+
const y = normalize3(cross(n, x));
|
|
10906
|
+
const span = endAngle - startAngle;
|
|
10907
|
+
const segs = arcSegments(span);
|
|
10908
|
+
const pts = [];
|
|
10909
|
+
for (let i = 0; i <= segs; i++) {
|
|
10910
|
+
const a = startAngle + span * i / segs;
|
|
10911
|
+
pts.push(add(center, add(scaleVec(x, radius * Math.cos(a)), scaleVec(y, radius * Math.sin(a)))));
|
|
10912
|
+
}
|
|
10913
|
+
return pts;
|
|
10914
|
+
}
|
|
10915
|
+
/** Circular arc through three points, sampled as a polyline p1..p2..p3. */
|
|
10916
|
+
function circleFrom3(p1, p2, p3) {
|
|
10917
|
+
const v1 = sub(p2, p1);
|
|
10918
|
+
const v2 = sub(p3, p1);
|
|
10919
|
+
const n = cross(v1, v2);
|
|
10920
|
+
if (length3(n) < 1e-12) return [
|
|
10921
|
+
p1,
|
|
10922
|
+
p2,
|
|
10923
|
+
p3
|
|
10924
|
+
];
|
|
10925
|
+
const nn = normalize3(n);
|
|
10926
|
+
const b = dot(v1, v1);
|
|
10927
|
+
const c = dot(v2, v2);
|
|
10928
|
+
const d = dot(v1, v2);
|
|
10929
|
+
const denom = 2 * (b * c - d * d);
|
|
10930
|
+
if (Math.abs(denom) < 1e-18) return [
|
|
10931
|
+
p1,
|
|
10932
|
+
p2,
|
|
10933
|
+
p3
|
|
10934
|
+
];
|
|
10935
|
+
const s = c * (b - d) / denom;
|
|
10936
|
+
const t = b * (c - d) / denom;
|
|
10937
|
+
const center = add(p1, add(scaleVec(v1, s), scaleVec(v2, t)));
|
|
10938
|
+
const radius = length3(sub(p1, center));
|
|
10939
|
+
const x = normalize3(sub(p1, center));
|
|
10940
|
+
const y = normalize3(cross(nn, x));
|
|
10941
|
+
const angleOf = (p) => Math.atan2(dot(sub(p, center), y), dot(sub(p, center), x));
|
|
10942
|
+
let a3 = angleOf(p3);
|
|
10943
|
+
if (a3 < 0) a3 += 2 * Math.PI;
|
|
10944
|
+
return sampleArc(center, nn, radius, 0, a3, x);
|
|
10945
|
+
}
|
|
10946
|
+
/** De Casteljau sampling of a Bezier of arbitrary degree. */
|
|
10947
|
+
function sampleBezier(points) {
|
|
10948
|
+
const out = [];
|
|
10949
|
+
for (let i = 0; i <= BEZIER_SEGMENTS; i++) {
|
|
10950
|
+
const t = i / BEZIER_SEGMENTS;
|
|
10951
|
+
const tmp = points.map((p) => [...p]);
|
|
10952
|
+
for (let k = 1; k < tmp.length; k++) for (let j = 0; j < tmp.length - k; j++) {
|
|
10953
|
+
const a = at3(tmp, j);
|
|
10954
|
+
const bnext = at3(tmp, j + 1);
|
|
10955
|
+
tmp[j] = [
|
|
10956
|
+
a[0] * (1 - t) + bnext[0] * t,
|
|
10957
|
+
a[1] * (1 - t) + bnext[1] * t,
|
|
10958
|
+
a[2] * (1 - t) + bnext[2] * t
|
|
10959
|
+
];
|
|
10960
|
+
}
|
|
10961
|
+
out.push(at3(tmp, 0));
|
|
10962
|
+
}
|
|
10963
|
+
return out;
|
|
10964
|
+
}
|
|
10965
|
+
/** Newell's method: area-weighted normal of a (possibly non-convex) planar ring. */
|
|
10966
|
+
function ringNormal(ring) {
|
|
10967
|
+
let nx = 0;
|
|
10968
|
+
let ny = 0;
|
|
10969
|
+
let nz = 0;
|
|
10970
|
+
for (let i = 0; i < ring.length; i++) {
|
|
10971
|
+
const a = at3(ring, i);
|
|
10972
|
+
const b = at3(ring, (i + 1) % ring.length);
|
|
10973
|
+
nx += (a[1] - b[1]) * (a[2] + b[2]);
|
|
10974
|
+
ny += (a[2] - b[2]) * (a[0] + b[0]);
|
|
10975
|
+
nz += (a[0] - b[0]) * (a[1] + b[1]);
|
|
10976
|
+
}
|
|
10977
|
+
const n = [
|
|
10978
|
+
nx,
|
|
10979
|
+
ny,
|
|
10980
|
+
nz
|
|
10981
|
+
];
|
|
10982
|
+
return length3(n) < 1e-12 ? [
|
|
10983
|
+
0,
|
|
10984
|
+
0,
|
|
10985
|
+
1
|
|
10986
|
+
] : normalize3(n);
|
|
10987
|
+
}
|
|
10988
|
+
function coincident(a, b) {
|
|
10989
|
+
return length3(sub(a, b)) < EPS_JOIN;
|
|
10990
|
+
}
|
|
10991
|
+
/** Chain edge polylines head-to-tail into one closed ring (flipping as needed). */
|
|
10992
|
+
function chainEdges(edgePts) {
|
|
10993
|
+
const first = edgePts[0];
|
|
10994
|
+
if (!first) return [];
|
|
10995
|
+
const ring = [...first];
|
|
10996
|
+
for (let i = 1; i < edgePts.length; i++) {
|
|
10997
|
+
let pts = edgePts[i] ?? [];
|
|
10998
|
+
if (pts.length === 0) continue;
|
|
10999
|
+
const end = at3(ring, ring.length - 1);
|
|
11000
|
+
const startsAtEnd = coincident(at3(pts, 0), end);
|
|
11001
|
+
const endsAtEnd = coincident(at3(pts, pts.length - 1), end);
|
|
11002
|
+
if (!startsAtEnd && endsAtEnd) pts = [...pts].reverse();
|
|
11003
|
+
const startSame = coincident(at3(pts, 0), end);
|
|
11004
|
+
for (let k = startSame ? 1 : 0; k < pts.length; k++) ring.push(at3(pts, k));
|
|
11005
|
+
}
|
|
11006
|
+
if (ring.length > 1 && coincident(at3(ring, 0), at3(ring, ring.length - 1))) ring.pop();
|
|
11007
|
+
return ring;
|
|
11008
|
+
}
|
|
11009
|
+
/** Project a planar 3D ring onto its own plane → 2D outline + world frame. */
|
|
11010
|
+
function frameFromRing(ring) {
|
|
11011
|
+
const normal = ringNormal(ring);
|
|
11012
|
+
const origin = at3(ring, 0);
|
|
11013
|
+
let xAxis = ring.length > 1 ? normalize3(sub(at3(ring, 1), origin)) : pickPerp(normal);
|
|
11014
|
+
xAxis = normalize3(sub(xAxis, scaleVec(normal, dot(xAxis, normal))));
|
|
11015
|
+
if (length3(xAxis) < 1e-9) xAxis = pickPerp(normal);
|
|
11016
|
+
const yAxis = normalize3(cross(normal, xAxis));
|
|
11017
|
+
return {
|
|
11018
|
+
outline: ensureCCW(ring.map((p) => {
|
|
11019
|
+
const rel = sub(p, origin);
|
|
11020
|
+
return [dot(rel, xAxis), dot(rel, yAxis)];
|
|
11021
|
+
})),
|
|
11022
|
+
origin,
|
|
11023
|
+
xAxis,
|
|
11024
|
+
yAxis
|
|
11025
|
+
};
|
|
11026
|
+
}
|
|
11027
|
+
var OCCT_CURVE_SEGMENTS = 12;
|
|
11028
|
+
/** Sample an OCCT edge into a polyline (line → endpoints, curve → 24 segments). */
|
|
11029
|
+
function sampleOcctEdge(occt, edge) {
|
|
11030
|
+
const [t0, t1] = occt.curveParameters(edge);
|
|
11031
|
+
let segs = OCCT_CURVE_SEGMENTS;
|
|
11032
|
+
try {
|
|
11033
|
+
if (occt.curveType(edge) === "line") segs = 1;
|
|
11034
|
+
} catch {}
|
|
11035
|
+
const pts = [];
|
|
11036
|
+
for (let i = 0; i <= segs; i++) pts.push(occt.curvePointAtParam(edge, t0 + (t1 - t0) * i / segs));
|
|
11037
|
+
return pts;
|
|
11038
|
+
}
|
|
11039
|
+
/** Discretize an OCCT wire (from the 2D-delegated blueprint path) into a ring. */
|
|
11040
|
+
function discretizeOcctWire(occt, wire) {
|
|
11041
|
+
return chainEdges(occt.iterShapes(wire, "edge").map((e) => sampleOcctEdge(occt, e)));
|
|
11042
|
+
}
|
|
11043
|
+
function makeProfileBuilders(_module) {
|
|
11044
|
+
function edge(pts, curve) {
|
|
11045
|
+
return wrap(PLACEHOLDER, makeNode("profileEdge", curve ? {
|
|
11046
|
+
pts,
|
|
11047
|
+
curve
|
|
11048
|
+
} : { pts }, []));
|
|
11049
|
+
}
|
|
11050
|
+
/** In-plane orthonormal frame (x, y) for a conic on a plane with the given normal. */
|
|
11051
|
+
function conicFrame(normal, xDir) {
|
|
11052
|
+
const n = normalize3(normal);
|
|
11053
|
+
const x = xDir ? normalize3(xDir) : pickPerp(n);
|
|
11054
|
+
return {
|
|
11055
|
+
x,
|
|
11056
|
+
y: normalize3(cross(n, x))
|
|
11057
|
+
};
|
|
11058
|
+
}
|
|
11059
|
+
/** Analytic conic descriptor for the circle through three points (or null if collinear). */
|
|
11060
|
+
function conicDescFrom3(p1, p2, p3) {
|
|
11061
|
+
const v1 = sub(p2, p1);
|
|
11062
|
+
const v2 = sub(p3, p1);
|
|
11063
|
+
const n = cross(v1, v2);
|
|
11064
|
+
if (length3(n) < 1e-12) return void 0;
|
|
11065
|
+
const nn = normalize3(n);
|
|
11066
|
+
const b = dot(v1, v1);
|
|
11067
|
+
const c = dot(v2, v2);
|
|
11068
|
+
const dd = dot(v1, v2);
|
|
11069
|
+
const denom = 2 * (b * c - dd * dd);
|
|
11070
|
+
if (Math.abs(denom) < 1e-18) return void 0;
|
|
11071
|
+
const s = c * (b - dd) / denom;
|
|
11072
|
+
const t = b * (c - dd) / denom;
|
|
11073
|
+
const center = add(p1, add(scaleVec(v1, s), scaleVec(v2, t)));
|
|
11074
|
+
const radius = length3(sub(p1, center));
|
|
11075
|
+
const x = normalize3(sub(p1, center));
|
|
11076
|
+
const y = normalize3(cross(nn, x));
|
|
11077
|
+
let a1 = Math.atan2(dot(sub(p3, center), y), dot(sub(p3, center), x));
|
|
11078
|
+
if (a1 < 0) a1 += 2 * Math.PI;
|
|
11079
|
+
return {
|
|
11080
|
+
k: "conic",
|
|
11081
|
+
center,
|
|
11082
|
+
x,
|
|
11083
|
+
y,
|
|
11084
|
+
rx: radius,
|
|
11085
|
+
ry: radius,
|
|
11086
|
+
a0: 0,
|
|
11087
|
+
a1
|
|
11088
|
+
};
|
|
11089
|
+
}
|
|
11090
|
+
function ringOrPts(shape) {
|
|
11091
|
+
const ms = asManifoldShape(shape);
|
|
11092
|
+
if (ms) {
|
|
11093
|
+
const params = ms.node.params;
|
|
11094
|
+
return params?.ring ?? params?.pts ?? [];
|
|
11095
|
+
}
|
|
11096
|
+
const occt = resolveOcct();
|
|
11097
|
+
return occt ? sampleOcctEdge(occt, shape) : [];
|
|
11098
|
+
}
|
|
11099
|
+
function inputNodes(items) {
|
|
11100
|
+
const nodes = [];
|
|
11101
|
+
for (const it of items) {
|
|
11102
|
+
const ms = asManifoldShape(it);
|
|
11103
|
+
if (ms) nodes.push(nodeOf(ms));
|
|
11104
|
+
}
|
|
11105
|
+
return nodes;
|
|
11106
|
+
}
|
|
11107
|
+
function wireFrom(items) {
|
|
11108
|
+
const ring = chainEdges(items.map((e) => ringOrPts(e)));
|
|
11109
|
+
return wrap(PLACEHOLDER, makeNode("profileWire", {
|
|
11110
|
+
ring,
|
|
11111
|
+
...ring.length >= 3 ? frameFromRing(ring) : void 0
|
|
11112
|
+
}, inputNodes(items)));
|
|
11113
|
+
}
|
|
11114
|
+
function faceFromRing(ring, input) {
|
|
11115
|
+
const { outline, origin, xAxis, yAxis } = frameFromRing(ring);
|
|
11116
|
+
return wrap(PLACEHOLDER, makeNode("profileFace", {
|
|
11117
|
+
outline,
|
|
11118
|
+
origin,
|
|
11119
|
+
xAxis,
|
|
11120
|
+
yAxis
|
|
11121
|
+
}, input ? [input] : []));
|
|
11122
|
+
}
|
|
11123
|
+
function makeFace(wire) {
|
|
11124
|
+
const ms = asManifoldShape(wire);
|
|
11125
|
+
if (ms) {
|
|
11126
|
+
const ring = ms.node.params?.ring ?? [];
|
|
11127
|
+
if (ring.length >= 3) return faceFromRing(ring, nodeOf(ms));
|
|
11128
|
+
}
|
|
11129
|
+
const occt = resolveOcct();
|
|
11130
|
+
return faceFromRing(occt ? discretizeOcctWire(occt, wire) : [], ms ? nodeOf(ms) : void 0);
|
|
11131
|
+
}
|
|
11132
|
+
function addHolesInFace(face, holeWires) {
|
|
11133
|
+
const fms = asManifoldShape(face);
|
|
11134
|
+
const fp = (fms?.node)?.params ?? {};
|
|
11135
|
+
const origin = fp.origin ?? ZERO3;
|
|
11136
|
+
const xAxis = fp.xAxis ?? [
|
|
11137
|
+
1,
|
|
11138
|
+
0,
|
|
11139
|
+
0
|
|
11140
|
+
];
|
|
11141
|
+
const yAxis = fp.yAxis ?? [
|
|
11142
|
+
0,
|
|
11143
|
+
1,
|
|
11144
|
+
0
|
|
11145
|
+
];
|
|
11146
|
+
const project = (p) => {
|
|
11147
|
+
const rel = sub(p, origin);
|
|
11148
|
+
return [dot(rel, xAxis), dot(rel, yAxis)];
|
|
11149
|
+
};
|
|
11150
|
+
const newHoles = [];
|
|
11151
|
+
for (const hw of holeWires) {
|
|
11152
|
+
const hms = asManifoldShape(hw);
|
|
11153
|
+
let ring;
|
|
11154
|
+
if (hms) ring = hms.node.params?.ring ?? [];
|
|
11155
|
+
else {
|
|
11156
|
+
const occt = resolveOcct();
|
|
11157
|
+
ring = occt ? discretizeOcctWire(occt, hw) : [];
|
|
11158
|
+
}
|
|
11159
|
+
if (ring.length >= 3) newHoles.push(ring.map(project));
|
|
11160
|
+
}
|
|
11161
|
+
const holes = [...fp.holes ?? [], ...newHoles];
|
|
11162
|
+
const inputs = fms ? [nodeOf(fms), ...inputNodes(holeWires)] : inputNodes(holeWires);
|
|
11163
|
+
return wrap(PLACEHOLDER, makeNode("profileFace", {
|
|
11164
|
+
outline: fp.outline ?? [],
|
|
11165
|
+
holes,
|
|
11166
|
+
origin,
|
|
11167
|
+
xAxis,
|
|
11168
|
+
yAxis
|
|
11169
|
+
}, inputs));
|
|
11170
|
+
}
|
|
11171
|
+
function ellipsePts(center, normal, majorRadius, minorRadius, xDir) {
|
|
11172
|
+
const n = normalize3(normal);
|
|
11173
|
+
const x = xDir ? normalize3(xDir) : pickPerp(n);
|
|
11174
|
+
const y = normalize3(cross(n, x));
|
|
11175
|
+
const pts = [];
|
|
11176
|
+
for (let i = 0; i <= FULL_CIRCLE_SEGMENTS; i++) {
|
|
11177
|
+
const a = 2 * Math.PI * i / FULL_CIRCLE_SEGMENTS;
|
|
11178
|
+
pts.push(add(center, add(scaleVec(x, majorRadius * Math.cos(a)), scaleVec(y, minorRadius * Math.sin(a)))));
|
|
11179
|
+
}
|
|
11180
|
+
return pts;
|
|
11181
|
+
}
|
|
11182
|
+
return {
|
|
11183
|
+
makeVertex: (x, y, z) => edge([[
|
|
11184
|
+
x,
|
|
11185
|
+
y,
|
|
11186
|
+
z
|
|
11187
|
+
]]),
|
|
11188
|
+
makeLineEdge: (p1, p2) => edge([p1, p2], {
|
|
11189
|
+
k: "line",
|
|
11190
|
+
p1,
|
|
11191
|
+
p2
|
|
11192
|
+
}),
|
|
11193
|
+
makeCircleEdge: (center, normal, radius) => {
|
|
11194
|
+
const { x, y } = conicFrame(normal);
|
|
11195
|
+
return edge(sampleArc(center, normal, radius, 0, 2 * Math.PI), {
|
|
11196
|
+
k: "conic",
|
|
11197
|
+
center,
|
|
11198
|
+
x,
|
|
11199
|
+
y,
|
|
11200
|
+
rx: radius,
|
|
11201
|
+
ry: radius,
|
|
11202
|
+
a0: 0,
|
|
11203
|
+
a1: 2 * Math.PI
|
|
11204
|
+
});
|
|
11205
|
+
},
|
|
11206
|
+
makeCircleArc: (center, normal, radius, startAngle, endAngle) => {
|
|
11207
|
+
const { x, y } = conicFrame(normal);
|
|
11208
|
+
return edge(sampleArc(center, normal, radius, startAngle, endAngle), {
|
|
11209
|
+
k: "conic",
|
|
11210
|
+
center,
|
|
11211
|
+
x,
|
|
11212
|
+
y,
|
|
11213
|
+
rx: radius,
|
|
11214
|
+
ry: radius,
|
|
11215
|
+
a0: startAngle,
|
|
11216
|
+
a1: endAngle
|
|
11217
|
+
});
|
|
11218
|
+
},
|
|
11219
|
+
makeArcEdge: (p1, p2, p3) => edge(circleFrom3(p1, p2, p3), conicDescFrom3(p1, p2, p3)),
|
|
11220
|
+
makeEllipseEdge: (center, normal, majorRadius, minorRadius, xDir) => {
|
|
11221
|
+
const { x, y } = conicFrame(normal, xDir);
|
|
11222
|
+
return edge(ellipsePts(center, normal, majorRadius, minorRadius, xDir), {
|
|
11223
|
+
k: "conic",
|
|
11224
|
+
center,
|
|
11225
|
+
x,
|
|
11226
|
+
y,
|
|
11227
|
+
rx: majorRadius,
|
|
11228
|
+
ry: minorRadius,
|
|
11229
|
+
a0: 0,
|
|
11230
|
+
a1: 2 * Math.PI
|
|
11231
|
+
});
|
|
11232
|
+
},
|
|
11233
|
+
makeBezierEdge: (points) => edge(sampleBezier(points), {
|
|
11234
|
+
k: "bezier",
|
|
11235
|
+
points: points.map((p) => [...p])
|
|
11236
|
+
}),
|
|
11237
|
+
makeTangentArc: (startPoint, _startTangent, endPoint) => edge([startPoint, endPoint], {
|
|
11238
|
+
k: "line",
|
|
11239
|
+
p1: startPoint,
|
|
11240
|
+
p2: endPoint
|
|
11241
|
+
}),
|
|
11242
|
+
makeHelixWire: (pitch, height, radius, center = [
|
|
11243
|
+
0,
|
|
11244
|
+
0,
|
|
11245
|
+
0
|
|
11246
|
+
], direction = [
|
|
11247
|
+
0,
|
|
11248
|
+
0,
|
|
11249
|
+
1
|
|
11250
|
+
], leftHanded = false) => {
|
|
11251
|
+
const axis = normalize3(direction);
|
|
11252
|
+
const x = pickPerp(axis);
|
|
11253
|
+
const y0 = normalize3(cross(axis, x));
|
|
11254
|
+
const y = leftHanded ? scaleVec(y0, -1) : y0;
|
|
11255
|
+
const turns = pitch !== 0 ? height / pitch : 0;
|
|
11256
|
+
const desc = {
|
|
11257
|
+
k: "helix",
|
|
11258
|
+
center,
|
|
11259
|
+
axis,
|
|
11260
|
+
x,
|
|
11261
|
+
y,
|
|
11262
|
+
radius,
|
|
11263
|
+
pitch,
|
|
11264
|
+
turns
|
|
11265
|
+
};
|
|
11266
|
+
const segs = Math.max(8, Math.ceil(turns * FULL_CIRCLE_SEGMENTS));
|
|
11267
|
+
const pts = [];
|
|
11268
|
+
for (let i = 0; i <= segs; i++) pts.push(descPointAt(desc, 2 * Math.PI * turns * i / segs));
|
|
11269
|
+
return edge(pts, desc);
|
|
11270
|
+
},
|
|
11271
|
+
makeWire: (edges) => wireFrom(edges),
|
|
11272
|
+
makeWireFromMixed: (items) => wireFrom(items),
|
|
11273
|
+
makeFace,
|
|
11274
|
+
addHolesInFace,
|
|
11275
|
+
makePolygonFace: (points) => faceFromRing(points)
|
|
11276
|
+
};
|
|
11277
|
+
}
|
|
11278
|
+
//#endregion
|
|
11279
|
+
//#region src/kernel/manifold/builderOps.ts
|
|
11280
|
+
function makeBuilderOps(module) {
|
|
11281
|
+
const Manifold = module.Manifold;
|
|
11282
|
+
const profile = makeProfileBuilders(module);
|
|
11283
|
+
function hullFromPoints(points, tolerance) {
|
|
11284
|
+
const coords = points.map((p) => [
|
|
11285
|
+
p.x,
|
|
11286
|
+
p.y,
|
|
11287
|
+
p.z
|
|
11288
|
+
]);
|
|
11289
|
+
return wrap(Manifold.hull(coords), makeNode("hullFromPoints", {
|
|
11290
|
+
points: coords,
|
|
11291
|
+
tolerance
|
|
11292
|
+
}, []));
|
|
11293
|
+
}
|
|
11294
|
+
function hull(shapes, tolerance) {
|
|
11295
|
+
const operands = shapes.map((s) => unwrap(s));
|
|
11296
|
+
return wrap(Manifold.hull(operands), makeNode("hull", { tolerance }, shapes.map((s) => nodeOf(s))));
|
|
11297
|
+
}
|
|
11298
|
+
function sewAndSolidify(faces, tolerance) {
|
|
11299
|
+
const first = faces[0];
|
|
11300
|
+
if (!first) notImplemented("sewAndSolidify (no input faces on manifold kernel)");
|
|
11301
|
+
return wrap(unwrap(first), makeNode("sewAndSolidify", { tolerance }, faces.map((f) => nodeOf(f))));
|
|
11302
|
+
}
|
|
11303
|
+
return {
|
|
11304
|
+
makeVertex: (x, y, z) => profile.makeVertex(x, y, z),
|
|
11305
|
+
makeEdge: () => notImplemented("makeEdge"),
|
|
11306
|
+
makeWire: (edges) => profile.makeWire(edges),
|
|
11307
|
+
makeFace: (wire, planar) => profile.makeFace(wire, planar),
|
|
11308
|
+
makeLineEdge: (p1, p2) => profile.makeLineEdge(p1, p2),
|
|
11309
|
+
makeCircleEdge: (center, normal, radius) => profile.makeCircleEdge(center, normal, radius),
|
|
11310
|
+
makeCircleArc: (center, normal, radius, startAngle, endAngle) => profile.makeCircleArc(center, normal, radius, startAngle, endAngle),
|
|
11311
|
+
makeArcEdge: (p1, p2, p3) => profile.makeArcEdge(p1, p2, p3),
|
|
11312
|
+
makeEllipseEdge: (center, normal, majorRadius, minorRadius, xDir) => profile.makeEllipseEdge(center, normal, majorRadius, minorRadius, xDir),
|
|
11313
|
+
makeEllipseArc: () => notImplemented("makeEllipseArc"),
|
|
11314
|
+
makeBezierEdge: (points) => profile.makeBezierEdge(points),
|
|
11315
|
+
makeTangentArc: (startPoint, startTangent, endPoint) => profile.makeTangentArc(startPoint, startTangent, endPoint),
|
|
11316
|
+
makeHelixWire: (pitch, height, radius, center, direction, leftHanded) => profile.makeHelixWire(pitch, height, radius, center, direction, leftHanded),
|
|
11317
|
+
makeWireFromMixed: (items) => profile.makeWireFromMixed(items),
|
|
11318
|
+
makeCompound: () => notImplemented("makeCompound"),
|
|
11319
|
+
solidFromShell: () => notImplemented("solidFromShell"),
|
|
11320
|
+
hull,
|
|
11321
|
+
hullFromPoints,
|
|
11322
|
+
buildSolidFromFaces: () => notImplemented("buildSolidFromFaces"),
|
|
11323
|
+
makeNonPlanarFace: () => notImplemented("makeNonPlanarFace"),
|
|
11324
|
+
addHolesInFace: (face, holeWires) => profile.addHolesInFace(face, holeWires),
|
|
11325
|
+
removeHolesFromFace: () => notImplemented("removeHolesFromFace"),
|
|
11326
|
+
makeFaceOnSurface: () => notImplemented("makeFaceOnSurface"),
|
|
11327
|
+
bsplineSurface: (points, rows, cols) => occtOrThrow("bsplineSurface").bsplineSurface(points, rows, cols),
|
|
11328
|
+
triangulatedSurface: (points, rows, cols) => occtOrThrow("triangulatedSurface").triangulatedSurface(points, rows, cols),
|
|
11329
|
+
buildTriFace: () => notImplemented("buildTriFace"),
|
|
11330
|
+
sewAndSolidify,
|
|
11331
|
+
createPoint3d: () => notImplemented("createPoint3d"),
|
|
11332
|
+
createDirection3d: () => notImplemented("createDirection3d"),
|
|
11333
|
+
createVector3d: () => notImplemented("createVector3d"),
|
|
11334
|
+
createAxis1: () => notImplemented("createAxis1"),
|
|
11335
|
+
createAxis2: () => notImplemented("createAxis2"),
|
|
11336
|
+
createAxis3: () => notImplemented("createAxis3")
|
|
11337
|
+
};
|
|
11338
|
+
}
|
|
11339
|
+
//#endregion
|
|
10681
11340
|
//#region src/kernel/manifold/sweepOps.ts
|
|
10682
11341
|
var RAD_PER_DEG = Math.PI / 180;
|
|
10683
11342
|
function asShape$1(shape) {
|
|
@@ -10892,8 +11551,10 @@ function extrudeOp(module, face, direction, length) {
|
|
|
10892
11551
|
direction[1] * length,
|
|
10893
11552
|
direction[2] * length
|
|
10894
11553
|
]);
|
|
10895
|
-
|
|
11554
|
+
const polygons = [toPolygon(section), ...(section.holes ?? []).map((h) => h.map((p) => [p[0], p[1]]))];
|
|
11555
|
+
return wrap(orientExtrusion(orientPositive(module, module.Manifold.extrude(polygons, height)), section, dir), makeNode("extrude", {
|
|
10896
11556
|
outline: section.outline,
|
|
11557
|
+
holes: section.holes,
|
|
10897
11558
|
origin: section.origin,
|
|
10898
11559
|
xAxis: section.xAxis,
|
|
10899
11560
|
yAxis: section.yAxis,
|
|
@@ -10911,14 +11572,59 @@ function revolveOp(module, shape, axisOrigin, axisDirection, angleRad, op, param
|
|
|
10911
11572
|
const radial = profileRadialOutline(section, axisOrigin, axisDirection);
|
|
10912
11573
|
return wrap(orientRevolution(module.Manifold.revolve([radial], 0, angleDeg), axisOrigin, axisDirection), makeNode(op, params, [nodeOf(asShape$1(shape))]));
|
|
10913
11574
|
}
|
|
11575
|
+
/**
|
|
11576
|
+
* Rotate `ring`'s point order to the cyclic offset that best lines up with
|
|
11577
|
+
* `ref` (minimizes Σ‖ring[(j+off)%n] − ref[j]‖²). Both rings must share a
|
|
11578
|
+
* vertex count. Keeps loft correspondence from twisting between dissimilar
|
|
11579
|
+
* sections.
|
|
11580
|
+
*/
|
|
11581
|
+
function alignRing(ring, ref) {
|
|
11582
|
+
const n = ring.length;
|
|
11583
|
+
if (n === 0 || ref.length !== n) return ring;
|
|
11584
|
+
let bestOff = 0;
|
|
11585
|
+
let bestCost = Infinity;
|
|
11586
|
+
for (let off = 0; off < n; off++) {
|
|
11587
|
+
let cost = 0;
|
|
11588
|
+
for (let j = 0; j < n && cost < bestCost; j++) {
|
|
11589
|
+
const a = ring[(j + off) % n] ?? [
|
|
11590
|
+
0,
|
|
11591
|
+
0,
|
|
11592
|
+
0
|
|
11593
|
+
];
|
|
11594
|
+
const b = ref[j] ?? [
|
|
11595
|
+
0,
|
|
11596
|
+
0,
|
|
11597
|
+
0
|
|
11598
|
+
];
|
|
11599
|
+
const dx = a[0] - b[0];
|
|
11600
|
+
const dy = a[1] - b[1];
|
|
11601
|
+
const dz = a[2] - b[2];
|
|
11602
|
+
cost += dx * dx + dy * dy + dz * dz;
|
|
11603
|
+
}
|
|
11604
|
+
if (cost < bestCost) {
|
|
11605
|
+
bestCost = cost;
|
|
11606
|
+
bestOff = off;
|
|
11607
|
+
}
|
|
11608
|
+
}
|
|
11609
|
+
if (bestOff === 0) return ring;
|
|
11610
|
+
const out = [];
|
|
11611
|
+
for (let j = 0; j < n; j++) out.push(ring[(j + bestOff) % n] ?? [
|
|
11612
|
+
0,
|
|
11613
|
+
0,
|
|
11614
|
+
0
|
|
11615
|
+
]);
|
|
11616
|
+
return out;
|
|
11617
|
+
}
|
|
10914
11618
|
function loftOp(module, wires, op, extraParams) {
|
|
10915
11619
|
if (wires.length < 2) throw new Error("manifold: loft requires at least two profiles");
|
|
10916
11620
|
const sections = wires.map(profileCrossSection);
|
|
10917
|
-
const
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
|
|
10921
|
-
}
|
|
11621
|
+
const target = sections.reduce((mx, s) => Math.max(mx, s.outline.length), 0);
|
|
11622
|
+
const rings = sections.map((s) => placeRing({
|
|
11623
|
+
...s,
|
|
11624
|
+
outline: resampleClosed(s.outline, target)
|
|
11625
|
+
}, sectionFrame(s)));
|
|
11626
|
+
for (let i = 1; i < rings.length; i++) rings[i] = alignRing(rings[i] ?? [], rings[i - 1] ?? []);
|
|
11627
|
+
return wrap(skinRings(module, rings), makeNode(op, {
|
|
10922
11628
|
sections: sections.map(serializeSection),
|
|
10923
11629
|
...extraParams
|
|
10924
11630
|
}, wires.map((w) => nodeOf(asShape$1(w)))));
|
|
@@ -11066,7 +11772,7 @@ function makeSweepOps(module) {
|
|
|
11066
11772
|
endFactor
|
|
11067
11773
|
}),
|
|
11068
11774
|
loftBatch: () => notImplemented("loftBatch"),
|
|
11069
|
-
extrudeBatch: () =>
|
|
11775
|
+
extrudeBatch: (entries) => entries.map((e) => extrudeOp(module, e.face, e.direction, e.length))
|
|
11070
11776
|
};
|
|
11071
11777
|
}
|
|
11072
11778
|
//#endregion
|
|
@@ -11202,10 +11908,55 @@ function chamferDistAngle(module, shape, edges, distance, angleDeg) {
|
|
|
11202
11908
|
selection
|
|
11203
11909
|
}, [nodeOf(input)]));
|
|
11204
11910
|
}
|
|
11911
|
+
/**
|
|
11912
|
+
* Hollow an extrude-origin solid with an open top — the gridfinity bin-body case
|
|
11913
|
+
* (and what `approxShellMesh` cannot do: manifold-3d exposes no Minkowski inward
|
|
11914
|
+
* offset, so the generic shell no-ops and leaves the body solid). Reconstruct the
|
|
11915
|
+
* cavity from the extrude op-node's recorded outline: offset it inward by
|
|
11916
|
+
* `thickness` (Clipper2 2D), extrude full height so it punches through the top
|
|
11917
|
+
* (open), lifted by `thickness` along the extrude dir so a floor remains, then
|
|
11918
|
+
* subtract. Returns undefined for non-extrude solids (caller falls back).
|
|
11919
|
+
*/
|
|
11920
|
+
function extrudeOpenTopShell(module, input, thickness) {
|
|
11921
|
+
const node = input.node;
|
|
11922
|
+
if (node?.op !== "extrude") return void 0;
|
|
11923
|
+
const p = node.params ?? {};
|
|
11924
|
+
const outline = p["outline"];
|
|
11925
|
+
const origin = p["origin"];
|
|
11926
|
+
const dir = p["direction"];
|
|
11927
|
+
const length = p["length"];
|
|
11928
|
+
if (!outline || outline.length < 3 || !origin || !dir || typeof length !== "number") return;
|
|
11929
|
+
const t = Math.abs(thickness);
|
|
11930
|
+
try {
|
|
11931
|
+
const inner = new module.CrossSection([outline.map((q) => [q[0], q[1]])]).offset(-t);
|
|
11932
|
+
if (typeof inner.isEmpty === "function" && inner.isEmpty()) return void 0;
|
|
11933
|
+
const cavity = module.Manifold.extrude(inner, length);
|
|
11934
|
+
const dlen = Math.hypot(dir[0], dir[1], dir[2]) || 1;
|
|
11935
|
+
const alignedZ = Math.abs(dir[0]) < 1e-9 && Math.abs(dir[1]) < 1e-9 && dir[2] > 0;
|
|
11936
|
+
let placed = cavity;
|
|
11937
|
+
if (!alignedZ) {
|
|
11938
|
+
const pitch = Math.atan2(Math.hypot(dir[0], dir[1]), dir[2]) * (180 / Math.PI);
|
|
11939
|
+
const yaw = Math.atan2(dir[1], dir[0]) * (180 / Math.PI);
|
|
11940
|
+
placed = placed.rotate([
|
|
11941
|
+
0,
|
|
11942
|
+
pitch,
|
|
11943
|
+
yaw
|
|
11944
|
+
]);
|
|
11945
|
+
}
|
|
11946
|
+
placed = placed.translate([
|
|
11947
|
+
origin[0] + dir[0] / dlen * t,
|
|
11948
|
+
origin[1] + dir[1] / dlen * t,
|
|
11949
|
+
origin[2] + dir[2] / dlen * t
|
|
11950
|
+
]);
|
|
11951
|
+
return orientPositive(module, unwrap(input).subtract(placed));
|
|
11952
|
+
} catch {
|
|
11953
|
+
return;
|
|
11954
|
+
}
|
|
11955
|
+
}
|
|
11205
11956
|
function shell(module, shape, faces, thickness, tolerance) {
|
|
11206
11957
|
const input = asShape(shape);
|
|
11207
11958
|
const selection = describeSelection(faces);
|
|
11208
|
-
return wrap(approxShellMesh(module, unwrap(input), thickness, false), makeNode("shell", tolerance === void 0 ? {
|
|
11959
|
+
return wrap(extrudeOpenTopShell(module, input, thickness) ?? approxShellMesh(module, unwrap(input), thickness, false), makeNode("shell", tolerance === void 0 ? {
|
|
11209
11960
|
thickness,
|
|
11210
11961
|
selection
|
|
11211
11962
|
} : {
|
|
@@ -11650,6 +12401,53 @@ var HANDLERS = {
|
|
|
11650
12401
|
if (typeof t.gridPattern !== "function") throw new Error("manifold replay: target kernel lacks gridPattern");
|
|
11651
12402
|
return t.gridPattern(input0(inputs), asVec3(p["directionX"]), asVec3(p["directionY"]), num(p["spacingX"]), num(p["spacingY"]), num(p["countX"], 1), num(p["countY"], 1));
|
|
11652
12403
|
},
|
|
12404
|
+
profileEdge: (t, p) => {
|
|
12405
|
+
const pts = p["pts"] ?? [];
|
|
12406
|
+
const a = pts[0] ?? [
|
|
12407
|
+
0,
|
|
12408
|
+
0,
|
|
12409
|
+
0
|
|
12410
|
+
];
|
|
12411
|
+
const b = pts.length > 1 ? pts[pts.length - 1] ?? a : [
|
|
12412
|
+
a[0] + .001,
|
|
12413
|
+
a[1],
|
|
12414
|
+
a[2]
|
|
12415
|
+
];
|
|
12416
|
+
return t.makeLineEdge([
|
|
12417
|
+
a[0],
|
|
12418
|
+
a[1],
|
|
12419
|
+
a[2]
|
|
12420
|
+
], [
|
|
12421
|
+
b[0],
|
|
12422
|
+
b[1],
|
|
12423
|
+
b[2]
|
|
12424
|
+
]);
|
|
12425
|
+
},
|
|
12426
|
+
profileWire: (t, p) => {
|
|
12427
|
+
const ring = p["ring"] ?? [];
|
|
12428
|
+
const edges = [];
|
|
12429
|
+
for (let i = 0; i < ring.length; i++) {
|
|
12430
|
+
const a = ring[i] ?? [
|
|
12431
|
+
0,
|
|
12432
|
+
0,
|
|
12433
|
+
0
|
|
12434
|
+
];
|
|
12435
|
+
const b = ring[(i + 1) % ring.length] ?? a;
|
|
12436
|
+
edges.push(t.makeLineEdge([
|
|
12437
|
+
a[0],
|
|
12438
|
+
a[1],
|
|
12439
|
+
a[2]
|
|
12440
|
+
], [
|
|
12441
|
+
b[0],
|
|
12442
|
+
b[1],
|
|
12443
|
+
b[2]
|
|
12444
|
+
]));
|
|
12445
|
+
}
|
|
12446
|
+
const wire = t.makeWire(edges);
|
|
12447
|
+
for (const e of edges) t.dispose(e);
|
|
12448
|
+
return wire;
|
|
12449
|
+
},
|
|
12450
|
+
profileFace: (t, p) => faceFromOutline(t, p),
|
|
11653
12451
|
extrude: (t, p) => {
|
|
11654
12452
|
const face = faceFromOutline(t, p);
|
|
11655
12453
|
return t.extrude(face, asVec3(p["direction"], [
|
|
@@ -11829,7 +12627,62 @@ function replay(node, targetKernel, cache = /* @__PURE__ */ new Map()) {
|
|
|
11829
12627
|
function solidOf(shape) {
|
|
11830
12628
|
return unwrap(shape);
|
|
11831
12629
|
}
|
|
12630
|
+
/** World points recorded on a profile placeholder node (edge/wire/face/vertex). */
|
|
12631
|
+
function profileWorldPoints(shape) {
|
|
12632
|
+
const p = (shape?.node)?.params;
|
|
12633
|
+
if (!p) return void 0;
|
|
12634
|
+
const pts = p["pts"] ?? p["ring"];
|
|
12635
|
+
if (pts && pts.length) return pts;
|
|
12636
|
+
const outline = p["outline"];
|
|
12637
|
+
if (outline && outline.length) {
|
|
12638
|
+
const o = p["origin"] ?? [
|
|
12639
|
+
0,
|
|
12640
|
+
0,
|
|
12641
|
+
0
|
|
12642
|
+
];
|
|
12643
|
+
const x = p["xAxis"] ?? [
|
|
12644
|
+
1,
|
|
12645
|
+
0,
|
|
12646
|
+
0
|
|
12647
|
+
];
|
|
12648
|
+
const y = p["yAxis"] ?? [
|
|
12649
|
+
0,
|
|
12650
|
+
1,
|
|
12651
|
+
0
|
|
12652
|
+
];
|
|
12653
|
+
return outline.map((q) => [
|
|
12654
|
+
o[0] + x[0] * q[0] + y[0] * q[1],
|
|
12655
|
+
o[1] + x[1] * q[0] + y[1] * q[1],
|
|
12656
|
+
o[2] + x[2] * q[0] + y[2] * q[1]
|
|
12657
|
+
]);
|
|
12658
|
+
}
|
|
12659
|
+
}
|
|
12660
|
+
function aabbOfPoints(pts) {
|
|
12661
|
+
const min = [
|
|
12662
|
+
Infinity,
|
|
12663
|
+
Infinity,
|
|
12664
|
+
Infinity
|
|
12665
|
+
];
|
|
12666
|
+
const max = [
|
|
12667
|
+
-Infinity,
|
|
12668
|
+
-Infinity,
|
|
12669
|
+
-Infinity
|
|
12670
|
+
];
|
|
12671
|
+
for (const q of pts) for (let i = 0; i < 3; i++) {
|
|
12672
|
+
const v = q[i] ?? 0;
|
|
12673
|
+
if (v < (min[i] ?? Infinity)) min[i] = v;
|
|
12674
|
+
if (v > (max[i] ?? -Infinity)) max[i] = v;
|
|
12675
|
+
}
|
|
12676
|
+
return {
|
|
12677
|
+
min,
|
|
12678
|
+
max
|
|
12679
|
+
};
|
|
12680
|
+
}
|
|
11832
12681
|
function boxOf(shape) {
|
|
12682
|
+
const w = shape;
|
|
12683
|
+
if (w && w.__manifoldSub && w.box) return w.box;
|
|
12684
|
+
const pts = profileWorldPoints(shape);
|
|
12685
|
+
if (pts) return aabbOfPoints(pts);
|
|
11833
12686
|
return solidOf(shape).boundingBox();
|
|
11834
12687
|
}
|
|
11835
12688
|
function meshOf$2(shape) {
|
|
@@ -11969,9 +12822,16 @@ function volume(shape) {
|
|
|
11969
12822
|
return solidOf(shape).volume();
|
|
11970
12823
|
}
|
|
11971
12824
|
function area(shape) {
|
|
12825
|
+
const w = shape;
|
|
12826
|
+
if (w && w.__nativeFace && typeof w.area === "number") return w.area;
|
|
11972
12827
|
return solidOf(shape).surfaceArea();
|
|
11973
12828
|
}
|
|
11974
12829
|
function boundingBox(shape) {
|
|
12830
|
+
const w = shape;
|
|
12831
|
+
if (w && w.__manifoldSub && w.box) return {
|
|
12832
|
+
min: [...w.box.min],
|
|
12833
|
+
max: [...w.box.max]
|
|
12834
|
+
};
|
|
11975
12835
|
const bb = boxOf(shape);
|
|
11976
12836
|
return {
|
|
11977
12837
|
min: [...bb.min],
|
|
@@ -12042,7 +12902,13 @@ function makeMeasureOps(_module) {
|
|
|
12042
12902
|
return {
|
|
12043
12903
|
volume: (shape) => volume(shape),
|
|
12044
12904
|
area: (shape) => area(shape),
|
|
12045
|
-
length: () =>
|
|
12905
|
+
length: (shape) => {
|
|
12906
|
+
const e = shape;
|
|
12907
|
+
if (e && e.__nativeEdge && typeof e.length === "number") return e.length;
|
|
12908
|
+
const node = asManifoldShape(shape)?.node;
|
|
12909
|
+
if (node?.op === "profileEdge" && node.params?.curve) return descLength(node.params.curve);
|
|
12910
|
+
return notImplemented("length");
|
|
12911
|
+
},
|
|
12046
12912
|
centerOfMass: (shape) => centerOfMass(shape),
|
|
12047
12913
|
linearCenterOfMass: (shape) => centerOfMass(shape),
|
|
12048
12914
|
boundingBox: (shape) => boundingBox(shape),
|
|
@@ -12057,6 +12923,459 @@ function makeMeasureOps(_module) {
|
|
|
12057
12923
|
};
|
|
12058
12924
|
}
|
|
12059
12925
|
//#endregion
|
|
12926
|
+
//#region src/kernel/manifold/nativeFaces.ts
|
|
12927
|
+
/** runOriginalID for a triangle index, via the runIndex run boundaries. */
|
|
12928
|
+
function originOfTri(mesh, triIndex) {
|
|
12929
|
+
const { runIndex, runOriginalID } = mesh;
|
|
12930
|
+
if (!runIndex || !runOriginalID) return 0;
|
|
12931
|
+
const vertPos = triIndex * 3;
|
|
12932
|
+
for (let r = 0; r < runOriginalID.length; r++) {
|
|
12933
|
+
const start = runIndex[r] ?? 0;
|
|
12934
|
+
const end = runIndex[r + 1] ?? Number.MAX_SAFE_INTEGER;
|
|
12935
|
+
if (vertPos >= start && vertPos < end) return runOriginalID[r] ?? 0;
|
|
12936
|
+
}
|
|
12937
|
+
return 0;
|
|
12938
|
+
}
|
|
12939
|
+
/**
|
|
12940
|
+
* Extract planar faces from a manifold solid by grouping triangles on `faceID`.
|
|
12941
|
+
* Returns one {@link NativeFace} per group with normal/center/area/bbox/origin.
|
|
12942
|
+
*/
|
|
12943
|
+
function extractFaces(meshUnknown) {
|
|
12944
|
+
const mesh = meshUnknown;
|
|
12945
|
+
const tv = mesh.triVerts;
|
|
12946
|
+
const vp = mesh.vertProperties;
|
|
12947
|
+
const stride = mesh.numProp || 3;
|
|
12948
|
+
const faceID = mesh.faceID;
|
|
12949
|
+
const triCount = tv.length / 3;
|
|
12950
|
+
const pos = (vi) => {
|
|
12951
|
+
const o = vi * stride;
|
|
12952
|
+
return [
|
|
12953
|
+
vp[o] ?? 0,
|
|
12954
|
+
vp[o + 1] ?? 0,
|
|
12955
|
+
vp[o + 2] ?? 0
|
|
12956
|
+
];
|
|
12957
|
+
};
|
|
12958
|
+
const groups = /* @__PURE__ */ new Map();
|
|
12959
|
+
for (let t = 0; t < triCount; t++) {
|
|
12960
|
+
const id = faceID ? faceID[t] ?? t : t;
|
|
12961
|
+
let g = groups.get(id);
|
|
12962
|
+
if (!g) {
|
|
12963
|
+
g = [];
|
|
12964
|
+
groups.set(id, g);
|
|
12965
|
+
}
|
|
12966
|
+
g.push(t);
|
|
12967
|
+
}
|
|
12968
|
+
const faces = [];
|
|
12969
|
+
for (const [faceId, tris] of groups) {
|
|
12970
|
+
let nx = 0;
|
|
12971
|
+
let ny = 0;
|
|
12972
|
+
let nz = 0;
|
|
12973
|
+
let cx = 0;
|
|
12974
|
+
let cy = 0;
|
|
12975
|
+
let cz = 0;
|
|
12976
|
+
let area = 0;
|
|
12977
|
+
let minX = Infinity;
|
|
12978
|
+
let minY = Infinity;
|
|
12979
|
+
let minZ = Infinity;
|
|
12980
|
+
let maxX = -Infinity;
|
|
12981
|
+
let maxY = -Infinity;
|
|
12982
|
+
let maxZ = -Infinity;
|
|
12983
|
+
const triData = new Float32Array(tris.length * 9);
|
|
12984
|
+
let w = 0;
|
|
12985
|
+
for (const t of tris) {
|
|
12986
|
+
const a = pos(tv[t * 3] ?? 0);
|
|
12987
|
+
const b = pos(tv[t * 3 + 1] ?? 0);
|
|
12988
|
+
const c = pos(tv[t * 3 + 2] ?? 0);
|
|
12989
|
+
const ux = b[0] - a[0];
|
|
12990
|
+
const uy = b[1] - a[1];
|
|
12991
|
+
const uz = b[2] - a[2];
|
|
12992
|
+
const vx = c[0] - a[0];
|
|
12993
|
+
const vy = c[1] - a[1];
|
|
12994
|
+
const vz = c[2] - a[2];
|
|
12995
|
+
const px = uy * vz - uz * vy;
|
|
12996
|
+
const py = uz * vx - ux * vz;
|
|
12997
|
+
const pz = ux * vy - uy * vx;
|
|
12998
|
+
nx += px;
|
|
12999
|
+
ny += py;
|
|
13000
|
+
nz += pz;
|
|
13001
|
+
const triArea = .5 * Math.hypot(px, py, pz);
|
|
13002
|
+
area += triArea;
|
|
13003
|
+
const tcx = (a[0] + b[0] + c[0]) / 3;
|
|
13004
|
+
const tcy = (a[1] + b[1] + c[1]) / 3;
|
|
13005
|
+
const tcz = (a[2] + b[2] + c[2]) / 3;
|
|
13006
|
+
cx += triArea * tcx;
|
|
13007
|
+
cy += triArea * tcy;
|
|
13008
|
+
cz += triArea * tcz;
|
|
13009
|
+
for (const p of [
|
|
13010
|
+
a,
|
|
13011
|
+
b,
|
|
13012
|
+
c
|
|
13013
|
+
]) {
|
|
13014
|
+
if (p[0] < minX) minX = p[0];
|
|
13015
|
+
if (p[1] < minY) minY = p[1];
|
|
13016
|
+
if (p[2] < minZ) minZ = p[2];
|
|
13017
|
+
if (p[0] > maxX) maxX = p[0];
|
|
13018
|
+
if (p[1] > maxY) maxY = p[1];
|
|
13019
|
+
if (p[2] > maxZ) maxZ = p[2];
|
|
13020
|
+
triData[w++] = p[0];
|
|
13021
|
+
triData[w++] = p[1];
|
|
13022
|
+
triData[w++] = p[2];
|
|
13023
|
+
}
|
|
13024
|
+
}
|
|
13025
|
+
const nlen = Math.hypot(nx, ny, nz) || 1;
|
|
13026
|
+
const normal = [
|
|
13027
|
+
nx / nlen,
|
|
13028
|
+
ny / nlen,
|
|
13029
|
+
nz / nlen
|
|
13030
|
+
];
|
|
13031
|
+
const center = area > 0 ? [
|
|
13032
|
+
cx / area,
|
|
13033
|
+
cy / area,
|
|
13034
|
+
cz / area
|
|
13035
|
+
] : [
|
|
13036
|
+
0,
|
|
13037
|
+
0,
|
|
13038
|
+
0
|
|
13039
|
+
];
|
|
13040
|
+
faces.push({
|
|
13041
|
+
__nativeFace: true,
|
|
13042
|
+
faceId,
|
|
13043
|
+
originId: originOfTri(mesh, tris[0] ?? 0),
|
|
13044
|
+
normal,
|
|
13045
|
+
center,
|
|
13046
|
+
area,
|
|
13047
|
+
min: [
|
|
13048
|
+
minX,
|
|
13049
|
+
minY,
|
|
13050
|
+
minZ
|
|
13051
|
+
],
|
|
13052
|
+
max: [
|
|
13053
|
+
maxX,
|
|
13054
|
+
maxY,
|
|
13055
|
+
maxZ
|
|
13056
|
+
],
|
|
13057
|
+
tris: triData
|
|
13058
|
+
});
|
|
13059
|
+
}
|
|
13060
|
+
return faces;
|
|
13061
|
+
}
|
|
13062
|
+
function isNativeFace(shape) {
|
|
13063
|
+
return !!shape && typeof shape === "object" && shape.__nativeFace === true;
|
|
13064
|
+
}
|
|
13065
|
+
//#endregion
|
|
13066
|
+
//#region src/kernel/manifold/nativeEdges.ts
|
|
13067
|
+
var CREASE_COS = Math.cos(45 * Math.PI / 180);
|
|
13068
|
+
var edgeKey = (a, b) => a < b ? a * 1e9 + b : b * 1e9 + a;
|
|
13069
|
+
var pairKey = (a, b) => a < b ? `${a},${b}` : `${b},${a}`;
|
|
13070
|
+
/**
|
|
13071
|
+
* Extract feature edges (face-pair boundaries) from a manifold solid's mesh.
|
|
13072
|
+
*/
|
|
13073
|
+
function extractEdges(meshUnknown) {
|
|
13074
|
+
const mesh = meshUnknown;
|
|
13075
|
+
const tv = mesh.triVerts;
|
|
13076
|
+
const vp = mesh.vertProperties;
|
|
13077
|
+
const stride = mesh.numProp || 3;
|
|
13078
|
+
const faceID = mesh.faceID;
|
|
13079
|
+
const triCount = tv.length / 3;
|
|
13080
|
+
if (!faceID) return [];
|
|
13081
|
+
const pos = (vi) => {
|
|
13082
|
+
const o = vi * stride;
|
|
13083
|
+
return [
|
|
13084
|
+
vp[o] ?? 0,
|
|
13085
|
+
vp[o + 1] ?? 0,
|
|
13086
|
+
vp[o + 2] ?? 0
|
|
13087
|
+
];
|
|
13088
|
+
};
|
|
13089
|
+
const triNormal = (t) => {
|
|
13090
|
+
const a = pos(tv[t * 3] ?? 0);
|
|
13091
|
+
const b = pos(tv[t * 3 + 1] ?? 0);
|
|
13092
|
+
const c = pos(tv[t * 3 + 2] ?? 0);
|
|
13093
|
+
const ux = b[0] - a[0];
|
|
13094
|
+
const uy = b[1] - a[1];
|
|
13095
|
+
const uz = b[2] - a[2];
|
|
13096
|
+
const vx = c[0] - a[0];
|
|
13097
|
+
const vy = c[1] - a[1];
|
|
13098
|
+
const vz = c[2] - a[2];
|
|
13099
|
+
const nx = uy * vz - uz * vy;
|
|
13100
|
+
const ny = uz * vx - ux * vz;
|
|
13101
|
+
const nz = ux * vy - uy * vx;
|
|
13102
|
+
const l = Math.hypot(nx, ny, nz) || 1;
|
|
13103
|
+
return [
|
|
13104
|
+
nx / l,
|
|
13105
|
+
ny / l,
|
|
13106
|
+
nz / l
|
|
13107
|
+
];
|
|
13108
|
+
};
|
|
13109
|
+
const edgeFaces = /* @__PURE__ */ new Map();
|
|
13110
|
+
for (let t = 0; t < triCount; t++) {
|
|
13111
|
+
const a = tv[t * 3] ?? 0;
|
|
13112
|
+
const b = tv[t * 3 + 1] ?? 0;
|
|
13113
|
+
const c = tv[t * 3 + 2] ?? 0;
|
|
13114
|
+
const fid = faceID[t] ?? t;
|
|
13115
|
+
const n = triNormal(t);
|
|
13116
|
+
for (const [u, w] of [
|
|
13117
|
+
[a, b],
|
|
13118
|
+
[b, c],
|
|
13119
|
+
[c, a]
|
|
13120
|
+
]) {
|
|
13121
|
+
const k = edgeKey(u, w);
|
|
13122
|
+
let e = edgeFaces.get(k);
|
|
13123
|
+
if (!e) {
|
|
13124
|
+
e = {
|
|
13125
|
+
v: [u, w],
|
|
13126
|
+
faces: [],
|
|
13127
|
+
nrm: []
|
|
13128
|
+
};
|
|
13129
|
+
edgeFaces.set(k, e);
|
|
13130
|
+
}
|
|
13131
|
+
e.faces.push(fid);
|
|
13132
|
+
e.nrm.push(n);
|
|
13133
|
+
}
|
|
13134
|
+
}
|
|
13135
|
+
const groups = /* @__PURE__ */ new Map();
|
|
13136
|
+
for (const { v, faces, nrm } of edgeFaces.values()) {
|
|
13137
|
+
if (faces.length !== 2) continue;
|
|
13138
|
+
const [f0, f1] = faces;
|
|
13139
|
+
if (f0 === f1) continue;
|
|
13140
|
+
const [n0, n1] = nrm;
|
|
13141
|
+
if (n0[0] * n1[0] + n0[1] * n1[1] + n0[2] * n1[2] > CREASE_COS) continue;
|
|
13142
|
+
const key = pairKey(f0, f1);
|
|
13143
|
+
let g = groups.get(key);
|
|
13144
|
+
if (!g) {
|
|
13145
|
+
g = {
|
|
13146
|
+
faces: f0 < f1 ? [f0, f1] : [f1, f0],
|
|
13147
|
+
segs: []
|
|
13148
|
+
};
|
|
13149
|
+
groups.set(key, g);
|
|
13150
|
+
}
|
|
13151
|
+
g.segs.push(v);
|
|
13152
|
+
}
|
|
13153
|
+
const edges = [];
|
|
13154
|
+
for (const { faces, segs } of groups.values()) for (const chain of chainSegments(segs)) edges.push(buildEdge(faces, chain, pos));
|
|
13155
|
+
return edges;
|
|
13156
|
+
}
|
|
13157
|
+
/** Order a bag of undirected vertex-pair segments into one or more vertex chains. */
|
|
13158
|
+
function chainSegments(segs) {
|
|
13159
|
+
const adj = /* @__PURE__ */ new Map();
|
|
13160
|
+
const used = /* @__PURE__ */ new Set();
|
|
13161
|
+
const addAdj = (v, i) => {
|
|
13162
|
+
let list = adj.get(v);
|
|
13163
|
+
if (!list) {
|
|
13164
|
+
list = [];
|
|
13165
|
+
adj.set(v, list);
|
|
13166
|
+
}
|
|
13167
|
+
list.push(i);
|
|
13168
|
+
};
|
|
13169
|
+
segs.forEach(([a, b], i) => {
|
|
13170
|
+
addAdj(a, i);
|
|
13171
|
+
addAdj(b, i);
|
|
13172
|
+
});
|
|
13173
|
+
const otherEnd = (i, v) => {
|
|
13174
|
+
const s = segs[i] ?? [v, v];
|
|
13175
|
+
return s[0] === v ? s[1] : s[0];
|
|
13176
|
+
};
|
|
13177
|
+
const chains = [];
|
|
13178
|
+
for (let start = 0; start < segs.length; start++) {
|
|
13179
|
+
if (used.has(start)) continue;
|
|
13180
|
+
used.add(start);
|
|
13181
|
+
const s = segs[start] ?? [0, 0];
|
|
13182
|
+
const chain = [s[0], s[1]];
|
|
13183
|
+
for (let guard = 0; guard < segs.length; guard++) {
|
|
13184
|
+
const tail = chain[chain.length - 1] ?? 0;
|
|
13185
|
+
const next = (adj.get(tail) ?? []).find((i) => !used.has(i));
|
|
13186
|
+
if (next === void 0) break;
|
|
13187
|
+
used.add(next);
|
|
13188
|
+
chain.push(otherEnd(next, tail));
|
|
13189
|
+
}
|
|
13190
|
+
for (let guard = 0; guard < segs.length; guard++) {
|
|
13191
|
+
const head = chain[0] ?? 0;
|
|
13192
|
+
const prev = (adj.get(head) ?? []).find((i) => !used.has(i));
|
|
13193
|
+
if (prev === void 0) break;
|
|
13194
|
+
used.add(prev);
|
|
13195
|
+
chain.unshift(otherEnd(prev, head));
|
|
13196
|
+
}
|
|
13197
|
+
chains.push(chain);
|
|
13198
|
+
}
|
|
13199
|
+
return chains;
|
|
13200
|
+
}
|
|
13201
|
+
function buildEdge(faces, chain, pos) {
|
|
13202
|
+
const n = chain.length;
|
|
13203
|
+
const pts = new Float32Array(n * 3);
|
|
13204
|
+
const arc = new Float32Array(n);
|
|
13205
|
+
let minX = Infinity;
|
|
13206
|
+
let minY = Infinity;
|
|
13207
|
+
let minZ = Infinity;
|
|
13208
|
+
let maxX = -Infinity;
|
|
13209
|
+
let maxY = -Infinity;
|
|
13210
|
+
let maxZ = -Infinity;
|
|
13211
|
+
let len = 0;
|
|
13212
|
+
let prev = null;
|
|
13213
|
+
for (let i = 0; i < n; i++) {
|
|
13214
|
+
const p = pos(chain[i] ?? 0);
|
|
13215
|
+
pts[i * 3] = p[0];
|
|
13216
|
+
pts[i * 3 + 1] = p[1];
|
|
13217
|
+
pts[i * 3 + 2] = p[2];
|
|
13218
|
+
if (prev) len += Math.hypot(p[0] - prev[0], p[1] - prev[1], p[2] - prev[2]);
|
|
13219
|
+
arc[i] = len;
|
|
13220
|
+
prev = p;
|
|
13221
|
+
if (p[0] < minX) minX = p[0];
|
|
13222
|
+
if (p[1] < minY) minY = p[1];
|
|
13223
|
+
if (p[2] < minZ) minZ = p[2];
|
|
13224
|
+
if (p[0] > maxX) maxX = p[0];
|
|
13225
|
+
if (p[1] > maxY) maxY = p[1];
|
|
13226
|
+
if (p[2] > maxZ) maxZ = p[2];
|
|
13227
|
+
}
|
|
13228
|
+
return {
|
|
13229
|
+
__nativeEdge: true,
|
|
13230
|
+
faces,
|
|
13231
|
+
pts,
|
|
13232
|
+
arc,
|
|
13233
|
+
length: len,
|
|
13234
|
+
min: [
|
|
13235
|
+
minX,
|
|
13236
|
+
minY,
|
|
13237
|
+
minZ
|
|
13238
|
+
],
|
|
13239
|
+
max: [
|
|
13240
|
+
maxX,
|
|
13241
|
+
maxY,
|
|
13242
|
+
maxZ
|
|
13243
|
+
],
|
|
13244
|
+
curveType: isStraight(pts) ? "LINE" : "CIRCLE"
|
|
13245
|
+
};
|
|
13246
|
+
}
|
|
13247
|
+
/** Straight if every interior point lies on the chord within a small tolerance. */
|
|
13248
|
+
function isStraight(pts) {
|
|
13249
|
+
const n = pts.length / 3;
|
|
13250
|
+
if (n <= 2) return true;
|
|
13251
|
+
const ax = pts[0] ?? 0;
|
|
13252
|
+
const ay = pts[1] ?? 0;
|
|
13253
|
+
const az = pts[2] ?? 0;
|
|
13254
|
+
const bx = pts[(n - 1) * 3] ?? 0;
|
|
13255
|
+
const by = pts[(n - 1) * 3 + 1] ?? 0;
|
|
13256
|
+
const bz = pts[(n - 1) * 3 + 2] ?? 0;
|
|
13257
|
+
let dx = bx - ax;
|
|
13258
|
+
let dy = by - ay;
|
|
13259
|
+
let dz = bz - az;
|
|
13260
|
+
const dl = Math.hypot(dx, dy, dz) || 1;
|
|
13261
|
+
dx /= dl;
|
|
13262
|
+
dy /= dl;
|
|
13263
|
+
dz /= dl;
|
|
13264
|
+
for (let i = 1; i < n - 1; i++) {
|
|
13265
|
+
const px = (pts[i * 3] ?? 0) - ax;
|
|
13266
|
+
const py = (pts[i * 3 + 1] ?? 0) - ay;
|
|
13267
|
+
const pz = (pts[i * 3 + 2] ?? 0) - az;
|
|
13268
|
+
const t = px * dx + py * dy + pz * dz;
|
|
13269
|
+
const ex = px - t * dx;
|
|
13270
|
+
const ey = py - t * dy;
|
|
13271
|
+
const ez = pz - t * dz;
|
|
13272
|
+
if (Math.hypot(ex, ey, ez) > 1e-6 * Math.max(1, dl)) return false;
|
|
13273
|
+
}
|
|
13274
|
+
return true;
|
|
13275
|
+
}
|
|
13276
|
+
/**
|
|
13277
|
+
* Extract B-rep vertices: mesh vertices where three or more distinct faces
|
|
13278
|
+
* (faceID groups) meet — i.e. the corners where edges terminate. A box yields
|
|
13279
|
+
* its 8 corners; interior/edge-midpoint vertices (1–2 faces) are excluded.
|
|
13280
|
+
*/
|
|
13281
|
+
function extractVertices(meshUnknown) {
|
|
13282
|
+
const mesh = meshUnknown;
|
|
13283
|
+
const tv = mesh.triVerts;
|
|
13284
|
+
const vp = mesh.vertProperties;
|
|
13285
|
+
const stride = mesh.numProp || 3;
|
|
13286
|
+
const faceID = mesh.faceID;
|
|
13287
|
+
if (!faceID) return [];
|
|
13288
|
+
const triCount = tv.length / 3;
|
|
13289
|
+
const facesAtVert = /* @__PURE__ */ new Map();
|
|
13290
|
+
for (let t = 0; t < triCount; t++) {
|
|
13291
|
+
const fid = faceID[t] ?? t;
|
|
13292
|
+
for (let j = 0; j < 3; j++) {
|
|
13293
|
+
const v = tv[t * 3 + j] ?? 0;
|
|
13294
|
+
let s = facesAtVert.get(v);
|
|
13295
|
+
if (!s) {
|
|
13296
|
+
s = /* @__PURE__ */ new Set();
|
|
13297
|
+
facesAtVert.set(v, s);
|
|
13298
|
+
}
|
|
13299
|
+
s.add(fid);
|
|
13300
|
+
}
|
|
13301
|
+
}
|
|
13302
|
+
const verts = [];
|
|
13303
|
+
for (const [v, faces] of facesAtVert) {
|
|
13304
|
+
if (faces.size < 3) continue;
|
|
13305
|
+
const o = v * stride;
|
|
13306
|
+
verts.push({
|
|
13307
|
+
__nativeVertex: true,
|
|
13308
|
+
point: [
|
|
13309
|
+
vp[o] ?? 0,
|
|
13310
|
+
vp[o + 1] ?? 0,
|
|
13311
|
+
vp[o + 2] ?? 0
|
|
13312
|
+
]
|
|
13313
|
+
});
|
|
13314
|
+
}
|
|
13315
|
+
return verts;
|
|
13316
|
+
}
|
|
13317
|
+
function isNativeVertex(shape) {
|
|
13318
|
+
return !!shape && typeof shape === "object" && shape.__nativeVertex === true;
|
|
13319
|
+
}
|
|
13320
|
+
function isNativeEdge(shape) {
|
|
13321
|
+
return !!shape && typeof shape === "object" && shape.__nativeEdge === true;
|
|
13322
|
+
}
|
|
13323
|
+
/** Point at arc-length `s` along the edge polyline. */
|
|
13324
|
+
function edgePointAt(edge, s) {
|
|
13325
|
+
const { pts, arc } = edge;
|
|
13326
|
+
const n = arc.length;
|
|
13327
|
+
if (n === 0) return [
|
|
13328
|
+
0,
|
|
13329
|
+
0,
|
|
13330
|
+
0
|
|
13331
|
+
];
|
|
13332
|
+
if (s <= 0) return [
|
|
13333
|
+
pts[0] ?? 0,
|
|
13334
|
+
pts[1] ?? 0,
|
|
13335
|
+
pts[2] ?? 0
|
|
13336
|
+
];
|
|
13337
|
+
if (s >= edge.length) return [
|
|
13338
|
+
pts[(n - 1) * 3] ?? 0,
|
|
13339
|
+
pts[(n - 1) * 3 + 1] ?? 0,
|
|
13340
|
+
pts[(n - 1) * 3 + 2] ?? 0
|
|
13341
|
+
];
|
|
13342
|
+
let i = 1;
|
|
13343
|
+
while (i < n && (arc[i] ?? 0) < s) i++;
|
|
13344
|
+
const a0 = arc[i - 1] ?? 0;
|
|
13345
|
+
const a1 = arc[i] ?? a0;
|
|
13346
|
+
const t = a1 > a0 ? (s - a0) / (a1 - a0) : 0;
|
|
13347
|
+
const o0 = (i - 1) * 3;
|
|
13348
|
+
const o1 = i * 3;
|
|
13349
|
+
return [
|
|
13350
|
+
(pts[o0] ?? 0) + ((pts[o1] ?? 0) - (pts[o0] ?? 0)) * t,
|
|
13351
|
+
(pts[o0 + 1] ?? 0) + ((pts[o1 + 1] ?? 0) - (pts[o0 + 1] ?? 0)) * t,
|
|
13352
|
+
(pts[o0 + 2] ?? 0) + ((pts[o1 + 2] ?? 0) - (pts[o0 + 2] ?? 0)) * t
|
|
13353
|
+
];
|
|
13354
|
+
}
|
|
13355
|
+
/** Unit tangent at arc-length `s` (direction of the containing segment). */
|
|
13356
|
+
function edgeTangentAt(edge, s) {
|
|
13357
|
+
const { pts, arc } = edge;
|
|
13358
|
+
const n = arc.length;
|
|
13359
|
+
if (n < 2) return [
|
|
13360
|
+
1,
|
|
13361
|
+
0,
|
|
13362
|
+
0
|
|
13363
|
+
];
|
|
13364
|
+
let i = 1;
|
|
13365
|
+
while (i < n - 1 && (arc[i] ?? 0) < s) i++;
|
|
13366
|
+
const o0 = (i - 1) * 3;
|
|
13367
|
+
const o1 = i * 3;
|
|
13368
|
+
const dx = (pts[o1] ?? 0) - (pts[o0] ?? 0);
|
|
13369
|
+
const dy = (pts[o1 + 1] ?? 0) - (pts[o0 + 1] ?? 0);
|
|
13370
|
+
const dz = (pts[o1 + 2] ?? 0) - (pts[o0 + 2] ?? 0);
|
|
13371
|
+
const l = Math.hypot(dx, dy, dz) || 1;
|
|
13372
|
+
return [
|
|
13373
|
+
dx / l,
|
|
13374
|
+
dy / l,
|
|
13375
|
+
dz / l
|
|
13376
|
+
];
|
|
13377
|
+
}
|
|
13378
|
+
//#endregion
|
|
12060
13379
|
//#region src/kernel/manifold/topologyOps.ts
|
|
12061
13380
|
function brepOf(shape, method) {
|
|
12062
13381
|
const ms = asManifoldShape(shape);
|
|
@@ -12101,7 +13420,50 @@ function iterShapes(shape, type) {
|
|
|
12101
13420
|
const s = asManifoldShape(shape);
|
|
12102
13421
|
if (!s) return [];
|
|
12103
13422
|
if (type === "solid") return [shape];
|
|
13423
|
+
if (type === "vertex") {
|
|
13424
|
+
const solid = unwrap(s);
|
|
13425
|
+
if (solid && typeof solid.getMesh === "function") return extractVertices(solid.getMesh()).map((v, index) => ({
|
|
13426
|
+
...v,
|
|
13427
|
+
__manifoldSub: true,
|
|
13428
|
+
index,
|
|
13429
|
+
box: {
|
|
13430
|
+
min: v.point,
|
|
13431
|
+
max: v.point
|
|
13432
|
+
},
|
|
13433
|
+
parent: s.node,
|
|
13434
|
+
subType: "vertex"
|
|
13435
|
+
}));
|
|
13436
|
+
return [];
|
|
13437
|
+
}
|
|
12104
13438
|
if (type !== "edge" && type !== "face") return [];
|
|
13439
|
+
if (type === "face") {
|
|
13440
|
+
const solid = unwrap(s);
|
|
13441
|
+
if (solid && typeof solid.getMesh === "function") return extractFaces(solid.getMesh()).map((f, index) => ({
|
|
13442
|
+
...f,
|
|
13443
|
+
__manifoldSub: true,
|
|
13444
|
+
index,
|
|
13445
|
+
box: {
|
|
13446
|
+
min: f.min,
|
|
13447
|
+
max: f.max
|
|
13448
|
+
},
|
|
13449
|
+
parent: s.node,
|
|
13450
|
+
subType: "face"
|
|
13451
|
+
}));
|
|
13452
|
+
}
|
|
13453
|
+
if (type === "edge") {
|
|
13454
|
+
const solid = unwrap(s);
|
|
13455
|
+
if (solid && typeof solid.getMesh === "function") return extractEdges(solid.getMesh()).map((e, index) => ({
|
|
13456
|
+
...e,
|
|
13457
|
+
__manifoldSub: true,
|
|
13458
|
+
index,
|
|
13459
|
+
box: {
|
|
13460
|
+
min: e.min,
|
|
13461
|
+
max: e.max
|
|
13462
|
+
},
|
|
13463
|
+
parent: s.node,
|
|
13464
|
+
subType: "edge"
|
|
13465
|
+
}));
|
|
13466
|
+
}
|
|
12105
13467
|
if (!s.node.replayable) return [];
|
|
12106
13468
|
const occt = resolveOcct();
|
|
12107
13469
|
if (!occt) return [];
|
|
@@ -12113,7 +13475,10 @@ function iterShapes(shape, type) {
|
|
|
12113
13475
|
return occt.iterShapes(brep, type).map((sub, index) => ({
|
|
12114
13476
|
__manifoldSub: true,
|
|
12115
13477
|
index,
|
|
12116
|
-
box: occt.boundingBox(sub)
|
|
13478
|
+
box: occt.boundingBox(sub),
|
|
13479
|
+
occt: sub,
|
|
13480
|
+
parent: s.node,
|
|
13481
|
+
subType: type
|
|
12117
13482
|
}));
|
|
12118
13483
|
}
|
|
12119
13484
|
function edgeToFaceMap(shape) {
|
|
@@ -12688,6 +14053,12 @@ function vertexAt(mesh, index) {
|
|
|
12688
14053
|
];
|
|
12689
14054
|
}
|
|
12690
14055
|
function viaOcct(shape, query) {
|
|
14056
|
+
const witness = shape;
|
|
14057
|
+
if (witness && witness.__manifoldSub && witness.occt) {
|
|
14058
|
+
const occt = resolveOcct();
|
|
14059
|
+
if (!occt) throw new Error("manifold: sub-shape geometry query requires a registered occt kernel");
|
|
14060
|
+
return query(witness.occt, occt);
|
|
14061
|
+
}
|
|
12691
14062
|
const ms = asManifoldShape(shape);
|
|
12692
14063
|
if (!ms) throw new Error("manifold: exact geometry query requires a manifold shape handle");
|
|
12693
14064
|
const occt = resolveOcct();
|
|
@@ -12700,15 +14071,56 @@ function viaOcct(shape, query) {
|
|
|
12700
14071
|
}
|
|
12701
14072
|
return query(occtShape, occt);
|
|
12702
14073
|
}
|
|
14074
|
+
/** The analytic curve descriptor of a standalone profile edge, if it has one. */
|
|
14075
|
+
function descOf(shape) {
|
|
14076
|
+
const ms = asManifoldShape(shape);
|
|
14077
|
+
if (!ms) return void 0;
|
|
14078
|
+
const node = ms.node;
|
|
14079
|
+
return node.op === "profileEdge" ? node.params?.curve : void 0;
|
|
14080
|
+
}
|
|
12703
14081
|
function makeGeometryOps(_module) {
|
|
12704
14082
|
return {
|
|
12705
|
-
curveType: (shape) =>
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
14083
|
+
curveType: (shape) => {
|
|
14084
|
+
const d = descOf(shape);
|
|
14085
|
+
if (d) return descType(d);
|
|
14086
|
+
return isNativeEdge(shape) ? shape.curveType : viaOcct(shape, (s, occt) => occt.curveType(s));
|
|
14087
|
+
},
|
|
14088
|
+
curveParameters: (shape) => {
|
|
14089
|
+
const d = descOf(shape);
|
|
14090
|
+
if (d) {
|
|
14091
|
+
const b = descBounds(d);
|
|
14092
|
+
return [b.first, b.last];
|
|
14093
|
+
}
|
|
14094
|
+
return isNativeEdge(shape) ? [0, shape.length] : viaOcct(shape, (s, occt) => occt.curveParameters(s));
|
|
14095
|
+
},
|
|
14096
|
+
curvePointAtParam: (shape, param) => {
|
|
14097
|
+
const d = descOf(shape);
|
|
14098
|
+
if (d) return descPointAt(d, param);
|
|
14099
|
+
return isNativeEdge(shape) ? edgePointAt(shape, param) : viaOcct(shape, (s, occt) => occt.curvePointAtParam(s, param));
|
|
14100
|
+
},
|
|
14101
|
+
curveTangent: (shape, param) => {
|
|
14102
|
+
const d = descOf(shape);
|
|
14103
|
+
if (d) return {
|
|
14104
|
+
point: descPointAt(d, param),
|
|
14105
|
+
tangent: descTangent(d, param)
|
|
14106
|
+
};
|
|
14107
|
+
return isNativeEdge(shape) ? {
|
|
14108
|
+
point: edgePointAt(shape, param),
|
|
14109
|
+
tangent: edgeTangentAt(shape, param)
|
|
14110
|
+
} : viaOcct(shape, (s, occt) => occt.curveTangent(s, param));
|
|
14111
|
+
},
|
|
14112
|
+
curveIsClosed: (shape) => {
|
|
14113
|
+
const d = descOf(shape);
|
|
14114
|
+
return d ? descIsClosed(d) : viaOcct(shape, (s, occt) => occt.curveIsClosed(s));
|
|
14115
|
+
},
|
|
14116
|
+
curveIsPeriodic: (shape) => {
|
|
14117
|
+
const d = descOf(shape);
|
|
14118
|
+
return d ? descIsPeriodic(d) : viaOcct(shape, (s, occt) => occt.curveIsPeriodic(s));
|
|
14119
|
+
},
|
|
14120
|
+
curvePeriod: (shape) => {
|
|
14121
|
+
const d = descOf(shape);
|
|
14122
|
+
return d ? descPeriod(d) : viaOcct(shape, (s, occt) => occt.curvePeriod(s));
|
|
14123
|
+
},
|
|
12712
14124
|
interpolatePoints: (points, options) => occtOrThrow("interpolatePoints").interpolatePoints(points, options),
|
|
12713
14125
|
approximatePoints: (points, options) => occtOrThrow("approximatePoints").approximatePoints(points, options),
|
|
12714
14126
|
curveDegreeElevate: (edge, elevateBy) => viaOcct(edge, (s, occt) => occt.curveDegreeElevate(s, elevateBy)),
|
|
@@ -12719,13 +14131,24 @@ function makeGeometryOps(_module) {
|
|
|
12719
14131
|
getBezierPenultimatePole: (edge) => viaOcct(edge, (s, occt) => occt.getBezierPenultimatePole(s)),
|
|
12720
14132
|
getNurbsCurveData: (edge) => viaOcct(edge, (s, occt) => occt.getNurbsCurveData?.(s) ?? null),
|
|
12721
14133
|
vertexPosition: (vertex) => {
|
|
14134
|
+
if (isNativeVertex(vertex)) return vertex.point;
|
|
12722
14135
|
if (!asManifoldShape(vertex)) return viaOcct(vertex, (s, occt) => occt.vertexPosition(s));
|
|
12723
14136
|
return vertexAt(meshOf(vertex), 0);
|
|
12724
14137
|
},
|
|
12725
|
-
surfaceType: (face) =>
|
|
12726
|
-
|
|
14138
|
+
surfaceType: (face) => {
|
|
14139
|
+
if (isNativeFace(face)) return "plane";
|
|
14140
|
+
const ms = asManifoldShape(face);
|
|
14141
|
+
if (ms && ms.node.op === "profileFace") return "plane";
|
|
14142
|
+
return viaOcct(face, (s, occt) => occt.surfaceType(s));
|
|
14143
|
+
},
|
|
14144
|
+
uvBounds: (face) => isNativeFace(face) ? {
|
|
14145
|
+
uMin: 0,
|
|
14146
|
+
uMax: 1,
|
|
14147
|
+
vMin: 0,
|
|
14148
|
+
vMax: 1
|
|
14149
|
+
} : viaOcct(face, (s, occt) => occt.uvBounds(s)),
|
|
12727
14150
|
outerWire: (face) => viaOcct(face, (s, occt) => occt.outerWire(s)),
|
|
12728
|
-
surfaceNormal: (face, u, v) => viaOcct(face, (s, occt) => occt.surfaceNormal(s, u, v)),
|
|
14151
|
+
surfaceNormal: (face, u, v) => isNativeFace(face) ? face.normal : viaOcct(face, (s, occt) => occt.surfaceNormal(s, u, v)),
|
|
12729
14152
|
pointOnSurface: (face, u, v) => viaOcct(face, (s, occt) => occt.pointOnSurface(s, u, v)),
|
|
12730
14153
|
uvFromPoint: (face, point) => viaOcct(face, (s, occt) => occt.uvFromPoint(s, point)),
|
|
12731
14154
|
projectPointOnFace: (face, point) => viaOcct(face, (s, occt) => occt.projectPointOnFace(s, point)),
|
|
@@ -12819,6 +14242,614 @@ function makeRepairOps(_module) {
|
|
|
12819
14242
|
};
|
|
12820
14243
|
}
|
|
12821
14244
|
//#endregion
|
|
14245
|
+
//#region src/kernel/manifold/kernel2dNative.ts
|
|
14246
|
+
var CHORD_TOL = .004;
|
|
14247
|
+
function isNative(x) {
|
|
14248
|
+
return !!x && typeof x === "object" && x.__nativeC2d === true;
|
|
14249
|
+
}
|
|
14250
|
+
function line(p1, p2) {
|
|
14251
|
+
return {
|
|
14252
|
+
__nativeC2d: true,
|
|
14253
|
+
k: "line",
|
|
14254
|
+
p1,
|
|
14255
|
+
p2
|
|
14256
|
+
};
|
|
14257
|
+
}
|
|
14258
|
+
function conic(c, u, v, a0, a1) {
|
|
14259
|
+
return {
|
|
14260
|
+
__nativeC2d: true,
|
|
14261
|
+
k: "conic",
|
|
14262
|
+
c,
|
|
14263
|
+
u,
|
|
14264
|
+
v,
|
|
14265
|
+
a0,
|
|
14266
|
+
a1
|
|
14267
|
+
};
|
|
14268
|
+
}
|
|
14269
|
+
function applyPt(m, [x, y]) {
|
|
14270
|
+
return [m.a * x + m.b * y + m.tx, m.c * x + m.d * y + m.ty];
|
|
14271
|
+
}
|
|
14272
|
+
function applyVec(m, [x, y]) {
|
|
14273
|
+
return [m.a * x + m.b * y, m.c * x + m.d * y];
|
|
14274
|
+
}
|
|
14275
|
+
function compose(p, q) {
|
|
14276
|
+
return {
|
|
14277
|
+
a: p.a * q.a + p.b * q.c,
|
|
14278
|
+
b: p.a * q.b + p.b * q.d,
|
|
14279
|
+
c: p.c * q.a + p.d * q.c,
|
|
14280
|
+
d: p.c * q.b + p.d * q.d,
|
|
14281
|
+
tx: p.a * q.tx + p.b * q.ty + p.tx,
|
|
14282
|
+
ty: p.c * q.tx + p.d * q.ty + p.ty
|
|
14283
|
+
};
|
|
14284
|
+
}
|
|
14285
|
+
function transform(curve, m) {
|
|
14286
|
+
if (curve.k === "line") return line(applyPt(m, curve.p1), applyPt(m, curve.p2));
|
|
14287
|
+
if (curve.k === "bezier") return {
|
|
14288
|
+
__nativeC2d: true,
|
|
14289
|
+
k: "bezier",
|
|
14290
|
+
pts: curve.pts.map((p) => applyPt(m, p))
|
|
14291
|
+
};
|
|
14292
|
+
return conic(applyPt(m, curve.c), applyVec(m, curve.u), applyVec(m, curve.v), curve.a0, curve.a1);
|
|
14293
|
+
}
|
|
14294
|
+
function bezierAt(pts, t) {
|
|
14295
|
+
const tmp = pts.map((p) => [p[0], p[1]]);
|
|
14296
|
+
for (let k = 1; k < tmp.length; k++) for (let i = 0; i < tmp.length - k; i++) {
|
|
14297
|
+
const a = tmp[i] ?? [0, 0];
|
|
14298
|
+
const b = tmp[i + 1] ?? [0, 0];
|
|
14299
|
+
tmp[i] = [a[0] * (1 - t) + b[0] * t, a[1] * (1 - t) + b[1] * t];
|
|
14300
|
+
}
|
|
14301
|
+
return tmp[0] ?? [0, 0];
|
|
14302
|
+
}
|
|
14303
|
+
function conicPoint(c, t) {
|
|
14304
|
+
const ct = Math.cos(t);
|
|
14305
|
+
const st = Math.sin(t);
|
|
14306
|
+
return [c.c[0] + c.u[0] * ct + c.v[0] * st, c.c[1] + c.u[1] * ct + c.v[1] * st];
|
|
14307
|
+
}
|
|
14308
|
+
/** Bezier first derivative B'(t) = n·Σ(P_{i+1}−P_i)·b_{i,n−1}(t). */
|
|
14309
|
+
function bezierD1(pts, t) {
|
|
14310
|
+
const n = pts.length - 1;
|
|
14311
|
+
if (n < 1) return [0, 0];
|
|
14312
|
+
const diff = [];
|
|
14313
|
+
for (let i = 0; i < n; i++) {
|
|
14314
|
+
const a = pts[i] ?? [0, 0];
|
|
14315
|
+
const b = pts[i + 1] ?? [0, 0];
|
|
14316
|
+
diff.push([n * (b[0] - a[0]), n * (b[1] - a[1])]);
|
|
14317
|
+
}
|
|
14318
|
+
return bezierAt(diff, t);
|
|
14319
|
+
}
|
|
14320
|
+
/**
|
|
14321
|
+
* Segment count for a conic arc, driven by chord-height tolerance rather than a
|
|
14322
|
+
* fixed count: the polygon's sagitta is `ρ(1 − cos(Δθ/2))`, so to keep it under
|
|
14323
|
+
* CHORD_TOL the per-segment angle must satisfy `Δθ ≤ 2·acos(1 − tol/ρ)`. This
|
|
14324
|
+
* gives tiny gridfinity corner arcs few segments (fast) while large circles get
|
|
14325
|
+
* enough to match OCCT's exact volume (parity). `ρ` is the larger conjugate
|
|
14326
|
+
* radius (ellipse worst case).
|
|
14327
|
+
*/
|
|
14328
|
+
function conicSegments(curve) {
|
|
14329
|
+
const span = Math.abs(curve.a1 - curve.a0);
|
|
14330
|
+
const ru = Math.hypot(curve.u[0], curve.u[1]);
|
|
14331
|
+
const rv = Math.hypot(curve.v[0], curve.v[1]);
|
|
14332
|
+
const rho = Math.max(ru, rv);
|
|
14333
|
+
if (rho <= CHORD_TOL) return Math.max(2, Math.ceil(span / (2 * Math.PI) * 8));
|
|
14334
|
+
const maxAngle = 2 * Math.acos(Math.max(-1, 1 - CHORD_TOL / rho));
|
|
14335
|
+
return Math.max(2, Math.ceil(span / maxAngle));
|
|
14336
|
+
}
|
|
14337
|
+
function sample(curve) {
|
|
14338
|
+
if (curve.k === "line") return [curve.p1, curve.p2];
|
|
14339
|
+
if (curve.k === "bezier") {
|
|
14340
|
+
const out = [];
|
|
14341
|
+
for (let i = 0; i <= 24; i++) out.push(bezierAt(curve.pts, i / 24));
|
|
14342
|
+
return out;
|
|
14343
|
+
}
|
|
14344
|
+
const span = curve.a1 - curve.a0;
|
|
14345
|
+
const n = conicSegments(curve);
|
|
14346
|
+
const pts = [];
|
|
14347
|
+
for (let i = 0; i <= n; i++) {
|
|
14348
|
+
const t = curve.a0 + span * i / n;
|
|
14349
|
+
const ct = Math.cos(t);
|
|
14350
|
+
const st = Math.sin(t);
|
|
14351
|
+
pts.push([curve.c[0] + curve.u[0] * ct + curve.v[0] * st, curve.c[1] + curve.u[1] * ct + curve.v[1] * st]);
|
|
14352
|
+
}
|
|
14353
|
+
return pts;
|
|
14354
|
+
}
|
|
14355
|
+
/** Circle/arc through param helpers. */
|
|
14356
|
+
function circleThrough3(p1, pm, p2) {
|
|
14357
|
+
const ax = p1[0];
|
|
14358
|
+
const ay = p1[1];
|
|
14359
|
+
const bx = pm[0];
|
|
14360
|
+
const by = pm[1];
|
|
14361
|
+
const cx2 = p2[0];
|
|
14362
|
+
const cy2 = p2[1];
|
|
14363
|
+
const d = 2 * (ax * (by - cy2) + bx * (cy2 - ay) + cx2 * (ay - by));
|
|
14364
|
+
if (Math.abs(d) < 1e-12) return line(p1, p2);
|
|
14365
|
+
const ux = ((ax * ax + ay * ay) * (by - cy2) + (bx * bx + by * by) * (cy2 - ay) + (cx2 * cx2 + cy2 * cy2) * (ay - by)) / d;
|
|
14366
|
+
const uy = ((ax * ax + ay * ay) * (cx2 - bx) + (bx * bx + by * by) * (ax - cx2) + (cx2 * cx2 + cy2 * cy2) * (bx - ax)) / d;
|
|
14367
|
+
const center = [ux, uy];
|
|
14368
|
+
const r = Math.hypot(ax - ux, ay - uy);
|
|
14369
|
+
const ang = (p) => Math.atan2(p[1] - uy, p[0] - ux);
|
|
14370
|
+
const a0 = ang(p1);
|
|
14371
|
+
const am = ang(pm);
|
|
14372
|
+
let a1 = ang(p2);
|
|
14373
|
+
const norm = (x) => (x % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
|
|
14374
|
+
const dm = norm(am - a0);
|
|
14375
|
+
const d1 = norm(a1 - a0);
|
|
14376
|
+
if (dm <= d1) a1 = a0 + d1;
|
|
14377
|
+
else a1 = a0 - norm(a0 - a1);
|
|
14378
|
+
return conic(center, [r, 0], [0, r], a0, a1);
|
|
14379
|
+
}
|
|
14380
|
+
function makeNativeKernel2DOps(module, occt) {
|
|
14381
|
+
const PLACEHOLDER = {
|
|
14382
|
+
delete: () => {},
|
|
14383
|
+
isEmpty: () => false
|
|
14384
|
+
};
|
|
14385
|
+
function delegate(method, ...args) {
|
|
14386
|
+
const o = occt();
|
|
14387
|
+
const fn = o?.[method];
|
|
14388
|
+
if (!fn) throw new Error(`manifold 2D: ${method} needs an OCCT kernel (none registered)`);
|
|
14389
|
+
return fn.call(o, ...args);
|
|
14390
|
+
}
|
|
14391
|
+
const asC = (h) => h;
|
|
14392
|
+
function occtOr(method) {
|
|
14393
|
+
const o = occt();
|
|
14394
|
+
if (!o) throw new Error(`manifold 2D: ${method} needs an OCCT kernel (none registered)`);
|
|
14395
|
+
return o;
|
|
14396
|
+
}
|
|
14397
|
+
/**
|
|
14398
|
+
* Reconstruct a native curve descriptor as an *exact* OCCT 2D curve so it can
|
|
14399
|
+
* flow into OCCT-only paths (2D booleans, intersection, NURBS). Curve type is
|
|
14400
|
+
* preserved — line→line, circle→circle, arc→3-point arc, ellipse→ellipse —
|
|
14401
|
+
* which keeps OCCT's shared-edge detection working in blueprint booleans.
|
|
14402
|
+
* Skewed/odd cases fall back to a dense B-spline. OCCT handles pass through.
|
|
14403
|
+
*/
|
|
14404
|
+
function toOcct(h) {
|
|
14405
|
+
if (!isNative(h)) return h;
|
|
14406
|
+
const o = occtOr("toOcct");
|
|
14407
|
+
const c = asC(h);
|
|
14408
|
+
if (c.k === "line") return o.makeLine2d(c.p1[0], c.p1[1], c.p2[0], c.p2[1]);
|
|
14409
|
+
if (c.k === "bezier") return o.makeBezier2d(c.pts);
|
|
14410
|
+
const ru = Math.hypot(c.u[0], c.u[1]);
|
|
14411
|
+
const rv = Math.hypot(c.v[0], c.v[1]);
|
|
14412
|
+
const full = Math.abs(Math.abs(c.a1 - c.a0) - 2 * Math.PI) < 1e-9;
|
|
14413
|
+
const sense = c.u[0] * c.v[1] - c.u[1] * c.v[0] >= 0;
|
|
14414
|
+
if (Math.abs(ru - rv) < 1e-9 * Math.max(1, ru)) {
|
|
14415
|
+
if (full) return o.makeCircle2d(c.c[0], c.c[1], ru, sense);
|
|
14416
|
+
const p0 = conicPoint(c, c.a0);
|
|
14417
|
+
const pm = conicPoint(c, (c.a0 + c.a1) / 2);
|
|
14418
|
+
const p1 = conicPoint(c, c.a1);
|
|
14419
|
+
return o.makeArc2dThreePoints(p0[0], p0[1], pm[0], pm[1], p1[0], p1[1]);
|
|
14420
|
+
}
|
|
14421
|
+
const dotUV = c.u[0] * c.v[0] + c.u[1] * c.v[1];
|
|
14422
|
+
if (Math.abs(dotUV) < 1e-9 * Math.max(1, ru * rv)) {
|
|
14423
|
+
const xdx = c.u[0] / ru;
|
|
14424
|
+
const xdy = c.u[1] / ru;
|
|
14425
|
+
return full ? o.makeEllipse2d(c.c[0], c.c[1], ru, rv, xdx, xdy, sense) : o.makeEllipseArc2d(c.c[0], c.c[1], ru, rv, c.a0, c.a1, xdx, xdy, sense);
|
|
14426
|
+
}
|
|
14427
|
+
return o.makeBSpline2d(sample(c).map((p) => [p[0], p[1]]));
|
|
14428
|
+
}
|
|
14429
|
+
const impl = {
|
|
14430
|
+
createPoint2d: (x, y) => ({
|
|
14431
|
+
x,
|
|
14432
|
+
y
|
|
14433
|
+
}),
|
|
14434
|
+
createDirection2d: (x, y) => ({
|
|
14435
|
+
x,
|
|
14436
|
+
y
|
|
14437
|
+
}),
|
|
14438
|
+
createVector2d: (x, y) => ({
|
|
14439
|
+
x,
|
|
14440
|
+
y
|
|
14441
|
+
}),
|
|
14442
|
+
createAxis2d: (px, py, dx, dy) => ({
|
|
14443
|
+
px,
|
|
14444
|
+
py,
|
|
14445
|
+
dx,
|
|
14446
|
+
dy
|
|
14447
|
+
}),
|
|
14448
|
+
wrapCurve2dHandle: (h) => h,
|
|
14449
|
+
createCurve2dAdaptor: (h) => h,
|
|
14450
|
+
makeLine2d: (x1, y1, x2, y2) => line([x1, y1], [x2, y2]),
|
|
14451
|
+
makeCircle2d: (cx, cy, r, sense = true) => conic([cx, cy], [r, 0], sense ? [0, r] : [0, -r], 0, 2 * Math.PI),
|
|
14452
|
+
makeArc2dThreePoints: (x1, y1, xm, ym, x2, y2) => circleThrough3([x1, y1], [xm, ym], [x2, y2]),
|
|
14453
|
+
makeArc2dTangent: (sx, sy, tx, ty, ex, ey) => {
|
|
14454
|
+
const tlen = Math.hypot(tx, ty) || 1;
|
|
14455
|
+
const nx = -ty / tlen;
|
|
14456
|
+
const ny = tx / tlen;
|
|
14457
|
+
const chord = [ex - sx, ey - sy];
|
|
14458
|
+
const ndotc = nx * chord[0] + ny * chord[1];
|
|
14459
|
+
if (Math.abs(ndotc) < 1e-12) return line([sx, sy], [ex, ey]);
|
|
14460
|
+
const t = (chord[0] * chord[0] + chord[1] * chord[1]) / (2 * ndotc);
|
|
14461
|
+
const cx = sx + nx * t;
|
|
14462
|
+
const cy = sy + ny * t;
|
|
14463
|
+
const r = Math.hypot(sx - cx, sy - cy);
|
|
14464
|
+
const a0 = Math.atan2(sy - cy, sx - cx);
|
|
14465
|
+
let a1 = Math.atan2(ey - cy, ex - cx);
|
|
14466
|
+
nx * tx + ny * ty;
|
|
14467
|
+
const norm = (x) => (x % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
|
|
14468
|
+
const tangCCW = [-(sy - cy), sx - cx];
|
|
14469
|
+
if (tangCCW[0] * tx + tangCCW[1] * ty >= 0) a1 = a0 + norm(a1 - a0);
|
|
14470
|
+
else a1 = a0 - norm(a0 - a1);
|
|
14471
|
+
return conic([cx, cy], [r, 0], [0, r], a0, a1);
|
|
14472
|
+
},
|
|
14473
|
+
makeEllipse2d: (cx, cy, maj, min, xdx = 1, xdy = 0, sense = true) => {
|
|
14474
|
+
const xl = Math.hypot(xdx, xdy) || 1;
|
|
14475
|
+
const ux = xdx / xl * maj;
|
|
14476
|
+
const uy = xdy / xl * maj;
|
|
14477
|
+
const vx = -xdy / xl * min * (sense ? 1 : -1);
|
|
14478
|
+
const vy = xdx / xl * min * (sense ? 1 : -1);
|
|
14479
|
+
return conic([cx, cy], [ux, uy], [vx, vy], 0, 2 * Math.PI);
|
|
14480
|
+
},
|
|
14481
|
+
makeEllipseArc2d: (cx, cy, maj, min, a0, a1, xdx = 1, xdy = 0, sense = true) => {
|
|
14482
|
+
const xl = Math.hypot(xdx, xdy) || 1;
|
|
14483
|
+
const ux = xdx / xl * maj;
|
|
14484
|
+
const uy = xdy / xl * maj;
|
|
14485
|
+
const vx = -xdy / xl * min * (sense ? 1 : -1);
|
|
14486
|
+
const vy = xdx / xl * min * (sense ? 1 : -1);
|
|
14487
|
+
return conic([cx, cy], [ux, uy], [vx, vy], a0, a1);
|
|
14488
|
+
},
|
|
14489
|
+
makeBezier2d: (points) => ({
|
|
14490
|
+
__nativeC2d: true,
|
|
14491
|
+
k: "bezier",
|
|
14492
|
+
pts: points.map((p) => [p[0], p[1]])
|
|
14493
|
+
}),
|
|
14494
|
+
evaluateCurve2d: (h, param) => {
|
|
14495
|
+
const c = asC(h);
|
|
14496
|
+
if (c.k === "line") {
|
|
14497
|
+
const dx = c.p2[0] - c.p1[0];
|
|
14498
|
+
const dy = c.p2[1] - c.p1[1];
|
|
14499
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
14500
|
+
return [c.p1[0] + dx * param / len, c.p1[1] + dy * param / len];
|
|
14501
|
+
}
|
|
14502
|
+
if (c.k === "bezier") return bezierAt(c.pts, param);
|
|
14503
|
+
return [c.c[0] + c.u[0] * Math.cos(param) + c.v[0] * Math.sin(param), c.c[1] + c.u[1] * Math.cos(param) + c.v[1] * Math.sin(param)];
|
|
14504
|
+
},
|
|
14505
|
+
evaluateCurve2dD1: (h, param) => {
|
|
14506
|
+
const c = asC(h);
|
|
14507
|
+
if (c.k === "line") {
|
|
14508
|
+
const dx = c.p2[0] - c.p1[0];
|
|
14509
|
+
const dy = c.p2[1] - c.p1[1];
|
|
14510
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
14511
|
+
return {
|
|
14512
|
+
point: [c.p1[0] + dx * param / len, c.p1[1] + dy * param / len],
|
|
14513
|
+
tangent: [dx / len, dy / len]
|
|
14514
|
+
};
|
|
14515
|
+
}
|
|
14516
|
+
if (c.k === "bezier") return {
|
|
14517
|
+
point: bezierAt(c.pts, param),
|
|
14518
|
+
tangent: bezierD1(c.pts, param)
|
|
14519
|
+
};
|
|
14520
|
+
const ct = Math.cos(param);
|
|
14521
|
+
const st = Math.sin(param);
|
|
14522
|
+
return {
|
|
14523
|
+
point: [c.c[0] + c.u[0] * ct + c.v[0] * st, c.c[1] + c.u[1] * ct + c.v[1] * st],
|
|
14524
|
+
tangent: [-c.u[0] * st + c.v[0] * ct, -c.u[1] * st + c.v[1] * ct]
|
|
14525
|
+
};
|
|
14526
|
+
},
|
|
14527
|
+
getCurve2dBounds: (h) => {
|
|
14528
|
+
const c = asC(h);
|
|
14529
|
+
if (c.k === "conic") return {
|
|
14530
|
+
first: c.a0,
|
|
14531
|
+
last: c.a1
|
|
14532
|
+
};
|
|
14533
|
+
if (c.k === "line") return {
|
|
14534
|
+
first: 0,
|
|
14535
|
+
last: Math.hypot(c.p2[0] - c.p1[0], c.p2[1] - c.p1[1])
|
|
14536
|
+
};
|
|
14537
|
+
return {
|
|
14538
|
+
first: 0,
|
|
14539
|
+
last: 1
|
|
14540
|
+
};
|
|
14541
|
+
},
|
|
14542
|
+
getCurve2dType: (h) => {
|
|
14543
|
+
const c = asC(h);
|
|
14544
|
+
return c.k === "line" ? "LINE" : c.k === "bezier" ? "BEZIER" : "CIRCLE";
|
|
14545
|
+
},
|
|
14546
|
+
reverseCurve2d: (h) => {
|
|
14547
|
+
const c = asC(h);
|
|
14548
|
+
if (c.k === "line") {
|
|
14549
|
+
const t = c.p1;
|
|
14550
|
+
c.p1 = c.p2;
|
|
14551
|
+
c.p2 = t;
|
|
14552
|
+
} else if (c.k === "bezier") c.pts.reverse();
|
|
14553
|
+
else {
|
|
14554
|
+
const t = c.a0;
|
|
14555
|
+
c.a0 = c.a1;
|
|
14556
|
+
c.a1 = t;
|
|
14557
|
+
}
|
|
14558
|
+
},
|
|
14559
|
+
copyCurve2d: (h) => structuredClone(asC(h)),
|
|
14560
|
+
trimCurve2d: (h, start, end) => {
|
|
14561
|
+
const c = asC(h);
|
|
14562
|
+
if (c.k === "conic") return conic(c.c, c.u, c.v, start, end);
|
|
14563
|
+
if (c.k === "line") {
|
|
14564
|
+
const dx = c.p2[0] - c.p1[0];
|
|
14565
|
+
const dy = c.p2[1] - c.p1[1];
|
|
14566
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
14567
|
+
return line([c.p1[0] + dx * start / len, c.p1[1] + dy * start / len], [c.p1[0] + dx * end / len, c.p1[1] + dy * end / len]);
|
|
14568
|
+
}
|
|
14569
|
+
return structuredClone(c);
|
|
14570
|
+
},
|
|
14571
|
+
createIdentityGTrsf2d: () => ({
|
|
14572
|
+
a: 1,
|
|
14573
|
+
b: 0,
|
|
14574
|
+
c: 0,
|
|
14575
|
+
d: 1,
|
|
14576
|
+
tx: 0,
|
|
14577
|
+
ty: 0
|
|
14578
|
+
}),
|
|
14579
|
+
createTranslationGTrsf2d: (dx, dy) => ({
|
|
14580
|
+
a: 1,
|
|
14581
|
+
b: 0,
|
|
14582
|
+
c: 0,
|
|
14583
|
+
d: 1,
|
|
14584
|
+
tx: dx,
|
|
14585
|
+
ty: dy
|
|
14586
|
+
}),
|
|
14587
|
+
createRotationGTrsf2d: (angle, cx, cy) => {
|
|
14588
|
+
const co = Math.cos(angle);
|
|
14589
|
+
const si = Math.sin(angle);
|
|
14590
|
+
return {
|
|
14591
|
+
a: co,
|
|
14592
|
+
b: -si,
|
|
14593
|
+
c: si,
|
|
14594
|
+
d: co,
|
|
14595
|
+
tx: cx - (co * cx - si * cy),
|
|
14596
|
+
ty: cy - (si * cx + co * cy)
|
|
14597
|
+
};
|
|
14598
|
+
},
|
|
14599
|
+
createScaleGTrsf2d: (f, cx, cy) => ({
|
|
14600
|
+
a: f,
|
|
14601
|
+
b: 0,
|
|
14602
|
+
c: 0,
|
|
14603
|
+
d: f,
|
|
14604
|
+
tx: cx - f * cx,
|
|
14605
|
+
ty: cy - f * cy
|
|
14606
|
+
}),
|
|
14607
|
+
createMirrorGTrsf2d: (cx, cy, mode, ox = 0, oy = 0, dx = 1, dy = 0) => {
|
|
14608
|
+
if (mode === "point") return {
|
|
14609
|
+
a: -1,
|
|
14610
|
+
b: 0,
|
|
14611
|
+
c: 0,
|
|
14612
|
+
d: -1,
|
|
14613
|
+
tx: 2 * cx,
|
|
14614
|
+
ty: 2 * cy
|
|
14615
|
+
};
|
|
14616
|
+
const l = Math.hypot(dx, dy) || 1;
|
|
14617
|
+
const ux = dx / l;
|
|
14618
|
+
const uy = dy / l;
|
|
14619
|
+
const a = ux * ux - uy * uy;
|
|
14620
|
+
const b = 2 * ux * uy;
|
|
14621
|
+
return {
|
|
14622
|
+
a,
|
|
14623
|
+
b,
|
|
14624
|
+
c: b,
|
|
14625
|
+
d: -a,
|
|
14626
|
+
tx: ox - (a * ox + b * oy),
|
|
14627
|
+
ty: oy - (b * ox - a * oy)
|
|
14628
|
+
};
|
|
14629
|
+
},
|
|
14630
|
+
createAffinityGTrsf2d: (ox, oy, dx, dy, ratio) => {
|
|
14631
|
+
const l = Math.hypot(dx, dy) || 1;
|
|
14632
|
+
const ux = dx / l;
|
|
14633
|
+
const uy = dy / l;
|
|
14634
|
+
const nx = -uy;
|
|
14635
|
+
const ny = ux;
|
|
14636
|
+
const a = ux * ux + ratio * nx * nx;
|
|
14637
|
+
const b = ux * uy + ratio * nx * ny;
|
|
14638
|
+
const c = uy * ux + ratio * ny * nx;
|
|
14639
|
+
const d = uy * uy + ratio * ny * ny;
|
|
14640
|
+
return {
|
|
14641
|
+
a,
|
|
14642
|
+
b,
|
|
14643
|
+
c,
|
|
14644
|
+
d,
|
|
14645
|
+
tx: ox - (a * ox + b * oy),
|
|
14646
|
+
ty: oy - (c * ox + d * oy)
|
|
14647
|
+
};
|
|
14648
|
+
},
|
|
14649
|
+
setGTrsf2dTranslationPart: (g, dx, dy) => {
|
|
14650
|
+
const m = g;
|
|
14651
|
+
m.tx = dx;
|
|
14652
|
+
m.ty = dy;
|
|
14653
|
+
},
|
|
14654
|
+
multiplyGTrsf2d: (base, other) => {
|
|
14655
|
+
const r = compose(base, other);
|
|
14656
|
+
Object.assign(base, r);
|
|
14657
|
+
},
|
|
14658
|
+
transformCurve2dGeneral: (h, g) => transform(asC(h), g),
|
|
14659
|
+
translateCurve2d: (h, dx, dy) => transform(asC(h), {
|
|
14660
|
+
a: 1,
|
|
14661
|
+
b: 0,
|
|
14662
|
+
c: 0,
|
|
14663
|
+
d: 1,
|
|
14664
|
+
tx: dx,
|
|
14665
|
+
ty: dy
|
|
14666
|
+
}),
|
|
14667
|
+
rotateCurve2d: (h, angle, cx, cy) => {
|
|
14668
|
+
const co = Math.cos(angle);
|
|
14669
|
+
const si = Math.sin(angle);
|
|
14670
|
+
return transform(asC(h), {
|
|
14671
|
+
a: co,
|
|
14672
|
+
b: -si,
|
|
14673
|
+
c: si,
|
|
14674
|
+
d: co,
|
|
14675
|
+
tx: cx - (co * cx - si * cy),
|
|
14676
|
+
ty: cy - (si * cx + co * cy)
|
|
14677
|
+
});
|
|
14678
|
+
},
|
|
14679
|
+
scaleCurve2d: (h, f, cx, cy) => transform(asC(h), {
|
|
14680
|
+
a: f,
|
|
14681
|
+
b: 0,
|
|
14682
|
+
c: 0,
|
|
14683
|
+
d: f,
|
|
14684
|
+
tx: cx - f * cx,
|
|
14685
|
+
ty: cy - f * cy
|
|
14686
|
+
}),
|
|
14687
|
+
mirrorCurve2dAtPoint: (h, cx, cy) => transform(asC(h), {
|
|
14688
|
+
a: -1,
|
|
14689
|
+
b: 0,
|
|
14690
|
+
c: 0,
|
|
14691
|
+
d: -1,
|
|
14692
|
+
tx: 2 * cx,
|
|
14693
|
+
ty: 2 * cy
|
|
14694
|
+
}),
|
|
14695
|
+
mirrorCurve2dAcrossAxis: (h, ox, oy, dx, dy) => {
|
|
14696
|
+
const l = Math.hypot(dx, dy) || 1;
|
|
14697
|
+
const ux = dx / l;
|
|
14698
|
+
const uy = dy / l;
|
|
14699
|
+
const a = ux * ux - uy * uy;
|
|
14700
|
+
const b = 2 * ux * uy;
|
|
14701
|
+
return transform(asC(h), {
|
|
14702
|
+
a,
|
|
14703
|
+
b,
|
|
14704
|
+
c: b,
|
|
14705
|
+
d: -a,
|
|
14706
|
+
tx: ox - (a * ox + b * oy),
|
|
14707
|
+
ty: oy - (b * ox - a * oy)
|
|
14708
|
+
});
|
|
14709
|
+
},
|
|
14710
|
+
affinityTransform2d: (h, ox, oy, dx, dy, ratio) => {
|
|
14711
|
+
const l = Math.hypot(dx, dy) || 1;
|
|
14712
|
+
const ux = dx / l;
|
|
14713
|
+
const uy = dy / l;
|
|
14714
|
+
const nx = -uy;
|
|
14715
|
+
const ny = ux;
|
|
14716
|
+
const a = ux * ux + ratio * nx * nx;
|
|
14717
|
+
const b = ux * uy + ratio * nx * ny;
|
|
14718
|
+
const c = uy * ux + ratio * ny * nx;
|
|
14719
|
+
const d = uy * uy + ratio * ny * ny;
|
|
14720
|
+
return transform(asC(h), {
|
|
14721
|
+
a,
|
|
14722
|
+
b,
|
|
14723
|
+
c,
|
|
14724
|
+
d,
|
|
14725
|
+
tx: ox - (a * ox + b * oy),
|
|
14726
|
+
ty: oy - (c * ox + d * oy)
|
|
14727
|
+
});
|
|
14728
|
+
},
|
|
14729
|
+
createBoundingBox2d: () => ({
|
|
14730
|
+
min: [Infinity, Infinity],
|
|
14731
|
+
max: [-Infinity, -Infinity]
|
|
14732
|
+
}),
|
|
14733
|
+
addCurveToBBox2d: (bb, h) => {
|
|
14734
|
+
const box = bb;
|
|
14735
|
+
let pts;
|
|
14736
|
+
if (isNative(h)) pts = sample(asC(h));
|
|
14737
|
+
else {
|
|
14738
|
+
const o = occtOr("addCurveToBBox2d");
|
|
14739
|
+
const { first, last } = o.getCurve2dBounds(h);
|
|
14740
|
+
pts = [];
|
|
14741
|
+
for (let i = 0; i <= 32; i++) pts.push(o.evaluateCurve2d(h, first + (last - first) * i / 32));
|
|
14742
|
+
}
|
|
14743
|
+
for (const [x, y] of pts) {
|
|
14744
|
+
if (x < box.min[0]) box.min[0] = x;
|
|
14745
|
+
if (y < box.min[1]) box.min[1] = y;
|
|
14746
|
+
if (x > box.max[0]) box.max[0] = x;
|
|
14747
|
+
if (y > box.max[1]) box.max[1] = y;
|
|
14748
|
+
}
|
|
14749
|
+
},
|
|
14750
|
+
getBBox2dBounds: (bb) => {
|
|
14751
|
+
const box = bb;
|
|
14752
|
+
return {
|
|
14753
|
+
xMin: box.min[0],
|
|
14754
|
+
yMin: box.min[1],
|
|
14755
|
+
xMax: box.max[0],
|
|
14756
|
+
yMax: box.max[1]
|
|
14757
|
+
};
|
|
14758
|
+
},
|
|
14759
|
+
mergeBBox2d: (t, o) => {
|
|
14760
|
+
const a = t;
|
|
14761
|
+
const b = o;
|
|
14762
|
+
a.min[0] = Math.min(a.min[0], b.min[0]);
|
|
14763
|
+
a.min[1] = Math.min(a.min[1], b.min[1]);
|
|
14764
|
+
a.max[0] = Math.max(a.max[0], b.max[0]);
|
|
14765
|
+
a.max[1] = Math.max(a.max[1], b.max[1]);
|
|
14766
|
+
},
|
|
14767
|
+
isBBox2dOut: (a, b) => {
|
|
14768
|
+
const x = a;
|
|
14769
|
+
const y = b;
|
|
14770
|
+
return x.max[0] < y.min[0] || x.min[0] > y.max[0] || x.max[1] < y.min[1] || x.min[1] > y.max[1];
|
|
14771
|
+
},
|
|
14772
|
+
isBBox2dOutPoint: (bb, x, y) => {
|
|
14773
|
+
const box = bb;
|
|
14774
|
+
return x < box.min[0] || x > box.max[0] || y < box.min[1] || y > box.max[1];
|
|
14775
|
+
},
|
|
14776
|
+
getCurve2dCircleData: (h) => {
|
|
14777
|
+
const c = asC(h);
|
|
14778
|
+
if (c.k !== "conic") return null;
|
|
14779
|
+
const ru = Math.hypot(c.u[0], c.u[1]);
|
|
14780
|
+
const rv = Math.hypot(c.v[0], c.v[1]);
|
|
14781
|
+
if (Math.abs(ru - rv) > 1e-6) return null;
|
|
14782
|
+
return {
|
|
14783
|
+
cx: c.c[0],
|
|
14784
|
+
cy: c.c[1],
|
|
14785
|
+
radius: ru,
|
|
14786
|
+
isDirect: true
|
|
14787
|
+
};
|
|
14788
|
+
},
|
|
14789
|
+
liftCurve2dToPlane: (h, origin, zDir, xDir) => {
|
|
14790
|
+
const zx = zDir[0];
|
|
14791
|
+
const zy = zDir[1];
|
|
14792
|
+
const zz = zDir[2];
|
|
14793
|
+
const xx = xDir[0];
|
|
14794
|
+
const xy = xDir[1];
|
|
14795
|
+
const xz = xDir[2];
|
|
14796
|
+
const yx = zy * xz - zz * xy;
|
|
14797
|
+
const yy = zz * xx - zx * xz;
|
|
14798
|
+
const yz = zx * xy - zy * xx;
|
|
14799
|
+
return wrap(PLACEHOLDER, makeNode("profileEdge", { pts: sample(asC(h)).map(([u, v]) => [
|
|
14800
|
+
origin[0] + xx * u + yx * v,
|
|
14801
|
+
origin[1] + xy * u + yy * v,
|
|
14802
|
+
origin[2] + xz * u + yz * v
|
|
14803
|
+
]) }, []));
|
|
14804
|
+
},
|
|
14805
|
+
makeBSpline2d: (points, options) => occtOr("makeBSpline2d").makeBSpline2d(points, options),
|
|
14806
|
+
offsetCurve2d: (c, offset) => occtOr("offsetCurve2d").offsetCurve2d(toOcct(c), offset),
|
|
14807
|
+
intersectCurves2d: (c1, c2, tol) => occtOr("intersectCurves2d").intersectCurves2d(toOcct(c1), toOcct(c2), tol),
|
|
14808
|
+
projectPointOnCurve2d: (c, x, y) => occtOr("projectPointOnCurve2d").projectPointOnCurve2d(toOcct(c), x, y),
|
|
14809
|
+
distanceBetweenCurves2d: (c1, c2, s1, e1, s2, e2) => occtOr("distanceBetweenCurves2d").distanceBetweenCurves2d(toOcct(c1), toOcct(c2), s1, e1, s2, e2),
|
|
14810
|
+
approximateCurve2dAsBSpline: (c, tol, cont, maxSeg) => occtOr("approximateCurve2dAsBSpline").approximateCurve2dAsBSpline(toOcct(c), tol, cont, maxSeg),
|
|
14811
|
+
decomposeBSpline2dToBeziers: (c) => occtOr("decomposeBSpline2dToBeziers").decomposeBSpline2dToBeziers(toOcct(c)),
|
|
14812
|
+
serializeCurve2d: (c) => occtOr("serializeCurve2d").serializeCurve2d(toOcct(c)),
|
|
14813
|
+
deserializeCurve2d: (data) => occtOr("deserializeCurve2d").deserializeCurve2d(data),
|
|
14814
|
+
splitCurve2d: (c, params) => occtOr("splitCurve2d").splitCurve2d(toOcct(c), params),
|
|
14815
|
+
getCurve2dEllipseData: (c) => occtOr("getCurve2dEllipseData").getCurve2dEllipseData(toOcct(c)),
|
|
14816
|
+
getCurve2dBezierPoles: (c) => occtOr("getCurve2dBezierPoles").getCurve2dBezierPoles(toOcct(c)),
|
|
14817
|
+
getCurve2dBezierDegree: (c) => occtOr("getCurve2dBezierDegree").getCurve2dBezierDegree(toOcct(c)),
|
|
14818
|
+
getCurve2dBSplineData: (c) => occtOr("getCurve2dBSplineData").getCurve2dBSplineData(toOcct(c)),
|
|
14819
|
+
buildEdgeOnSurface: (c, surface) => occtOr("buildEdgeOnSurface").buildEdgeOnSurface(toOcct(c), surface),
|
|
14820
|
+
extractSurfaceFromFace: (face) => occtOr("extractSurfaceFromFace").extractSurfaceFromFace(face),
|
|
14821
|
+
extractCurve2dFromEdge: (edge, face) => occtOr("extractCurve2dFromEdge").extractCurve2dFromEdge(edge, face),
|
|
14822
|
+
buildCurves3d: (wire) => {
|
|
14823
|
+
occtOr("buildCurves3d").buildCurves3d(wire);
|
|
14824
|
+
},
|
|
14825
|
+
fixWireOnFace: (wire, face, tol) => occtOr("fixWireOnFace").fixWireOnFace(wire, face, tol),
|
|
14826
|
+
fillSurface: (wires, opts) => occtOr("fillSurface").fillSurface(wires, opts)
|
|
14827
|
+
};
|
|
14828
|
+
for (const m of [
|
|
14829
|
+
"evaluateCurve2d",
|
|
14830
|
+
"evaluateCurve2dD1",
|
|
14831
|
+
"getCurve2dBounds",
|
|
14832
|
+
"getCurve2dType",
|
|
14833
|
+
"reverseCurve2d",
|
|
14834
|
+
"copyCurve2d",
|
|
14835
|
+
"trimCurve2d",
|
|
14836
|
+
"transformCurve2dGeneral",
|
|
14837
|
+
"translateCurve2d",
|
|
14838
|
+
"rotateCurve2d",
|
|
14839
|
+
"scaleCurve2d",
|
|
14840
|
+
"mirrorCurve2dAtPoint",
|
|
14841
|
+
"mirrorCurve2dAcrossAxis",
|
|
14842
|
+
"affinityTransform2d",
|
|
14843
|
+
"getCurve2dCircleData",
|
|
14844
|
+
"liftCurve2dToPlane"
|
|
14845
|
+
]) {
|
|
14846
|
+
const nativeFn = impl[m];
|
|
14847
|
+
if (!nativeFn) continue;
|
|
14848
|
+
impl[m] = ((h, ...rest) => isNative(h) ? nativeFn(h, ...rest) : delegate(m, h, ...rest));
|
|
14849
|
+
}
|
|
14850
|
+
return impl;
|
|
14851
|
+
}
|
|
14852
|
+
//#endregion
|
|
12822
14853
|
//#region src/kernel/manifold/kernel2dOps.ts
|
|
12823
14854
|
var KERNEL_2D_METHODS = [
|
|
12824
14855
|
"createPoint2d",
|
|
@@ -12890,9 +14921,11 @@ function resolveOcct2D(method) {
|
|
|
12890
14921
|
if (!occt) throw new Error(`manifold: ${method} unsupported on manifold kernel; no B-rep kernel registered`);
|
|
12891
14922
|
return occt;
|
|
12892
14923
|
}
|
|
12893
|
-
function makeKernel2DOps(
|
|
14924
|
+
function makeKernel2DOps(module) {
|
|
12894
14925
|
const ops = {};
|
|
12895
14926
|
for (const method of KERNEL_2D_METHODS) ops[method] = (...args) => resolveOcct2D(method)[method](...args);
|
|
14927
|
+
const native = makeNativeKernel2DOps(module, () => resolveOcct());
|
|
14928
|
+
Object.assign(ops, native);
|
|
12896
14929
|
return ops;
|
|
12897
14930
|
}
|
|
12898
14931
|
//#endregion
|