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