brepjs 18.62.0 → 18.63.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 (82) hide show
  1. package/dist/2d.cjs +6 -6
  2. package/dist/2d.js +6 -6
  3. package/dist/{blueprint-U7q8mCkd.js → blueprint-DaRszQg-.js} +5 -5
  4. package/dist/{blueprint-oAHtiBNZ.cjs → blueprint-XEJiK6ON.cjs} +5 -5
  5. package/dist/{blueprintFns-B430IS5G.js → blueprintFns-C0ULkqGT.js} +2 -2
  6. package/dist/{blueprintFns-BXIg6yIJ.cjs → blueprintFns-CTJGTMYw.cjs} +2 -2
  7. package/dist/{blueprintSketcher-CpYK3eMF.js → blueprintSketcher-DHWmjOmJ.js} +3 -3
  8. package/dist/{blueprintSketcher-BMy4GYLb.cjs → blueprintSketcher-r0uUFBZZ.cjs} +3 -3
  9. package/dist/{boolean2D-CalRrd7P.cjs → boolean2D-BT3RGDXW.cjs} +4 -4
  10. package/dist/{boolean2D-BVGfOj7k.js → boolean2D-BfavHsXe.js} +4 -4
  11. package/dist/{booleanFns-CejWQwHg.cjs → booleanFns-B3vIRhjz.cjs} +4 -4
  12. package/dist/{booleanFns-BF1OlY3H.js → booleanFns-DNG6xxu8.js} +4 -4
  13. package/dist/brepjs.cjs +24 -24
  14. package/dist/brepjs.js +24 -24
  15. package/dist/{cameraFns-CDmeZqa_.js → cameraFns-CQkS1TSY.js} +2 -2
  16. package/dist/{cameraFns-DkasWoY3.cjs → cameraFns-CqplcvtL.cjs} +2 -2
  17. package/dist/core.cjs +1 -1
  18. package/dist/core.js +1 -1
  19. package/dist/{cornerFinder-mHD8eDmo.cjs → cornerFinder-ByEss8KB.cjs} +1 -1
  20. package/dist/{cornerFinder-mUvhWdNG.js → cornerFinder-CE1b1IMu.js} +1 -1
  21. package/dist/{curveFns-Ba1IEqn_.cjs → curveFns-Dx9ha0n_.cjs} +1 -1
  22. package/dist/{curveFns-C_va_3Js.js → curveFns-vZbW7CVb.js} +1 -1
  23. package/dist/{drawFns-zcgjasyw.js → drawFns-DiJFFH16.js} +12 -12
  24. package/dist/{drawFns-DGENZIVf.cjs → drawFns-ZPhrUGWl.cjs} +12 -12
  25. package/dist/{extrudeFns-BkOhPexa.js → extrudeFns-BamV4cSU.js} +1 -1
  26. package/dist/{extrudeFns-DiIGGA5r.cjs → extrudeFns-DsvQYfqd.cjs} +1 -1
  27. package/dist/{faceFns-eCr0tWic.cjs → faceFns-DGmQI57P.cjs} +2 -2
  28. package/dist/{faceFns-DUmTMdax.js → faceFns-LJ_lU5IE.js} +2 -2
  29. package/dist/{helpers-Cpw0iU4L.cjs → helpers-B6abm0X7.cjs} +6 -6
  30. package/dist/{helpers-C7WpHbhO.js → helpers-CxZqc5vt.js} +6 -6
  31. package/dist/{historyFns-BVNMHteL.js → historyFns-DpHCn1yw.js} +4 -4
  32. package/dist/{historyFns-B5zXm33l.cjs → historyFns-a2plj33G.cjs} +4 -4
  33. package/dist/{importFns-DcUisQj3.js → importFns-CEz6N0mP.js} +2 -2
  34. package/dist/{importFns-BQEVbZwt.cjs → importFns-iJZHFevB.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/kernel2dNative.d.ts +26 -0
  39. package/dist/kernel/manifold/kernel2dOps.d.ts +1 -1
  40. package/dist/kernel/manifold/nativeEdges.d.ts +37 -0
  41. package/dist/kernel/manifold/nativeFaces.d.ts +27 -0
  42. package/dist/kernel/manifold/profileOps.d.ts +19 -0
  43. package/dist/kernel/manifold/sweepOps.d.ts +12 -0
  44. package/dist/{measureFns-BdYuCuf9.js → measureFns-1vEKylTt.js} +3 -3
  45. package/dist/{measureFns-Bve7WG1g.cjs → measureFns-CvAOj_Io.cjs} +3 -3
  46. package/dist/measurement.cjs +1 -1
  47. package/dist/measurement.js +1 -1
  48. package/dist/{meshFns-BtjQFcwe.js → meshFns-CoPoc7dh.js} +3 -3
  49. package/dist/{meshFns-Hxj3Vngd.cjs → meshFns-D2rlqrTt.cjs} +3 -3
  50. package/dist/operations.cjs +2 -2
  51. package/dist/operations.js +2 -2
  52. package/dist/{primitiveFns-n-OA8SFM.cjs → primitiveFns-Bi22B25-.cjs} +7 -7
  53. package/dist/{primitiveFns-ODt9feUB.js → primitiveFns-OuEHerl6.js} +7 -7
  54. package/dist/projection.cjs +1 -1
  55. package/dist/projection.js +1 -1
  56. package/dist/query.cjs +2 -2
  57. package/dist/query.js +2 -2
  58. package/dist/{shapeFns-CGiJTWpD.js → shapeFns-C3wVDhtn.js} +2 -2
  59. package/dist/{shapeFns-Dl7cAveQ.cjs → shapeFns-n4A9y80v.cjs} +2 -2
  60. package/dist/shapeRef.cjs +1 -1
  61. package/dist/shapeRef.js +1 -1
  62. package/dist/{shapeRefFns-CqLB7rA2.js → shapeRefFns-CUt56Cul.js} +4 -4
  63. package/dist/{shapeRefFns-DppFONN_.cjs → shapeRefFns-eqCfRF0P.cjs} +4 -4
  64. package/dist/{shapeTypes-BGeihZZ2.js → shapeTypes-9yfC5Z8_.js} +1910 -215
  65. package/dist/{shapeTypes-DgN4lV-c.cjs → shapeTypes-C_liO43e.cjs} +1910 -215
  66. package/dist/sketching.cjs +3 -3
  67. package/dist/sketching.js +3 -3
  68. package/dist/{solidBuilders-C8x5hl-i.cjs → solidBuilders-BtfspzpT.cjs} +2 -2
  69. package/dist/{solidBuilders-C_i7lfl1.js → solidBuilders-DSAmLmHd.js} +2 -2
  70. package/dist/{surfaceBuilders-8Q5NVxkT.cjs → surfaceBuilders-6BfGn0MN.cjs} +2 -2
  71. package/dist/{surfaceBuilders-OuKORf6n.js → surfaceBuilders-ChWLM9Io.js} +2 -2
  72. package/dist/text.cjs +2 -2
  73. package/dist/text.js +2 -2
  74. package/dist/{textBlueprints-CjWzCUDy.js → textBlueprints-B_RYPM3J.js} +7 -7
  75. package/dist/{textBlueprints-CMszuH5i.cjs → textBlueprints-DTaz774E.cjs} +7 -7
  76. package/dist/{textMetrics-ogNOndAW.cjs → textMetrics-CEBrAzpE.cjs} +1 -1
  77. package/dist/{textMetrics-nDL3MTCV.js → textMetrics-RRDn50ym.js} +1 -1
  78. package/dist/topology.cjs +7 -7
  79. package/dist/topology.js +7 -7
  80. package/dist/{topologyQueryFns-CTq8qOKc.cjs → topologyQueryFns-B6BTVGLX.cjs} +1 -1
  81. package/dist/{topologyQueryFns-B_zXrMDh.js → topologyQueryFns-nbhh_XJF.js} +1 -1
  82. package/package.json +1 -1
@@ -387,7 +387,7 @@ function iterShapesJS(oc, shape, type) {
387
387
  /**
388
388
  * Iterates sub-shapes of a given type, using C++ bulk extraction when available.
389
389
  */
390
- function iterShapes$1(oc, shape, type) {
390
+ function iterShapes$2(oc, shape, type) {
391
391
  if (oc.TopologyExtractor) return iterShapesBulk(oc, shape, type);
392
392
  return iterShapesJS(oc, shape, type);
393
393
  }
@@ -416,7 +416,7 @@ function getShapeTypeMap(oc) {
416
416
  /**
417
417
  * Returns the shape type string for a given shape.
418
418
  */
419
- function shapeType$1(oc, shape) {
419
+ function shapeType$2(oc, shape) {
420
420
  if (shape.IsNull()) throw new Error("Cannot determine shape type: shape is null");
421
421
  const result = getShapeTypeMap(oc).get(shape.ShapeType());
422
422
  if (!result) throw new Error("Unknown shape type enum value");
@@ -468,7 +468,7 @@ function iterShapeList$1(oc, list, callback) {
468
468
  /**
469
469
  * Checks if two shapes are the same (same TShape, location, and orientation).
470
470
  */
471
- function isSame$1(a, b) {
471
+ function isSame$2(a, b) {
472
472
  return a.IsSame(b);
473
473
  }
474
474
  /**
@@ -480,12 +480,12 @@ function isEqual$1(a, b) {
480
480
  /** Co-located factory: returns the topology-iteration slice of {@link KernelAdapter} bound to `oc`. */
481
481
  function makeTopologyOps$2(oc) {
482
482
  return {
483
- iterShapes: (shape, type) => iterShapes$1(oc, shape, type),
483
+ iterShapes: (shape, type) => iterShapes$2(oc, shape, type),
484
484
  iterShapeList: (list, callback) => {
485
485
  iterShapeList$1(oc, list, callback);
486
486
  },
487
- shapeType: (shape) => shapeType$1(oc, shape),
488
- isSame: (a, b) => isSame$1(a, b),
487
+ shapeType: (shape) => shapeType$2(oc, shape),
488
+ isSame: (a, b) => isSame$2(a, b),
489
489
  isEqual: (a, b) => isEqual$1(a, b),
490
490
  isValid: (shape) => isValid$1(oc, shape),
491
491
  sew: (shapes, tolerance) => sew$1(oc, shapes, tolerance)
@@ -874,7 +874,7 @@ function makeFace$1(oc, wire, planar = true) {
874
874
  return face;
875
875
  }
876
876
  const builder = new oc.BRepOffsetAPI_MakeFilling(3, 15, 2, false, 1e-5, 1e-4, .01, .1, 8, 9);
877
- const edges = iterShapes$1(oc, wire, "edge");
877
+ const edges = iterShapes$2(oc, wire, "edge");
878
878
  for (const edge of edges) builder.Add_1(edge, oc.GeomAbs_Shape.GeomAbs_C0, true);
879
879
  const progress = new oc.Message_ProgressRange_1();
880
880
  builder.Build(progress);
@@ -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),
@@ -2772,11 +2772,11 @@ function shapeTypeStr(oc, shape) {
2772
2772
  return "compound";
2773
2773
  }
2774
2774
  /** Get the hash code of a shape. */
2775
- function hashCode$1(oc, shape, upperBound) {
2775
+ function hashCode$2(oc, shape, upperBound) {
2776
2776
  return oc.shapeHashCode(shape, upperBound);
2777
2777
  }
2778
2778
  /** Check if a shape is null. */
2779
- function isNull$1(_oc, shape) {
2779
+ function isNull$2(_oc, shape) {
2780
2780
  return shape.IsNull();
2781
2781
  }
2782
2782
  /** Return a copy of the shape with reversed orientation. */
@@ -2871,8 +2871,8 @@ function makeGeometryQueryOps(oc) {
2871
2871
  curveParameters: (shape) => curveParameters$1(oc, shape),
2872
2872
  shapeOrientation: (shape) => shapeOrientation$1(oc, shape),
2873
2873
  downcast: (shape, type) => downcast$1(oc, shape, type),
2874
- hashCode: (shape, upperBound) => hashCode$1(oc, shape, upperBound),
2875
- isNull: (shape) => isNull$1(oc, shape),
2874
+ hashCode: (shape, upperBound) => hashCode$2(oc, shape, upperBound),
2875
+ isNull: (shape) => isNull$2(oc, shape),
2876
2876
  hasTriangulation: (shape) => hasTriangulation$2(oc, shape),
2877
2877
  meshShape: (shape, tolerance, angularTolerance) => {
2878
2878
  meshShape$2(oc, shape, tolerance, angularTolerance);
@@ -5130,7 +5130,7 @@ function fillSurface$1(oc, wires, options = {}) {
5130
5130
  const { order = 3, nbPtsOnCur = 15, nbIter = 2, tol3d = 1e-5, tol2d = 1e-4, maxDeg = 8, maxSeg = 9 } = options;
5131
5131
  const builder = new oc.BRepOffsetAPI_MakeFilling(order, nbPtsOnCur, nbIter, false, tol3d, tol2d, .01, .1, maxDeg, maxSeg);
5132
5132
  for (let wi = 0; wi < wires.length; wi++) {
5133
- const edges = iterShapes$1(oc, wires[wi], "edge");
5133
+ const edges = iterShapes$2(oc, wires[wi], "edge");
5134
5134
  for (const edge of edges) builder.Add_1(edge, oc.GeomAbs_Shape.GeomAbs_C0, wi === 0);
5135
5135
  }
5136
5136
  const progress = new oc.Message_ProgressRange_1();
@@ -5590,9 +5590,9 @@ function resolveUniformAngle(faces, angleDeg) {
5590
5590
  //#region src/kernel/brepkit/topologyOps.ts
5591
5591
  function iterCompound(bk, h, type) {
5592
5592
  const children = syntheticCompounds.get(h);
5593
- if (children) return children.flatMap((child) => child.type === type ? [child] : iterShapes(bk, child, type));
5593
+ if (children) return children.flatMap((child) => child.type === type ? [child] : iterShapes$1(bk, child, type));
5594
5594
  if (type === "solid") return toArray(bk.getCompoundSolids(h)).map(solidHandle);
5595
- if (type === "face" || type === "edge" || type === "vertex" || type === "wire") return toArray(bk.getCompoundSolids(h)).map(solidHandle).flatMap((s) => iterShapes(bk, s, type));
5595
+ if (type === "face" || type === "edge" || type === "vertex" || type === "wire") return toArray(bk.getCompoundSolids(h)).map(solidHandle).flatMap((s) => iterShapes$1(bk, s, type));
5596
5596
  return [];
5597
5597
  }
5598
5598
  function iterSolid(bk, h, type) {
@@ -5608,7 +5608,7 @@ function iterShellChildren(bk, h, type) {
5608
5608
  const faces = toArray(bk.getShellFaces(h)).map(faceHandle);
5609
5609
  const seen = /* @__PURE__ */ new Set();
5610
5610
  const results = [];
5611
- for (const face of faces) for (const child of iterShapes(bk, face, type)) {
5611
+ for (const face of faces) for (const child of iterShapes$1(bk, face, type)) {
5612
5612
  const childId = unwrap$1(child);
5613
5613
  if (seen.has(childId)) continue;
5614
5614
  seen.add(childId);
@@ -5668,7 +5668,7 @@ function iterEdge(bk, shape, h, type) {
5668
5668
  }
5669
5669
  return [];
5670
5670
  }
5671
- function iterShapes(bk, shape, type) {
5671
+ function iterShapes$1(bk, shape, type) {
5672
5672
  const h = unwrap$1(shape);
5673
5673
  switch (shape.type) {
5674
5674
  case "compound": return iterCompound(bk, h, type);
@@ -5683,31 +5683,31 @@ function iterShapes(bk, shape, type) {
5683
5683
  function iterShapeList(_bk, list, callback) {
5684
5684
  if (Array.isArray(list)) for (const item of list) callback(item);
5685
5685
  }
5686
- function shapeType(_bk, shape) {
5686
+ function shapeType$1(_bk, shape) {
5687
5687
  if (isBrepkitHandle(shape)) return shape.type;
5688
5688
  throw new Error("brepkit: cannot determine shape type of non-brepkit handle");
5689
5689
  }
5690
- function isSame(_bk, a, b) {
5690
+ function isSame$1(_bk, a, b) {
5691
5691
  return isBrepkitHandle(a) && isBrepkitHandle(b) && a.id === b.id && a.type === b.type;
5692
5692
  }
5693
5693
  function isEqual(_bk, a, b) {
5694
- return isSame(_bk, a, b);
5694
+ return isSame$1(_bk, a, b);
5695
5695
  }
5696
5696
  function downcast(_bk, shape, _type) {
5697
5697
  return shape;
5698
5698
  }
5699
- function hashCode(_bk, shape, upperBound) {
5699
+ function hashCode$1(_bk, shape, upperBound) {
5700
5700
  if (!isBrepkitHandle(shape)) return 0;
5701
5701
  return shape.id % upperBound;
5702
5702
  }
5703
- function isNull(_bk, shape) {
5703
+ function isNull$1(_bk, shape) {
5704
5704
  return !shape || !isBrepkitHandle(shape);
5705
5705
  }
5706
5706
  function shapeOrientation(bk, shape) {
5707
5707
  const h = unwrap$1(shape);
5708
5708
  return bk.getShapeOrientation(h);
5709
5709
  }
5710
- function edgeToFaceMap(bk, shape) {
5710
+ function edgeToFaceMap$1(bk, shape) {
5711
5711
  const solidId = unwrapSolidOrThrow(shape, "edgeToFaceMap");
5712
5712
  return bk.edgeToFaceMap(solidId);
5713
5713
  }
@@ -5716,7 +5716,7 @@ function sharedEdges(bk, faceA, faceB) {
5716
5716
  const bId = unwrap$1(faceB, "face");
5717
5717
  return Array.from(bk.sharedEdges(aId, bId)).map((id) => edgeHandle(id));
5718
5718
  }
5719
- function adjacentFaces(bk, shape, face) {
5719
+ function adjacentFaces$1(bk, shape, face) {
5720
5720
  const solidId = unwrapSolidOrThrow(shape, "adjacentFaces");
5721
5721
  const faceId = unwrap$1(face, "face");
5722
5722
  return Array.from(bk.adjacentFaces(solidId, faceId)).map((id) => faceHandle(id));
@@ -5741,20 +5741,20 @@ function sew(bk, shapes, tolerance) {
5741
5741
  /** Co-located factory: returns the topology-iteration slice of {@link KernelAdapter} bound to `bk`. */
5742
5742
  function makeTopologyOps$1(bk) {
5743
5743
  return {
5744
- iterShapes: (shape, type) => iterShapes(bk, shape, type),
5744
+ iterShapes: (shape, type) => iterShapes$1(bk, shape, type),
5745
5745
  iterShapeList: (list, callback) => {
5746
5746
  iterShapeList(bk, list, callback);
5747
5747
  },
5748
- shapeType: (shape) => shapeType(bk, shape),
5749
- isSame: (a, b) => isSame(bk, a, b),
5748
+ shapeType: (shape) => shapeType$1(bk, shape),
5749
+ isSame: (a, b) => isSame$1(bk, a, b),
5750
5750
  isEqual: (a, b) => isEqual(bk, a, b),
5751
5751
  downcast: (shape, type) => downcast(bk, shape, type),
5752
- hashCode: (shape, upperBound) => hashCode(bk, shape, upperBound),
5753
- isNull: (shape) => isNull(bk, shape),
5752
+ hashCode: (shape, upperBound) => hashCode$1(bk, shape, upperBound),
5753
+ isNull: (shape) => isNull$1(bk, shape),
5754
5754
  shapeOrientation: (shape) => shapeOrientation(bk, shape),
5755
- edgeToFaceMap: (shape) => edgeToFaceMap(bk, shape),
5755
+ edgeToFaceMap: (shape) => edgeToFaceMap$1(bk, shape),
5756
5756
  sharedEdges: (faceA, faceB) => sharedEdges(bk, faceA, faceB),
5757
- adjacentFaces: (shape, face) => adjacentFaces(bk, shape, face),
5757
+ adjacentFaces: (shape, face) => adjacentFaces$1(bk, shape, face),
5758
5758
  sew: (shapes, tolerance) => sew(bk, shapes, tolerance)
5759
5759
  };
5760
5760
  }
@@ -5900,7 +5900,7 @@ function curvePeriod(bk, shape) {
5900
5900
  }
5901
5901
  function curveType(bk, shape) {
5902
5902
  if (shape.type === "wire") {
5903
- const first = iterShapes(bk, shape, "edge")[0];
5903
+ const first = iterShapes$1(bk, shape, "edge")[0];
5904
5904
  if (first) return bk.getEdgeCurveType(unwrap$1(first, "edge"));
5905
5905
  return "LINE";
5906
5906
  }
@@ -5976,7 +5976,7 @@ function recognizeFeatures(bk, shape, tolerance) {
5976
5976
  return bk.recognizeFeatures(solidId, tolerance);
5977
5977
  }
5978
5978
  function projectEdges(bk, shape, _cameraOrigin, _cameraDirection, _cameraXAxis) {
5979
- const edges = iterShapes(bk, shape, "edge");
5979
+ const edges = iterShapes$1(bk, shape, "edge");
5980
5980
  const emptyCompound = edges.length > 0 ? edges[0] : shape;
5981
5981
  return {
5982
5982
  visible: {
@@ -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),
@@ -6231,7 +6231,7 @@ function extractPlaneFromFace(bk, faceShape) {
6231
6231
  let faceId;
6232
6232
  const h = faceShape;
6233
6233
  if (h.type === "solid" || h.type === "compound") {
6234
- const faces = iterShapes(bk, faceShape, "face");
6234
+ const faces = iterShapes$1(bk, faceShape, "face");
6235
6235
  if (faces.length === 0) throw new Error("brepkit: extractPlaneFromFace: no faces found");
6236
6236
  const firstFace = faces[0];
6237
6237
  if (!firstFace) throw new Error("brepkit: extractPlaneFromFace: no faces found");
@@ -7377,7 +7377,7 @@ function brepkitJoinType(joinType) {
7377
7377
  }
7378
7378
  function offsetWire2D(bk, wire, offsetVal, joinType) {
7379
7379
  if (typeof bk.offsetWire2DWithJoin === "function") return wireHandle(bk.offsetWire2DWithJoin(unwrap$1(wire, "wire"), offsetVal, brepkitJoinType(joinType)));
7380
- const edges = iterShapes(bk, wire, "edge");
7380
+ const edges = iterShapes$1(bk, wire, "edge");
7381
7381
  if (edges.length === 0) return wire;
7382
7382
  const coords2d = [];
7383
7383
  for (const edge of edges) {
@@ -8780,7 +8780,7 @@ function fixWireOnFace(wire, _face, _tolerance) {
8780
8780
  }
8781
8781
  function fillSurface(bk, wires, _options) {
8782
8782
  if (wires.length >= 1) {
8783
- const wireEdges = iterShapes(bk, wires[0], "edge");
8783
+ const wireEdges = iterShapes$1(bk, wires[0], "edge");
8784
8784
  if (wireEdges.length === 4) {
8785
8785
  const allCoords = [];
8786
8786
  const curveLengths = [];
@@ -8892,7 +8892,7 @@ function volume$1(bk, shape) {
8892
8892
  const h = shape;
8893
8893
  if (h.type === "solid") return bk.volume(unwrap$1(shape), DEFAULT_DEFLECTION);
8894
8894
  if (h.type === "compound") {
8895
- const solids = iterShapes(bk, shape, "solid");
8895
+ const solids = iterShapes$1(bk, shape, "solid");
8896
8896
  let total = 0;
8897
8897
  for (const s of solids) total += bk.volume(unwrap$1(s), DEFAULT_DEFLECTION);
8898
8898
  return total;
@@ -8904,7 +8904,7 @@ function area$1(bk, shape) {
8904
8904
  if (h.type === "face") return bk.faceArea(unwrap$1(shape), DEFAULT_DEFLECTION);
8905
8905
  if (h.type === "solid") return bk.surfaceArea(unwrap$1(shape), DEFAULT_DEFLECTION);
8906
8906
  if (h.type === "compound") {
8907
- const faces = iterShapes(bk, shape, "face");
8907
+ const faces = iterShapes$1(bk, shape, "face");
8908
8908
  let total = 0;
8909
8909
  for (const face of faces) total += bk.faceArea(unwrap$1(face), DEFAULT_DEFLECTION);
8910
8910
  return total;
@@ -8935,7 +8935,7 @@ function centerOfMass$1(bk, shape) {
8935
8935
  }
8936
8936
  if (h.type === "edge") return edgeMidpoint(bk, h.id);
8937
8937
  if (h.type === "vertex") return vertexPosition(bk, shape);
8938
- const vertices = iterShapes(bk, shape, "vertex");
8938
+ const vertices = iterShapes$1(bk, shape, "vertex");
8939
8939
  if (vertices.length > 0) {
8940
8940
  let sx = 0, sy = 0, sz = 0;
8941
8941
  for (const v of vertices) {
@@ -8977,7 +8977,7 @@ function boundingBox$1(bk, shape) {
8977
8977
  max: [...pos]
8978
8978
  };
8979
8979
  }
8980
- const vertices = iterShapes(bk, shape, "vertex");
8980
+ const vertices = iterShapes$1(bk, shape, "vertex");
8981
8981
  if (vertices.length === 0) return {
8982
8982
  min: [
8983
8983
  0,
@@ -9409,7 +9409,7 @@ function sweep(bk, wire, spine, options) {
9409
9409
  const profileHandle = wire;
9410
9410
  const faceId = profileHandle.type === "wire" ? bk.makeFaceFromWire(profileHandle.id) : unwrap$1(wire, "face");
9411
9411
  if (spine.type === "wire") {
9412
- const edgeIds = iterShapes(bk, spine, "edge").map((e) => unwrap$1(e, "edge"));
9412
+ const edgeIds = iterShapes$1(bk, spine, "edge").map((e) => unwrap$1(e, "edge"));
9413
9413
  if (contactMode && edgeIds.length === 1) {
9414
9414
  const edgeId = edgeIds[0];
9415
9415
  if (edgeId !== void 0) return solidHandle(bk.sweepWithOptions(faceId, edgeId, contactMode, [], 0, "transformed"));
@@ -9429,7 +9429,7 @@ function simplePipe(bk, profile, spine) {
9429
9429
  const profileHandle = profile;
9430
9430
  const faceId = profileHandle.type === "wire" ? bk.makeFaceFromWire(profileHandle.id) : unwrap$1(profile, "face");
9431
9431
  if (spine.type === "wire") {
9432
- const edgeIds = iterShapes(bk, spine, "edge").map((e) => unwrap$1(e, "edge"));
9432
+ const edgeIds = iterShapes$1(bk, spine, "edge").map((e) => unwrap$1(e, "edge"));
9433
9433
  return solidHandle(bk.sweepAlongEdges(faceId, edgeIds));
9434
9434
  }
9435
9435
  const nurbsData = extractNurbsFromEdge(bk, spine);
@@ -9472,7 +9472,7 @@ function tryContactModeSweep(bk, faceId, edgeId, contactMode) {
9472
9472
  function resolveContactModeEdge(bk, spine) {
9473
9473
  try {
9474
9474
  if (spine.type !== "wire") return { edgeId: unwrap$1(spine, "edge") };
9475
- const edges = iterShapes(bk, spine, "edge");
9475
+ const edges = iterShapes$1(bk, spine, "edge");
9476
9476
  if (edges.length === 1) {
9477
9477
  const first = edges[0];
9478
9478
  if (first) return { edgeId: unwrap$1(first, "edge") };
@@ -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);
@@ -9738,12 +9741,11 @@ function nodeOf(shape) {
9738
9741
  function asManifoldShape(shape) {
9739
9742
  if (shape && typeof shape === "object" && "manifold" in shape && "node" in shape) return shape;
9740
9743
  }
9744
+ var OCCT_FAMILY_IDS = ["occt", "occt-wasm"];
9741
9745
  function resolveOcct() {
9742
- try {
9743
- return getKernel("occt");
9744
- } catch {
9745
- return;
9746
- }
9746
+ for (const id of OCCT_FAMILY_IDS) try {
9747
+ return getKernel(id);
9748
+ } catch {}
9747
9749
  }
9748
9750
  function occtOrThrow(method) {
9749
9751
  const occt = resolveOcct();
@@ -10181,7 +10183,7 @@ function scale(shape, center, factor) {
10181
10183
  factor
10182
10184
  }, nodeOf(s));
10183
10185
  }
10184
- function transform(shape, trsf) {
10186
+ function transform$1(shape, trsf) {
10185
10187
  if (!Array.isArray(trsf) || trsf.length !== 16) throw new Error("manifold: transform expects a 16-element column-major matrix");
10186
10188
  const s = asShape$3(shape);
10187
10189
  return applyMatrix(unwrap(s), trsf, "transformShape", { matrix: [...trsf] }, nodeOf(s));
@@ -10263,7 +10265,7 @@ function transformBatch(entries) {
10263
10265
  function makeTransformOps(module) {
10264
10266
  return {
10265
10267
  composeTransform,
10266
- transform,
10268
+ transform: transform$1,
10267
10269
  translate,
10268
10270
  rotate,
10269
10271
  mirror,
@@ -10291,66 +10293,6 @@ function multiplyMatrix(a, b) {
10291
10293
  return out;
10292
10294
  }
10293
10295
  //#endregion
10294
- //#region src/kernel/manifold/builderOps.ts
10295
- function makeBuilderOps(module) {
10296
- const Manifold = module.Manifold;
10297
- function hullFromPoints(points, tolerance) {
10298
- const coords = points.map((p) => [
10299
- p.x,
10300
- p.y,
10301
- p.z
10302
- ]);
10303
- return wrap(Manifold.hull(coords), makeNode("hullFromPoints", {
10304
- points: coords,
10305
- tolerance
10306
- }, []));
10307
- }
10308
- function hull(shapes, tolerance) {
10309
- const operands = shapes.map((s) => unwrap(s));
10310
- return wrap(Manifold.hull(operands), makeNode("hull", { tolerance }, shapes.map((s) => nodeOf(s))));
10311
- }
10312
- function sewAndSolidify(faces, tolerance) {
10313
- const first = faces[0];
10314
- if (!first) notImplemented("sewAndSolidify (no input faces on manifold kernel)");
10315
- return wrap(unwrap(first), makeNode("sewAndSolidify", { tolerance }, faces.map((f) => nodeOf(f))));
10316
- }
10317
- return {
10318
- makeVertex: () => notImplemented("makeVertex"),
10319
- makeEdge: () => notImplemented("makeEdge"),
10320
- makeWire: () => notImplemented("makeWire"),
10321
- makeFace: () => notImplemented("makeFace"),
10322
- makeLineEdge: () => notImplemented("makeLineEdge"),
10323
- makeCircleEdge: () => notImplemented("makeCircleEdge"),
10324
- makeCircleArc: () => notImplemented("makeCircleArc"),
10325
- makeArcEdge: () => notImplemented("makeArcEdge"),
10326
- makeEllipseEdge: () => notImplemented("makeEllipseEdge"),
10327
- makeEllipseArc: () => notImplemented("makeEllipseArc"),
10328
- makeBezierEdge: () => notImplemented("makeBezierEdge"),
10329
- makeTangentArc: () => notImplemented("makeTangentArc"),
10330
- makeHelixWire: () => notImplemented("makeHelixWire"),
10331
- makeWireFromMixed: () => notImplemented("makeWireFromMixed"),
10332
- makeCompound: () => notImplemented("makeCompound"),
10333
- solidFromShell: () => notImplemented("solidFromShell"),
10334
- hull,
10335
- hullFromPoints,
10336
- buildSolidFromFaces: () => notImplemented("buildSolidFromFaces"),
10337
- makeNonPlanarFace: () => notImplemented("makeNonPlanarFace"),
10338
- addHolesInFace: () => notImplemented("addHolesInFace"),
10339
- removeHolesFromFace: () => notImplemented("removeHolesFromFace"),
10340
- makeFaceOnSurface: () => notImplemented("makeFaceOnSurface"),
10341
- bsplineSurface: (points, rows, cols) => occtOrThrow("bsplineSurface").bsplineSurface(points, rows, cols),
10342
- triangulatedSurface: (points, rows, cols) => occtOrThrow("triangulatedSurface").triangulatedSurface(points, rows, cols),
10343
- buildTriFace: () => notImplemented("buildTriFace"),
10344
- sewAndSolidify,
10345
- createPoint3d: () => notImplemented("createPoint3d"),
10346
- createDirection3d: () => notImplemented("createDirection3d"),
10347
- createVector3d: () => notImplemented("createVector3d"),
10348
- createAxis1: () => notImplemented("createAxis1"),
10349
- createAxis2: () => notImplemented("createAxis2"),
10350
- createAxis3: () => notImplemented("createAxis3")
10351
- };
10352
- }
10353
- //#endregion
10354
10296
  //#region src/kernel/manifold/approximations.ts
10355
10297
  function asShape$2(shape) {
10356
10298
  return shape;
@@ -10437,6 +10379,10 @@ function signedArea(outline) {
10437
10379
  function ensureCCW(outline) {
10438
10380
  return signedArea(outline) < 0 ? [...outline].reverse() : outline;
10439
10381
  }
10382
+ /** Force CW winding (used for holes, opposite the CCW outline). */
10383
+ function ensureCW(outline) {
10384
+ return signedArea(outline) > 0 ? [...outline].reverse() : outline;
10385
+ }
10440
10386
  function readNodeParams(shape) {
10441
10387
  return shape.node?.params;
10442
10388
  }
@@ -10459,8 +10405,10 @@ function profileCrossSection(profile) {
10459
10405
  0
10460
10406
  ];
10461
10407
  const outline = ensureCCW(recorded.map((p) => [p[0], p[1]]));
10408
+ const holes = (params?.["holes"])?.filter((h) => h.length >= 3).map((h) => ensureCW(h.map((p) => [p[0], p[1]])));
10462
10409
  if (params?.["xAxis"] && params["yAxis"]) return {
10463
10410
  outline,
10411
+ holes,
10464
10412
  origin,
10465
10413
  xAxis: params["xAxis"],
10466
10414
  yAxis: params["yAxis"]
@@ -10472,6 +10420,7 @@ function profileCrossSection(profile) {
10472
10420
  ]);
10473
10421
  return {
10474
10422
  outline,
10423
+ holes,
10475
10424
  origin,
10476
10425
  xAxis,
10477
10426
  yAxis
@@ -10547,6 +10496,54 @@ function fanTriangulate(vertexCount) {
10547
10496
  * in correspondence with the section's outline order. `scale` rescales the
10548
10497
  * outline about its frame origin (for tapered/draft sweeps and scale laws).
10549
10498
  */
10499
+ /**
10500
+ * Upsample a closed 2D outline to exactly `n` points by KEEPING every original
10501
+ * vertex and inserting extra points on the longest segments (proportional to
10502
+ * length, largest-remainder allotment). Vertex-preserving so corners aren't
10503
+ * rounded off — resampling a 4-point rectangle to 4 returns it unchanged. Used
10504
+ * to give loft sections a common vertex count so {@link skinRings} can connect
10505
+ * them by index; lofting profiles of different point counts (circle ↔ rounded
10506
+ * rect) is otherwise impossible on the mesh kernel. `n < k` is not supported
10507
+ * (callers pass `n = max` count), so it never downsamples.
10508
+ */
10509
+ function resampleClosed(outline, n) {
10510
+ const k = outline.length;
10511
+ if (k < 2 || n <= k) return outline.map((p) => [p[0], p[1]]);
10512
+ const seg = [];
10513
+ let total = 0;
10514
+ for (let i = 0; i < k; i++) {
10515
+ const a = outline[i] ?? [0, 0];
10516
+ const b = outline[(i + 1) % k] ?? [0, 0];
10517
+ const l = Math.hypot(b[0] - a[0], b[1] - a[1]);
10518
+ seg.push(l);
10519
+ total += l;
10520
+ }
10521
+ if (total === 0) return outline.map((p) => [p[0], p[1]]);
10522
+ const extra = n - k;
10523
+ const quota = seg.map((l) => extra * l / total);
10524
+ const add = quota.map((q) => Math.floor(q));
10525
+ let placed = add.reduce((s, v) => s + v, 0);
10526
+ const rema = quota.map((q, i) => ({
10527
+ i,
10528
+ f: q - Math.floor(q)
10529
+ })).sort((x, y) => y.f - x.f);
10530
+ for (let r = 0; placed < extra; r++, placed++) {
10531
+ const idx = rema[r % rema.length]?.i ?? 0;
10532
+ add[idx] = (add[idx] ?? 0) + 1;
10533
+ }
10534
+ const out = [];
10535
+ for (let i = 0; i < k; i++) {
10536
+ const a = outline[i] ?? [0, 0];
10537
+ const b = outline[(i + 1) % k] ?? [0, 0];
10538
+ out.push([a[0], a[1]]);
10539
+ const inner = add[i] ?? 0;
10540
+ for (let j = 1; j <= inner; j++) {
10541
+ const t = j / (inner + 1);
10542
+ out.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);
10543
+ }
10544
+ }
10545
+ return out;
10546
+ }
10550
10547
  function placeRing(section, frame, scale = 1) {
10551
10548
  return section.outline.map((p) => {
10552
10549
  const lx = p[0] * scale;
@@ -10581,7 +10578,32 @@ function skinRings(module, rings) {
10581
10578
  vertProperties: Float32Array.from(verts),
10582
10579
  triVerts: Uint32Array.from(tris)
10583
10580
  });
10584
- return new module.Manifold(built);
10581
+ return orientPositive(module, new module.Manifold(built));
10582
+ }
10583
+ /**
10584
+ * Normalize a built solid to outward (positive-volume) orientation. Skinning a
10585
+ * profile whose section order or outline winding runs "backwards" yields an
10586
+ * inside-out manifold (negative volume) that booleans then mishandle — a cut
10587
+ * tool that won't subtract, a fuse operand that cancels volume. If the volume
10588
+ * is negative, rebuild with reversed triangle winding so normals face outward.
10589
+ */
10590
+ function orientPositive(module, solid) {
10591
+ if (typeof solid.volume !== "function" || solid.volume() >= 0) return solid;
10592
+ const mesh = solid.getMesh();
10593
+ const tv = mesh.triVerts;
10594
+ for (let i = 0; i + 2 < tv.length; i += 3) {
10595
+ const t = tv[i + 1] ?? 0;
10596
+ tv[i + 1] = tv[i + 2] ?? 0;
10597
+ tv[i + 2] = t;
10598
+ }
10599
+ const flipped = new module.Mesh({
10600
+ numProp: mesh.numProp,
10601
+ vertProperties: mesh.vertProperties,
10602
+ triVerts: tv
10603
+ });
10604
+ const result = new module.Manifold(flipped);
10605
+ if (typeof solid.delete === "function") solid.delete();
10606
+ return result;
10585
10607
  }
10586
10608
  /** Build the triangle index list for `ringCount` rings of `m` points each. */
10587
10609
  function skinTriangles(ringCount, m) {
@@ -10671,6 +10693,358 @@ function rotationMinimizingFrames(path, seed) {
10671
10693
  return frames;
10672
10694
  }
10673
10695
  //#endregion
10696
+ //#region src/kernel/manifold/profileOps.ts
10697
+ /** Segments used to approximate a full circle; arcs scale by angle span. */
10698
+ var FULL_CIRCLE_SEGMENTS = 24;
10699
+ /** Bezier sampling segments per edge. */
10700
+ var BEZIER_SEGMENTS = 24;
10701
+ var ZERO3 = [
10702
+ 0,
10703
+ 0,
10704
+ 0
10705
+ ];
10706
+ var EPS_JOIN = 1e-6;
10707
+ var PLACEHOLDER = {
10708
+ delete: () => {},
10709
+ isEmpty: () => false
10710
+ };
10711
+ function at3(pts, i) {
10712
+ return pts[i] ?? ZERO3;
10713
+ }
10714
+ function arcSegments(angleSpan) {
10715
+ return Math.max(2, Math.ceil(Math.abs(angleSpan) / (2 * Math.PI) * FULL_CIRCLE_SEGMENTS));
10716
+ }
10717
+ function pickPerp(n) {
10718
+ return normalize3(cross(n, Math.abs(n[0]) < .9 ? [
10719
+ 1,
10720
+ 0,
10721
+ 0
10722
+ ] : [
10723
+ 0,
10724
+ 1,
10725
+ 0
10726
+ ]));
10727
+ }
10728
+ /** Sample a circular arc in the plane framed by `normal` about `center`. */
10729
+ function sampleArc(center, normal, radius, startAngle, endAngle, xDir) {
10730
+ const n = normalize3(normal);
10731
+ const x = xDir ? normalize3(xDir) : pickPerp(n);
10732
+ const y = normalize3(cross(n, x));
10733
+ const span = endAngle - startAngle;
10734
+ const segs = arcSegments(span);
10735
+ const pts = [];
10736
+ for (let i = 0; i <= segs; i++) {
10737
+ const a = startAngle + span * i / segs;
10738
+ pts.push(add(center, add(scaleVec(x, radius * Math.cos(a)), scaleVec(y, radius * Math.sin(a)))));
10739
+ }
10740
+ return pts;
10741
+ }
10742
+ /** Circular arc through three points, sampled as a polyline p1..p2..p3. */
10743
+ function circleFrom3(p1, p2, p3) {
10744
+ const v1 = sub(p2, p1);
10745
+ const v2 = sub(p3, p1);
10746
+ const n = cross(v1, v2);
10747
+ if (length3(n) < 1e-12) return [
10748
+ p1,
10749
+ p2,
10750
+ p3
10751
+ ];
10752
+ const nn = normalize3(n);
10753
+ const b = dot(v1, v1);
10754
+ const c = dot(v2, v2);
10755
+ const d = dot(v1, v2);
10756
+ const denom = 2 * (b * c - d * d);
10757
+ if (Math.abs(denom) < 1e-18) return [
10758
+ p1,
10759
+ p2,
10760
+ p3
10761
+ ];
10762
+ const s = c * (b - d) / denom;
10763
+ const t = b * (c - d) / denom;
10764
+ const center = add(p1, add(scaleVec(v1, s), scaleVec(v2, t)));
10765
+ const radius = length3(sub(p1, center));
10766
+ const x = normalize3(sub(p1, center));
10767
+ const y = normalize3(cross(nn, x));
10768
+ const angleOf = (p) => Math.atan2(dot(sub(p, center), y), dot(sub(p, center), x));
10769
+ let a3 = angleOf(p3);
10770
+ if (a3 < 0) a3 += 2 * Math.PI;
10771
+ return sampleArc(center, nn, radius, 0, a3, x);
10772
+ }
10773
+ /** De Casteljau sampling of a Bezier of arbitrary degree. */
10774
+ function sampleBezier(points) {
10775
+ const out = [];
10776
+ for (let i = 0; i <= BEZIER_SEGMENTS; i++) {
10777
+ const t = i / BEZIER_SEGMENTS;
10778
+ const tmp = points.map((p) => [...p]);
10779
+ for (let k = 1; k < tmp.length; k++) for (let j = 0; j < tmp.length - k; j++) {
10780
+ const a = at3(tmp, j);
10781
+ const bnext = at3(tmp, j + 1);
10782
+ tmp[j] = [
10783
+ a[0] * (1 - t) + bnext[0] * t,
10784
+ a[1] * (1 - t) + bnext[1] * t,
10785
+ a[2] * (1 - t) + bnext[2] * t
10786
+ ];
10787
+ }
10788
+ out.push(at3(tmp, 0));
10789
+ }
10790
+ return out;
10791
+ }
10792
+ /** Newell's method: area-weighted normal of a (possibly non-convex) planar ring. */
10793
+ function ringNormal(ring) {
10794
+ let nx = 0;
10795
+ let ny = 0;
10796
+ let nz = 0;
10797
+ for (let i = 0; i < ring.length; i++) {
10798
+ const a = at3(ring, i);
10799
+ const b = at3(ring, (i + 1) % ring.length);
10800
+ nx += (a[1] - b[1]) * (a[2] + b[2]);
10801
+ ny += (a[2] - b[2]) * (a[0] + b[0]);
10802
+ nz += (a[0] - b[0]) * (a[1] + b[1]);
10803
+ }
10804
+ const n = [
10805
+ nx,
10806
+ ny,
10807
+ nz
10808
+ ];
10809
+ return length3(n) < 1e-12 ? [
10810
+ 0,
10811
+ 0,
10812
+ 1
10813
+ ] : normalize3(n);
10814
+ }
10815
+ function coincident(a, b) {
10816
+ return length3(sub(a, b)) < EPS_JOIN;
10817
+ }
10818
+ /** Chain edge polylines head-to-tail into one closed ring (flipping as needed). */
10819
+ function chainEdges(edgePts) {
10820
+ const first = edgePts[0];
10821
+ if (!first) return [];
10822
+ const ring = [...first];
10823
+ for (let i = 1; i < edgePts.length; i++) {
10824
+ let pts = edgePts[i] ?? [];
10825
+ if (pts.length === 0) continue;
10826
+ const end = at3(ring, ring.length - 1);
10827
+ const startsAtEnd = coincident(at3(pts, 0), end);
10828
+ const endsAtEnd = coincident(at3(pts, pts.length - 1), end);
10829
+ if (!startsAtEnd && endsAtEnd) pts = [...pts].reverse();
10830
+ const startSame = coincident(at3(pts, 0), end);
10831
+ for (let k = startSame ? 1 : 0; k < pts.length; k++) ring.push(at3(pts, k));
10832
+ }
10833
+ if (ring.length > 1 && coincident(at3(ring, 0), at3(ring, ring.length - 1))) ring.pop();
10834
+ return ring;
10835
+ }
10836
+ /** Project a planar 3D ring onto its own plane → 2D outline + world frame. */
10837
+ function frameFromRing(ring) {
10838
+ const normal = ringNormal(ring);
10839
+ const origin = at3(ring, 0);
10840
+ let xAxis = ring.length > 1 ? normalize3(sub(at3(ring, 1), origin)) : pickPerp(normal);
10841
+ xAxis = normalize3(sub(xAxis, scaleVec(normal, dot(xAxis, normal))));
10842
+ if (length3(xAxis) < 1e-9) xAxis = pickPerp(normal);
10843
+ const yAxis = normalize3(cross(normal, xAxis));
10844
+ return {
10845
+ outline: ensureCCW(ring.map((p) => {
10846
+ const rel = sub(p, origin);
10847
+ return [dot(rel, xAxis), dot(rel, yAxis)];
10848
+ })),
10849
+ origin,
10850
+ xAxis,
10851
+ yAxis
10852
+ };
10853
+ }
10854
+ var OCCT_CURVE_SEGMENTS = 12;
10855
+ /** Sample an OCCT edge into a polyline (line → endpoints, curve → 24 segments). */
10856
+ function sampleOcctEdge(occt, edge) {
10857
+ const [t0, t1] = occt.curveParameters(edge);
10858
+ let segs = OCCT_CURVE_SEGMENTS;
10859
+ try {
10860
+ if (occt.curveType(edge) === "line") segs = 1;
10861
+ } catch {}
10862
+ const pts = [];
10863
+ for (let i = 0; i <= segs; i++) pts.push(occt.curvePointAtParam(edge, t0 + (t1 - t0) * i / segs));
10864
+ return pts;
10865
+ }
10866
+ /** Discretize an OCCT wire (from the 2D-delegated blueprint path) into a ring. */
10867
+ function discretizeOcctWire(occt, wire) {
10868
+ return chainEdges(occt.iterShapes(wire, "edge").map((e) => sampleOcctEdge(occt, e)));
10869
+ }
10870
+ function makeProfileBuilders(_module) {
10871
+ function edge(pts) {
10872
+ return wrap(PLACEHOLDER, makeNode("profileEdge", { pts }, []));
10873
+ }
10874
+ function ringOrPts(shape) {
10875
+ const ms = asManifoldShape(shape);
10876
+ if (ms) {
10877
+ const params = ms.node.params;
10878
+ return params?.ring ?? params?.pts ?? [];
10879
+ }
10880
+ const occt = resolveOcct();
10881
+ return occt ? sampleOcctEdge(occt, shape) : [];
10882
+ }
10883
+ function inputNodes(items) {
10884
+ const nodes = [];
10885
+ for (const it of items) {
10886
+ const ms = asManifoldShape(it);
10887
+ if (ms) nodes.push(nodeOf(ms));
10888
+ }
10889
+ return nodes;
10890
+ }
10891
+ function wireFrom(items) {
10892
+ const ring = chainEdges(items.map((e) => ringOrPts(e)));
10893
+ return wrap(PLACEHOLDER, makeNode("profileWire", {
10894
+ ring,
10895
+ ...ring.length >= 3 ? frameFromRing(ring) : void 0
10896
+ }, inputNodes(items)));
10897
+ }
10898
+ function faceFromRing(ring, input) {
10899
+ const { outline, origin, xAxis, yAxis } = frameFromRing(ring);
10900
+ return wrap(PLACEHOLDER, makeNode("profileFace", {
10901
+ outline,
10902
+ origin,
10903
+ xAxis,
10904
+ yAxis
10905
+ }, input ? [input] : []));
10906
+ }
10907
+ function makeFace(wire) {
10908
+ const ms = asManifoldShape(wire);
10909
+ if (ms) {
10910
+ const ring = ms.node.params?.ring ?? [];
10911
+ if (ring.length >= 3) return faceFromRing(ring, nodeOf(ms));
10912
+ }
10913
+ const occt = resolveOcct();
10914
+ return faceFromRing(occt ? discretizeOcctWire(occt, wire) : [], ms ? nodeOf(ms) : void 0);
10915
+ }
10916
+ function addHolesInFace(face, holeWires) {
10917
+ const fms = asManifoldShape(face);
10918
+ const fp = (fms?.node)?.params ?? {};
10919
+ const origin = fp.origin ?? ZERO3;
10920
+ const xAxis = fp.xAxis ?? [
10921
+ 1,
10922
+ 0,
10923
+ 0
10924
+ ];
10925
+ const yAxis = fp.yAxis ?? [
10926
+ 0,
10927
+ 1,
10928
+ 0
10929
+ ];
10930
+ const project = (p) => {
10931
+ const rel = sub(p, origin);
10932
+ return [dot(rel, xAxis), dot(rel, yAxis)];
10933
+ };
10934
+ const newHoles = [];
10935
+ for (const hw of holeWires) {
10936
+ const hms = asManifoldShape(hw);
10937
+ let ring;
10938
+ if (hms) ring = hms.node.params?.ring ?? [];
10939
+ else {
10940
+ const occt = resolveOcct();
10941
+ ring = occt ? discretizeOcctWire(occt, hw) : [];
10942
+ }
10943
+ if (ring.length >= 3) newHoles.push(ring.map(project));
10944
+ }
10945
+ const holes = [...fp.holes ?? [], ...newHoles];
10946
+ const inputs = fms ? [nodeOf(fms), ...inputNodes(holeWires)] : inputNodes(holeWires);
10947
+ return wrap(PLACEHOLDER, makeNode("profileFace", {
10948
+ outline: fp.outline ?? [],
10949
+ holes,
10950
+ origin,
10951
+ xAxis,
10952
+ yAxis
10953
+ }, inputs));
10954
+ }
10955
+ function ellipsePts(center, normal, majorRadius, minorRadius, xDir) {
10956
+ const n = normalize3(normal);
10957
+ const x = xDir ? normalize3(xDir) : pickPerp(n);
10958
+ const y = normalize3(cross(n, x));
10959
+ const pts = [];
10960
+ for (let i = 0; i <= FULL_CIRCLE_SEGMENTS; i++) {
10961
+ const a = 2 * Math.PI * i / FULL_CIRCLE_SEGMENTS;
10962
+ pts.push(add(center, add(scaleVec(x, majorRadius * Math.cos(a)), scaleVec(y, minorRadius * Math.sin(a)))));
10963
+ }
10964
+ return pts;
10965
+ }
10966
+ return {
10967
+ makeVertex: (x, y, z) => edge([[
10968
+ x,
10969
+ y,
10970
+ z
10971
+ ]]),
10972
+ makeLineEdge: (p1, p2) => edge([p1, p2]),
10973
+ makeCircleEdge: (center, normal, radius) => edge(sampleArc(center, normal, radius, 0, 2 * Math.PI)),
10974
+ makeCircleArc: (center, normal, radius, startAngle, endAngle) => edge(sampleArc(center, normal, radius, startAngle, endAngle)),
10975
+ makeArcEdge: (p1, p2, p3) => edge(circleFrom3(p1, p2, p3)),
10976
+ makeEllipseEdge: (center, normal, majorRadius, minorRadius, xDir) => edge(ellipsePts(center, normal, majorRadius, minorRadius, xDir)),
10977
+ makeBezierEdge: (points) => edge(sampleBezier(points)),
10978
+ makeTangentArc: (startPoint, _startTangent, endPoint) => edge([startPoint, endPoint]),
10979
+ makeWire: (edges) => wireFrom(edges),
10980
+ makeWireFromMixed: (items) => wireFrom(items),
10981
+ makeFace,
10982
+ addHolesInFace,
10983
+ makePolygonFace: (points) => faceFromRing(points)
10984
+ };
10985
+ }
10986
+ //#endregion
10987
+ //#region src/kernel/manifold/builderOps.ts
10988
+ function makeBuilderOps(module) {
10989
+ const Manifold = module.Manifold;
10990
+ const profile = makeProfileBuilders(module);
10991
+ function hullFromPoints(points, tolerance) {
10992
+ const coords = points.map((p) => [
10993
+ p.x,
10994
+ p.y,
10995
+ p.z
10996
+ ]);
10997
+ return wrap(Manifold.hull(coords), makeNode("hullFromPoints", {
10998
+ points: coords,
10999
+ tolerance
11000
+ }, []));
11001
+ }
11002
+ function hull(shapes, tolerance) {
11003
+ const operands = shapes.map((s) => unwrap(s));
11004
+ return wrap(Manifold.hull(operands), makeNode("hull", { tolerance }, shapes.map((s) => nodeOf(s))));
11005
+ }
11006
+ function sewAndSolidify(faces, tolerance) {
11007
+ const first = faces[0];
11008
+ if (!first) notImplemented("sewAndSolidify (no input faces on manifold kernel)");
11009
+ return wrap(unwrap(first), makeNode("sewAndSolidify", { tolerance }, faces.map((f) => nodeOf(f))));
11010
+ }
11011
+ return {
11012
+ makeVertex: (x, y, z) => profile.makeVertex(x, y, z),
11013
+ makeEdge: () => notImplemented("makeEdge"),
11014
+ makeWire: (edges) => profile.makeWire(edges),
11015
+ makeFace: (wire, planar) => profile.makeFace(wire, planar),
11016
+ makeLineEdge: (p1, p2) => profile.makeLineEdge(p1, p2),
11017
+ makeCircleEdge: (center, normal, radius) => profile.makeCircleEdge(center, normal, radius),
11018
+ makeCircleArc: (center, normal, radius, startAngle, endAngle) => profile.makeCircleArc(center, normal, radius, startAngle, endAngle),
11019
+ makeArcEdge: (p1, p2, p3) => profile.makeArcEdge(p1, p2, p3),
11020
+ makeEllipseEdge: (center, normal, majorRadius, minorRadius, xDir) => profile.makeEllipseEdge(center, normal, majorRadius, minorRadius, xDir),
11021
+ makeEllipseArc: () => notImplemented("makeEllipseArc"),
11022
+ makeBezierEdge: (points) => profile.makeBezierEdge(points),
11023
+ makeTangentArc: (startPoint, startTangent, endPoint) => profile.makeTangentArc(startPoint, startTangent, endPoint),
11024
+ makeHelixWire: () => notImplemented("makeHelixWire"),
11025
+ makeWireFromMixed: (items) => profile.makeWireFromMixed(items),
11026
+ makeCompound: () => notImplemented("makeCompound"),
11027
+ solidFromShell: () => notImplemented("solidFromShell"),
11028
+ hull,
11029
+ hullFromPoints,
11030
+ buildSolidFromFaces: () => notImplemented("buildSolidFromFaces"),
11031
+ makeNonPlanarFace: () => notImplemented("makeNonPlanarFace"),
11032
+ addHolesInFace: (face, holeWires) => profile.addHolesInFace(face, holeWires),
11033
+ removeHolesFromFace: () => notImplemented("removeHolesFromFace"),
11034
+ makeFaceOnSurface: () => notImplemented("makeFaceOnSurface"),
11035
+ bsplineSurface: (points, rows, cols) => occtOrThrow("bsplineSurface").bsplineSurface(points, rows, cols),
11036
+ triangulatedSurface: (points, rows, cols) => occtOrThrow("triangulatedSurface").triangulatedSurface(points, rows, cols),
11037
+ buildTriFace: () => notImplemented("buildTriFace"),
11038
+ sewAndSolidify,
11039
+ createPoint3d: () => notImplemented("createPoint3d"),
11040
+ createDirection3d: () => notImplemented("createDirection3d"),
11041
+ createVector3d: () => notImplemented("createVector3d"),
11042
+ createAxis1: () => notImplemented("createAxis1"),
11043
+ createAxis2: () => notImplemented("createAxis2"),
11044
+ createAxis3: () => notImplemented("createAxis3")
11045
+ };
11046
+ }
11047
+ //#endregion
10674
11048
  //#region src/kernel/manifold/sweepOps.ts
10675
11049
  var RAD_PER_DEG = Math.PI / 180;
10676
11050
  function asShape$1(shape) {
@@ -10885,8 +11259,10 @@ function extrudeOp(module, face, direction, length) {
10885
11259
  direction[1] * length,
10886
11260
  direction[2] * length
10887
11261
  ]);
10888
- return wrap(orientExtrusion(module.Manifold.extrude([toPolygon(section)], height), section, dir), makeNode("extrude", {
11262
+ const polygons = [toPolygon(section), ...(section.holes ?? []).map((h) => h.map((p) => [p[0], p[1]]))];
11263
+ return wrap(orientExtrusion(orientPositive(module, module.Manifold.extrude(polygons, height)), section, dir), makeNode("extrude", {
10889
11264
  outline: section.outline,
11265
+ holes: section.holes,
10890
11266
  origin: section.origin,
10891
11267
  xAxis: section.xAxis,
10892
11268
  yAxis: section.yAxis,
@@ -10904,14 +11280,59 @@ function revolveOp(module, shape, axisOrigin, axisDirection, angleRad, op, param
10904
11280
  const radial = profileRadialOutline(section, axisOrigin, axisDirection);
10905
11281
  return wrap(orientRevolution(module.Manifold.revolve([radial], 0, angleDeg), axisOrigin, axisDirection), makeNode(op, params, [nodeOf(asShape$1(shape))]));
10906
11282
  }
11283
+ /**
11284
+ * Rotate `ring`'s point order to the cyclic offset that best lines up with
11285
+ * `ref` (minimizes Σ‖ring[(j+off)%n] − ref[j]‖²). Both rings must share a
11286
+ * vertex count. Keeps loft correspondence from twisting between dissimilar
11287
+ * sections.
11288
+ */
11289
+ function alignRing(ring, ref) {
11290
+ const n = ring.length;
11291
+ if (n === 0 || ref.length !== n) return ring;
11292
+ let bestOff = 0;
11293
+ let bestCost = Infinity;
11294
+ for (let off = 0; off < n; off++) {
11295
+ let cost = 0;
11296
+ for (let j = 0; j < n && cost < bestCost; j++) {
11297
+ const a = ring[(j + off) % n] ?? [
11298
+ 0,
11299
+ 0,
11300
+ 0
11301
+ ];
11302
+ const b = ref[j] ?? [
11303
+ 0,
11304
+ 0,
11305
+ 0
11306
+ ];
11307
+ const dx = a[0] - b[0];
11308
+ const dy = a[1] - b[1];
11309
+ const dz = a[2] - b[2];
11310
+ cost += dx * dx + dy * dy + dz * dz;
11311
+ }
11312
+ if (cost < bestCost) {
11313
+ bestCost = cost;
11314
+ bestOff = off;
11315
+ }
11316
+ }
11317
+ if (bestOff === 0) return ring;
11318
+ const out = [];
11319
+ for (let j = 0; j < n; j++) out.push(ring[(j + bestOff) % n] ?? [
11320
+ 0,
11321
+ 0,
11322
+ 0
11323
+ ]);
11324
+ return out;
11325
+ }
10907
11326
  function loftOp(module, wires, op, extraParams) {
10908
11327
  if (wires.length < 2) throw new Error("manifold: loft requires at least two profiles");
10909
11328
  const sections = wires.map(profileCrossSection);
10910
- const m = sections[0]?.outline.length ?? 0;
10911
- return wrap(skinRings(module, sections.map((s) => {
10912
- if (s.outline.length !== m) throw new Error("manifold: loft profiles must share a vertex count for mesh skinning");
10913
- return placeRing(s, sectionFrame(s));
10914
- })), makeNode(op, {
11329
+ const target = sections.reduce((mx, s) => Math.max(mx, s.outline.length), 0);
11330
+ const rings = sections.map((s) => placeRing({
11331
+ ...s,
11332
+ outline: resampleClosed(s.outline, target)
11333
+ }, sectionFrame(s)));
11334
+ for (let i = 1; i < rings.length; i++) rings[i] = alignRing(rings[i] ?? [], rings[i - 1] ?? []);
11335
+ return wrap(skinRings(module, rings), makeNode(op, {
10915
11336
  sections: sections.map(serializeSection),
10916
11337
  ...extraParams
10917
11338
  }, wires.map((w) => nodeOf(asShape$1(w)))));
@@ -11059,7 +11480,7 @@ function makeSweepOps(module) {
11059
11480
  endFactor
11060
11481
  }),
11061
11482
  loftBatch: () => notImplemented("loftBatch"),
11062
- extrudeBatch: () => notImplemented("extrudeBatch")
11483
+ extrudeBatch: (entries) => entries.map((e) => extrudeOp(module, e.face, e.direction, e.length))
11063
11484
  };
11064
11485
  }
11065
11486
  //#endregion
@@ -11195,10 +11616,55 @@ function chamferDistAngle(module, shape, edges, distance, angleDeg) {
11195
11616
  selection
11196
11617
  }, [nodeOf(input)]));
11197
11618
  }
11619
+ /**
11620
+ * Hollow an extrude-origin solid with an open top — the gridfinity bin-body case
11621
+ * (and what `approxShellMesh` cannot do: manifold-3d exposes no Minkowski inward
11622
+ * offset, so the generic shell no-ops and leaves the body solid). Reconstruct the
11623
+ * cavity from the extrude op-node's recorded outline: offset it inward by
11624
+ * `thickness` (Clipper2 2D), extrude full height so it punches through the top
11625
+ * (open), lifted by `thickness` along the extrude dir so a floor remains, then
11626
+ * subtract. Returns undefined for non-extrude solids (caller falls back).
11627
+ */
11628
+ function extrudeOpenTopShell(module, input, thickness) {
11629
+ const node = input.node;
11630
+ if (node?.op !== "extrude") return void 0;
11631
+ const p = node.params ?? {};
11632
+ const outline = p["outline"];
11633
+ const origin = p["origin"];
11634
+ const dir = p["direction"];
11635
+ const length = p["length"];
11636
+ if (!outline || outline.length < 3 || !origin || !dir || typeof length !== "number") return;
11637
+ const t = Math.abs(thickness);
11638
+ try {
11639
+ const inner = new module.CrossSection([outline.map((q) => [q[0], q[1]])]).offset(-t);
11640
+ if (typeof inner.isEmpty === "function" && inner.isEmpty()) return void 0;
11641
+ const cavity = module.Manifold.extrude(inner, length);
11642
+ const dlen = Math.hypot(dir[0], dir[1], dir[2]) || 1;
11643
+ const alignedZ = Math.abs(dir[0]) < 1e-9 && Math.abs(dir[1]) < 1e-9 && dir[2] > 0;
11644
+ let placed = cavity;
11645
+ if (!alignedZ) {
11646
+ const pitch = Math.atan2(Math.hypot(dir[0], dir[1]), dir[2]) * (180 / Math.PI);
11647
+ const yaw = Math.atan2(dir[1], dir[0]) * (180 / Math.PI);
11648
+ placed = placed.rotate([
11649
+ 0,
11650
+ pitch,
11651
+ yaw
11652
+ ]);
11653
+ }
11654
+ placed = placed.translate([
11655
+ origin[0] + dir[0] / dlen * t,
11656
+ origin[1] + dir[1] / dlen * t,
11657
+ origin[2] + dir[2] / dlen * t
11658
+ ]);
11659
+ return orientPositive(module, unwrap(input).subtract(placed));
11660
+ } catch {
11661
+ return;
11662
+ }
11663
+ }
11198
11664
  function shell(module, shape, faces, thickness, tolerance) {
11199
11665
  const input = asShape(shape);
11200
11666
  const selection = describeSelection(faces);
11201
- return wrap(approxShellMesh(module, unwrap(input), thickness, false), makeNode("shell", tolerance === void 0 ? {
11667
+ return wrap(extrudeOpenTopShell(module, input, thickness) ?? approxShellMesh(module, unwrap(input), thickness, false), makeNode("shell", tolerance === void 0 ? {
11202
11668
  thickness,
11203
11669
  selection
11204
11670
  } : {
@@ -11643,6 +12109,53 @@ var HANDLERS = {
11643
12109
  if (typeof t.gridPattern !== "function") throw new Error("manifold replay: target kernel lacks gridPattern");
11644
12110
  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));
11645
12111
  },
12112
+ profileEdge: (t, p) => {
12113
+ const pts = p["pts"] ?? [];
12114
+ const a = pts[0] ?? [
12115
+ 0,
12116
+ 0,
12117
+ 0
12118
+ ];
12119
+ const b = pts.length > 1 ? pts[pts.length - 1] ?? a : [
12120
+ a[0] + .001,
12121
+ a[1],
12122
+ a[2]
12123
+ ];
12124
+ return t.makeLineEdge([
12125
+ a[0],
12126
+ a[1],
12127
+ a[2]
12128
+ ], [
12129
+ b[0],
12130
+ b[1],
12131
+ b[2]
12132
+ ]);
12133
+ },
12134
+ profileWire: (t, p) => {
12135
+ const ring = p["ring"] ?? [];
12136
+ const edges = [];
12137
+ for (let i = 0; i < ring.length; i++) {
12138
+ const a = ring[i] ?? [
12139
+ 0,
12140
+ 0,
12141
+ 0
12142
+ ];
12143
+ const b = ring[(i + 1) % ring.length] ?? a;
12144
+ edges.push(t.makeLineEdge([
12145
+ a[0],
12146
+ a[1],
12147
+ a[2]
12148
+ ], [
12149
+ b[0],
12150
+ b[1],
12151
+ b[2]
12152
+ ]));
12153
+ }
12154
+ const wire = t.makeWire(edges);
12155
+ for (const e of edges) t.dispose(e);
12156
+ return wire;
12157
+ },
12158
+ profileFace: (t, p) => faceFromOutline(t, p),
11646
12159
  extrude: (t, p) => {
11647
12160
  const face = faceFromOutline(t, p);
11648
12161
  return t.extrude(face, asVec3(p["direction"], [
@@ -11822,19 +12335,74 @@ function replay(node, targetKernel, cache = /* @__PURE__ */ new Map()) {
11822
12335
  function solidOf(shape) {
11823
12336
  return unwrap(shape);
11824
12337
  }
11825
- function boxOf(shape) {
11826
- return solidOf(shape).boundingBox();
11827
- }
11828
- function meshOf$2(shape) {
11829
- return solidOf(shape)?.getMesh?.();
11830
- }
11831
- function vertexAt$2(mesh, i) {
11832
- const base = i * mesh.numProp;
11833
- return [
11834
- mesh.vertProperties[base] ?? 0,
11835
- mesh.vertProperties[base + 1] ?? 0,
11836
- mesh.vertProperties[base + 2] ?? 0
11837
- ];
12338
+ /** World points recorded on a profile placeholder node (edge/wire/face/vertex). */
12339
+ function profileWorldPoints(shape) {
12340
+ const p = (shape?.node)?.params;
12341
+ if (!p) return void 0;
12342
+ const pts = p["pts"] ?? p["ring"];
12343
+ if (pts && pts.length) return pts;
12344
+ const outline = p["outline"];
12345
+ if (outline && outline.length) {
12346
+ const o = p["origin"] ?? [
12347
+ 0,
12348
+ 0,
12349
+ 0
12350
+ ];
12351
+ const x = p["xAxis"] ?? [
12352
+ 1,
12353
+ 0,
12354
+ 0
12355
+ ];
12356
+ const y = p["yAxis"] ?? [
12357
+ 0,
12358
+ 1,
12359
+ 0
12360
+ ];
12361
+ return outline.map((q) => [
12362
+ o[0] + x[0] * q[0] + y[0] * q[1],
12363
+ o[1] + x[1] * q[0] + y[1] * q[1],
12364
+ o[2] + x[2] * q[0] + y[2] * q[1]
12365
+ ]);
12366
+ }
12367
+ }
12368
+ function aabbOfPoints(pts) {
12369
+ const min = [
12370
+ Infinity,
12371
+ Infinity,
12372
+ Infinity
12373
+ ];
12374
+ const max = [
12375
+ -Infinity,
12376
+ -Infinity,
12377
+ -Infinity
12378
+ ];
12379
+ for (const q of pts) for (let i = 0; i < 3; i++) {
12380
+ const v = q[i] ?? 0;
12381
+ if (v < (min[i] ?? Infinity)) min[i] = v;
12382
+ if (v > (max[i] ?? -Infinity)) max[i] = v;
12383
+ }
12384
+ return {
12385
+ min,
12386
+ max
12387
+ };
12388
+ }
12389
+ function boxOf(shape) {
12390
+ const w = shape;
12391
+ if (w && w.__manifoldSub && w.box) return w.box;
12392
+ const pts = profileWorldPoints(shape);
12393
+ if (pts) return aabbOfPoints(pts);
12394
+ return solidOf(shape).boundingBox();
12395
+ }
12396
+ function meshOf$2(shape) {
12397
+ return solidOf(shape)?.getMesh?.();
12398
+ }
12399
+ function vertexAt$2(mesh, i) {
12400
+ const base = i * mesh.numProp;
12401
+ return [
12402
+ mesh.vertProperties[base] ?? 0,
12403
+ mesh.vertProperties[base + 1] ?? 0,
12404
+ mesh.vertProperties[base + 2] ?? 0
12405
+ ];
11838
12406
  }
11839
12407
  function triangleAt$1(mesh, t) {
11840
12408
  const base = t * 3;
@@ -11962,9 +12530,16 @@ function volume(shape) {
11962
12530
  return solidOf(shape).volume();
11963
12531
  }
11964
12532
  function area(shape) {
12533
+ const w = shape;
12534
+ if (w && w.__nativeFace && typeof w.area === "number") return w.area;
11965
12535
  return solidOf(shape).surfaceArea();
11966
12536
  }
11967
12537
  function boundingBox(shape) {
12538
+ const w = shape;
12539
+ if (w && w.__manifoldSub && w.box) return {
12540
+ min: [...w.box.min],
12541
+ max: [...w.box.max]
12542
+ };
11968
12543
  const bb = boxOf(shape);
11969
12544
  return {
11970
12545
  min: [...bb.min],
@@ -12035,7 +12610,11 @@ function makeMeasureOps(_module) {
12035
12610
  return {
12036
12611
  volume: (shape) => volume(shape),
12037
12612
  area: (shape) => area(shape),
12038
- length: () => notImplemented("length"),
12613
+ length: (shape) => {
12614
+ const e = shape;
12615
+ if (e && e.__nativeEdge && typeof e.length === "number") return e.length;
12616
+ return notImplemented("length");
12617
+ },
12039
12618
  centerOfMass: (shape) => centerOfMass(shape),
12040
12619
  linearCenterOfMass: (shape) => centerOfMass(shape),
12041
12620
  boundingBox: (shape) => boundingBox(shape),
@@ -12050,6 +12629,459 @@ function makeMeasureOps(_module) {
12050
12629
  };
12051
12630
  }
12052
12631
  //#endregion
12632
+ //#region src/kernel/manifold/nativeFaces.ts
12633
+ /** runOriginalID for a triangle index, via the runIndex run boundaries. */
12634
+ function originOfTri(mesh, triIndex) {
12635
+ const { runIndex, runOriginalID } = mesh;
12636
+ if (!runIndex || !runOriginalID) return 0;
12637
+ const vertPos = triIndex * 3;
12638
+ for (let r = 0; r < runOriginalID.length; r++) {
12639
+ const start = runIndex[r] ?? 0;
12640
+ const end = runIndex[r + 1] ?? Number.MAX_SAFE_INTEGER;
12641
+ if (vertPos >= start && vertPos < end) return runOriginalID[r] ?? 0;
12642
+ }
12643
+ return 0;
12644
+ }
12645
+ /**
12646
+ * Extract planar faces from a manifold solid by grouping triangles on `faceID`.
12647
+ * Returns one {@link NativeFace} per group with normal/center/area/bbox/origin.
12648
+ */
12649
+ function extractFaces(meshUnknown) {
12650
+ const mesh = meshUnknown;
12651
+ const tv = mesh.triVerts;
12652
+ const vp = mesh.vertProperties;
12653
+ const stride = mesh.numProp || 3;
12654
+ const faceID = mesh.faceID;
12655
+ const triCount = tv.length / 3;
12656
+ const pos = (vi) => {
12657
+ const o = vi * stride;
12658
+ return [
12659
+ vp[o] ?? 0,
12660
+ vp[o + 1] ?? 0,
12661
+ vp[o + 2] ?? 0
12662
+ ];
12663
+ };
12664
+ const groups = /* @__PURE__ */ new Map();
12665
+ for (let t = 0; t < triCount; t++) {
12666
+ const id = faceID ? faceID[t] ?? t : t;
12667
+ let g = groups.get(id);
12668
+ if (!g) {
12669
+ g = [];
12670
+ groups.set(id, g);
12671
+ }
12672
+ g.push(t);
12673
+ }
12674
+ const faces = [];
12675
+ for (const [faceId, tris] of groups) {
12676
+ let nx = 0;
12677
+ let ny = 0;
12678
+ let nz = 0;
12679
+ let cx = 0;
12680
+ let cy = 0;
12681
+ let cz = 0;
12682
+ let area = 0;
12683
+ let minX = Infinity;
12684
+ let minY = Infinity;
12685
+ let minZ = Infinity;
12686
+ let maxX = -Infinity;
12687
+ let maxY = -Infinity;
12688
+ let maxZ = -Infinity;
12689
+ const triData = new Float32Array(tris.length * 9);
12690
+ let w = 0;
12691
+ for (const t of tris) {
12692
+ const a = pos(tv[t * 3] ?? 0);
12693
+ const b = pos(tv[t * 3 + 1] ?? 0);
12694
+ const c = pos(tv[t * 3 + 2] ?? 0);
12695
+ const ux = b[0] - a[0];
12696
+ const uy = b[1] - a[1];
12697
+ const uz = b[2] - a[2];
12698
+ const vx = c[0] - a[0];
12699
+ const vy = c[1] - a[1];
12700
+ const vz = c[2] - a[2];
12701
+ const px = uy * vz - uz * vy;
12702
+ const py = uz * vx - ux * vz;
12703
+ const pz = ux * vy - uy * vx;
12704
+ nx += px;
12705
+ ny += py;
12706
+ nz += pz;
12707
+ const triArea = .5 * Math.hypot(px, py, pz);
12708
+ area += triArea;
12709
+ const tcx = (a[0] + b[0] + c[0]) / 3;
12710
+ const tcy = (a[1] + b[1] + c[1]) / 3;
12711
+ const tcz = (a[2] + b[2] + c[2]) / 3;
12712
+ cx += triArea * tcx;
12713
+ cy += triArea * tcy;
12714
+ cz += triArea * tcz;
12715
+ for (const p of [
12716
+ a,
12717
+ b,
12718
+ c
12719
+ ]) {
12720
+ if (p[0] < minX) minX = p[0];
12721
+ if (p[1] < minY) minY = p[1];
12722
+ if (p[2] < minZ) minZ = p[2];
12723
+ if (p[0] > maxX) maxX = p[0];
12724
+ if (p[1] > maxY) maxY = p[1];
12725
+ if (p[2] > maxZ) maxZ = p[2];
12726
+ triData[w++] = p[0];
12727
+ triData[w++] = p[1];
12728
+ triData[w++] = p[2];
12729
+ }
12730
+ }
12731
+ const nlen = Math.hypot(nx, ny, nz) || 1;
12732
+ const normal = [
12733
+ nx / nlen,
12734
+ ny / nlen,
12735
+ nz / nlen
12736
+ ];
12737
+ const center = area > 0 ? [
12738
+ cx / area,
12739
+ cy / area,
12740
+ cz / area
12741
+ ] : [
12742
+ 0,
12743
+ 0,
12744
+ 0
12745
+ ];
12746
+ faces.push({
12747
+ __nativeFace: true,
12748
+ faceId,
12749
+ originId: originOfTri(mesh, tris[0] ?? 0),
12750
+ normal,
12751
+ center,
12752
+ area,
12753
+ min: [
12754
+ minX,
12755
+ minY,
12756
+ minZ
12757
+ ],
12758
+ max: [
12759
+ maxX,
12760
+ maxY,
12761
+ maxZ
12762
+ ],
12763
+ tris: triData
12764
+ });
12765
+ }
12766
+ return faces;
12767
+ }
12768
+ function isNativeFace(shape) {
12769
+ return !!shape && typeof shape === "object" && shape.__nativeFace === true;
12770
+ }
12771
+ //#endregion
12772
+ //#region src/kernel/manifold/nativeEdges.ts
12773
+ var CREASE_COS = Math.cos(45 * Math.PI / 180);
12774
+ var edgeKey = (a, b) => a < b ? a * 1e9 + b : b * 1e9 + a;
12775
+ var pairKey = (a, b) => a < b ? `${a},${b}` : `${b},${a}`;
12776
+ /**
12777
+ * Extract feature edges (face-pair boundaries) from a manifold solid's mesh.
12778
+ */
12779
+ function extractEdges(meshUnknown) {
12780
+ const mesh = meshUnknown;
12781
+ const tv = mesh.triVerts;
12782
+ const vp = mesh.vertProperties;
12783
+ const stride = mesh.numProp || 3;
12784
+ const faceID = mesh.faceID;
12785
+ const triCount = tv.length / 3;
12786
+ if (!faceID) return [];
12787
+ const pos = (vi) => {
12788
+ const o = vi * stride;
12789
+ return [
12790
+ vp[o] ?? 0,
12791
+ vp[o + 1] ?? 0,
12792
+ vp[o + 2] ?? 0
12793
+ ];
12794
+ };
12795
+ const triNormal = (t) => {
12796
+ const a = pos(tv[t * 3] ?? 0);
12797
+ const b = pos(tv[t * 3 + 1] ?? 0);
12798
+ const c = pos(tv[t * 3 + 2] ?? 0);
12799
+ const ux = b[0] - a[0];
12800
+ const uy = b[1] - a[1];
12801
+ const uz = b[2] - a[2];
12802
+ const vx = c[0] - a[0];
12803
+ const vy = c[1] - a[1];
12804
+ const vz = c[2] - a[2];
12805
+ const nx = uy * vz - uz * vy;
12806
+ const ny = uz * vx - ux * vz;
12807
+ const nz = ux * vy - uy * vx;
12808
+ const l = Math.hypot(nx, ny, nz) || 1;
12809
+ return [
12810
+ nx / l,
12811
+ ny / l,
12812
+ nz / l
12813
+ ];
12814
+ };
12815
+ const edgeFaces = /* @__PURE__ */ new Map();
12816
+ for (let t = 0; t < triCount; t++) {
12817
+ const a = tv[t * 3] ?? 0;
12818
+ const b = tv[t * 3 + 1] ?? 0;
12819
+ const c = tv[t * 3 + 2] ?? 0;
12820
+ const fid = faceID[t] ?? t;
12821
+ const n = triNormal(t);
12822
+ for (const [u, w] of [
12823
+ [a, b],
12824
+ [b, c],
12825
+ [c, a]
12826
+ ]) {
12827
+ const k = edgeKey(u, w);
12828
+ let e = edgeFaces.get(k);
12829
+ if (!e) {
12830
+ e = {
12831
+ v: [u, w],
12832
+ faces: [],
12833
+ nrm: []
12834
+ };
12835
+ edgeFaces.set(k, e);
12836
+ }
12837
+ e.faces.push(fid);
12838
+ e.nrm.push(n);
12839
+ }
12840
+ }
12841
+ const groups = /* @__PURE__ */ new Map();
12842
+ for (const { v, faces, nrm } of edgeFaces.values()) {
12843
+ if (faces.length !== 2) continue;
12844
+ const [f0, f1] = faces;
12845
+ if (f0 === f1) continue;
12846
+ const [n0, n1] = nrm;
12847
+ if (n0[0] * n1[0] + n0[1] * n1[1] + n0[2] * n1[2] > CREASE_COS) continue;
12848
+ const key = pairKey(f0, f1);
12849
+ let g = groups.get(key);
12850
+ if (!g) {
12851
+ g = {
12852
+ faces: f0 < f1 ? [f0, f1] : [f1, f0],
12853
+ segs: []
12854
+ };
12855
+ groups.set(key, g);
12856
+ }
12857
+ g.segs.push(v);
12858
+ }
12859
+ const edges = [];
12860
+ for (const { faces, segs } of groups.values()) for (const chain of chainSegments(segs)) edges.push(buildEdge(faces, chain, pos));
12861
+ return edges;
12862
+ }
12863
+ /** Order a bag of undirected vertex-pair segments into one or more vertex chains. */
12864
+ function chainSegments(segs) {
12865
+ const adj = /* @__PURE__ */ new Map();
12866
+ const used = /* @__PURE__ */ new Set();
12867
+ const addAdj = (v, i) => {
12868
+ let list = adj.get(v);
12869
+ if (!list) {
12870
+ list = [];
12871
+ adj.set(v, list);
12872
+ }
12873
+ list.push(i);
12874
+ };
12875
+ segs.forEach(([a, b], i) => {
12876
+ addAdj(a, i);
12877
+ addAdj(b, i);
12878
+ });
12879
+ const otherEnd = (i, v) => {
12880
+ const s = segs[i] ?? [v, v];
12881
+ return s[0] === v ? s[1] : s[0];
12882
+ };
12883
+ const chains = [];
12884
+ for (let start = 0; start < segs.length; start++) {
12885
+ if (used.has(start)) continue;
12886
+ used.add(start);
12887
+ const s = segs[start] ?? [0, 0];
12888
+ const chain = [s[0], s[1]];
12889
+ for (let guard = 0; guard < segs.length; guard++) {
12890
+ const tail = chain[chain.length - 1] ?? 0;
12891
+ const next = (adj.get(tail) ?? []).find((i) => !used.has(i));
12892
+ if (next === void 0) break;
12893
+ used.add(next);
12894
+ chain.push(otherEnd(next, tail));
12895
+ }
12896
+ for (let guard = 0; guard < segs.length; guard++) {
12897
+ const head = chain[0] ?? 0;
12898
+ const prev = (adj.get(head) ?? []).find((i) => !used.has(i));
12899
+ if (prev === void 0) break;
12900
+ used.add(prev);
12901
+ chain.unshift(otherEnd(prev, head));
12902
+ }
12903
+ chains.push(chain);
12904
+ }
12905
+ return chains;
12906
+ }
12907
+ function buildEdge(faces, chain, pos) {
12908
+ const n = chain.length;
12909
+ const pts = new Float32Array(n * 3);
12910
+ const arc = new Float32Array(n);
12911
+ let minX = Infinity;
12912
+ let minY = Infinity;
12913
+ let minZ = Infinity;
12914
+ let maxX = -Infinity;
12915
+ let maxY = -Infinity;
12916
+ let maxZ = -Infinity;
12917
+ let len = 0;
12918
+ let prev = null;
12919
+ for (let i = 0; i < n; i++) {
12920
+ const p = pos(chain[i] ?? 0);
12921
+ pts[i * 3] = p[0];
12922
+ pts[i * 3 + 1] = p[1];
12923
+ pts[i * 3 + 2] = p[2];
12924
+ if (prev) len += Math.hypot(p[0] - prev[0], p[1] - prev[1], p[2] - prev[2]);
12925
+ arc[i] = len;
12926
+ prev = p;
12927
+ if (p[0] < minX) minX = p[0];
12928
+ if (p[1] < minY) minY = p[1];
12929
+ if (p[2] < minZ) minZ = p[2];
12930
+ if (p[0] > maxX) maxX = p[0];
12931
+ if (p[1] > maxY) maxY = p[1];
12932
+ if (p[2] > maxZ) maxZ = p[2];
12933
+ }
12934
+ return {
12935
+ __nativeEdge: true,
12936
+ faces,
12937
+ pts,
12938
+ arc,
12939
+ length: len,
12940
+ min: [
12941
+ minX,
12942
+ minY,
12943
+ minZ
12944
+ ],
12945
+ max: [
12946
+ maxX,
12947
+ maxY,
12948
+ maxZ
12949
+ ],
12950
+ curveType: isStraight(pts) ? "LINE" : "CIRCLE"
12951
+ };
12952
+ }
12953
+ /** Straight if every interior point lies on the chord within a small tolerance. */
12954
+ function isStraight(pts) {
12955
+ const n = pts.length / 3;
12956
+ if (n <= 2) return true;
12957
+ const ax = pts[0] ?? 0;
12958
+ const ay = pts[1] ?? 0;
12959
+ const az = pts[2] ?? 0;
12960
+ const bx = pts[(n - 1) * 3] ?? 0;
12961
+ const by = pts[(n - 1) * 3 + 1] ?? 0;
12962
+ const bz = pts[(n - 1) * 3 + 2] ?? 0;
12963
+ let dx = bx - ax;
12964
+ let dy = by - ay;
12965
+ let dz = bz - az;
12966
+ const dl = Math.hypot(dx, dy, dz) || 1;
12967
+ dx /= dl;
12968
+ dy /= dl;
12969
+ dz /= dl;
12970
+ for (let i = 1; i < n - 1; i++) {
12971
+ const px = (pts[i * 3] ?? 0) - ax;
12972
+ const py = (pts[i * 3 + 1] ?? 0) - ay;
12973
+ const pz = (pts[i * 3 + 2] ?? 0) - az;
12974
+ const t = px * dx + py * dy + pz * dz;
12975
+ const ex = px - t * dx;
12976
+ const ey = py - t * dy;
12977
+ const ez = pz - t * dz;
12978
+ if (Math.hypot(ex, ey, ez) > 1e-6 * Math.max(1, dl)) return false;
12979
+ }
12980
+ return true;
12981
+ }
12982
+ /**
12983
+ * Extract B-rep vertices: mesh vertices where three or more distinct faces
12984
+ * (faceID groups) meet — i.e. the corners where edges terminate. A box yields
12985
+ * its 8 corners; interior/edge-midpoint vertices (1–2 faces) are excluded.
12986
+ */
12987
+ function extractVertices(meshUnknown) {
12988
+ const mesh = meshUnknown;
12989
+ const tv = mesh.triVerts;
12990
+ const vp = mesh.vertProperties;
12991
+ const stride = mesh.numProp || 3;
12992
+ const faceID = mesh.faceID;
12993
+ if (!faceID) return [];
12994
+ const triCount = tv.length / 3;
12995
+ const facesAtVert = /* @__PURE__ */ new Map();
12996
+ for (let t = 0; t < triCount; t++) {
12997
+ const fid = faceID[t] ?? t;
12998
+ for (let j = 0; j < 3; j++) {
12999
+ const v = tv[t * 3 + j] ?? 0;
13000
+ let s = facesAtVert.get(v);
13001
+ if (!s) {
13002
+ s = /* @__PURE__ */ new Set();
13003
+ facesAtVert.set(v, s);
13004
+ }
13005
+ s.add(fid);
13006
+ }
13007
+ }
13008
+ const verts = [];
13009
+ for (const [v, faces] of facesAtVert) {
13010
+ if (faces.size < 3) continue;
13011
+ const o = v * stride;
13012
+ verts.push({
13013
+ __nativeVertex: true,
13014
+ point: [
13015
+ vp[o] ?? 0,
13016
+ vp[o + 1] ?? 0,
13017
+ vp[o + 2] ?? 0
13018
+ ]
13019
+ });
13020
+ }
13021
+ return verts;
13022
+ }
13023
+ function isNativeVertex(shape) {
13024
+ return !!shape && typeof shape === "object" && shape.__nativeVertex === true;
13025
+ }
13026
+ function isNativeEdge(shape) {
13027
+ return !!shape && typeof shape === "object" && shape.__nativeEdge === true;
13028
+ }
13029
+ /** Point at arc-length `s` along the edge polyline. */
13030
+ function edgePointAt(edge, s) {
13031
+ const { pts, arc } = edge;
13032
+ const n = arc.length;
13033
+ if (n === 0) return [
13034
+ 0,
13035
+ 0,
13036
+ 0
13037
+ ];
13038
+ if (s <= 0) return [
13039
+ pts[0] ?? 0,
13040
+ pts[1] ?? 0,
13041
+ pts[2] ?? 0
13042
+ ];
13043
+ if (s >= edge.length) return [
13044
+ pts[(n - 1) * 3] ?? 0,
13045
+ pts[(n - 1) * 3 + 1] ?? 0,
13046
+ pts[(n - 1) * 3 + 2] ?? 0
13047
+ ];
13048
+ let i = 1;
13049
+ while (i < n && (arc[i] ?? 0) < s) i++;
13050
+ const a0 = arc[i - 1] ?? 0;
13051
+ const a1 = arc[i] ?? a0;
13052
+ const t = a1 > a0 ? (s - a0) / (a1 - a0) : 0;
13053
+ const o0 = (i - 1) * 3;
13054
+ const o1 = i * 3;
13055
+ return [
13056
+ (pts[o0] ?? 0) + ((pts[o1] ?? 0) - (pts[o0] ?? 0)) * t,
13057
+ (pts[o0 + 1] ?? 0) + ((pts[o1 + 1] ?? 0) - (pts[o0 + 1] ?? 0)) * t,
13058
+ (pts[o0 + 2] ?? 0) + ((pts[o1 + 2] ?? 0) - (pts[o0 + 2] ?? 0)) * t
13059
+ ];
13060
+ }
13061
+ /** Unit tangent at arc-length `s` (direction of the containing segment). */
13062
+ function edgeTangentAt(edge, s) {
13063
+ const { pts, arc } = edge;
13064
+ const n = arc.length;
13065
+ if (n < 2) return [
13066
+ 1,
13067
+ 0,
13068
+ 0
13069
+ ];
13070
+ let i = 1;
13071
+ while (i < n - 1 && (arc[i] ?? 0) < s) i++;
13072
+ const o0 = (i - 1) * 3;
13073
+ const o1 = i * 3;
13074
+ const dx = (pts[o1] ?? 0) - (pts[o0] ?? 0);
13075
+ const dy = (pts[o1 + 1] ?? 0) - (pts[o0 + 1] ?? 0);
13076
+ const dz = (pts[o1 + 2] ?? 0) - (pts[o0 + 2] ?? 0);
13077
+ const l = Math.hypot(dx, dy, dz) || 1;
13078
+ return [
13079
+ dx / l,
13080
+ dy / l,
13081
+ dz / l
13082
+ ];
13083
+ }
13084
+ //#endregion
12053
13085
  //#region src/kernel/manifold/topologyOps.ts
12054
13086
  function brepOf(shape, method) {
12055
13087
  const ms = asManifoldShape(shape);
@@ -12068,79 +13100,116 @@ function brepOf(shape, method) {
12068
13100
  brep
12069
13101
  };
12070
13102
  }
12071
- function makeTopologyOps(_module) {
12072
- function shapeType(shape) {
12073
- const { occt, brep } = brepOf(shape, "shapeType");
12074
- return occt.shapeType(brep);
12075
- }
12076
- function isSame(a, b) {
12077
- const sa = asManifoldShape(a);
12078
- const sb = asManifoldShape(b);
12079
- if (!sa || !sb) return false;
12080
- return sa.manifold === sb.manifold;
12081
- }
12082
- function isEqual(a, b) {
12083
- return isSame(a, b);
12084
- }
12085
- function hashCode(shape, upperBound) {
12086
- if (!asManifoldShape(shape)) return 0;
12087
- const { occt, brep } = brepOf(shape, "hashCode");
12088
- return occt.hashCode(brep, upperBound);
12089
- }
12090
- function isNull(shape) {
12091
- const s = asManifoldShape(shape);
12092
- if (!s) return true;
13103
+ function shapeType(shape) {
13104
+ if (asManifoldShape(shape)) return "solid";
13105
+ const { occt, brep } = brepOf(shape, "shapeType");
13106
+ return occt.shapeType(brep);
13107
+ }
13108
+ function isSame(a, b) {
13109
+ const sa = asManifoldShape(a);
13110
+ const sb = asManifoldShape(b);
13111
+ if (!sa || !sb) return false;
13112
+ return sa.manifold === sb.manifold;
13113
+ }
13114
+ function hashCode(shape, upperBound) {
13115
+ if (!asManifoldShape(shape)) return 0;
13116
+ const { occt, brep } = brepOf(shape, "hashCode");
13117
+ return occt.hashCode(brep, upperBound);
13118
+ }
13119
+ function isNull(shape) {
13120
+ const s = asManifoldShape(shape);
13121
+ if (!s) return true;
13122
+ const solid = unwrap(s);
13123
+ return !solid || typeof solid.isEmpty === "function" && solid.isEmpty();
13124
+ }
13125
+ function iterShapes(shape, type) {
13126
+ const s = asManifoldShape(shape);
13127
+ if (!s) return [];
13128
+ if (type === "solid") return [shape];
13129
+ if (type === "vertex") {
12093
13130
  const solid = unwrap(s);
12094
- return !solid || typeof solid.isEmpty === "function" && solid.isEmpty();
12095
- }
12096
- function shapeOrientation(_shape) {
12097
- return "forward";
12098
- }
12099
- function iterShapes(shape, type) {
12100
- const s = asManifoldShape(shape);
12101
- if (!s) return [];
12102
- if (type === "solid") return [shape];
12103
- if (type !== "edge" && type !== "face") return [];
12104
- if (!s.node.replayable) return [];
12105
- const occt = resolveOcct();
12106
- if (!occt) return [];
12107
- const brep = brepCache.get(s.node) ?? (() => {
12108
- const b = replay(s.node, occt);
12109
- brepCache.set(s.node, b);
12110
- return b;
12111
- })();
12112
- return occt.iterShapes(brep, type).map((sub, index) => ({
13131
+ if (solid && typeof solid.getMesh === "function") return extractVertices(solid.getMesh()).map((v, index) => ({
13132
+ ...v,
12113
13133
  __manifoldSub: true,
12114
13134
  index,
12115
- box: occt.boundingBox(sub)
13135
+ box: {
13136
+ min: v.point,
13137
+ max: v.point
13138
+ },
13139
+ parent: s.node,
13140
+ subType: "vertex"
12116
13141
  }));
13142
+ return [];
12117
13143
  }
12118
- function iterShapeList(list, callback) {
12119
- occtOrThrow("iterShapeList").iterShapeList(list, callback);
12120
- }
12121
- function edgeToFaceMap(shape) {
12122
- const { occt, brep } = brepOf(shape, "edgeToFaceMap");
12123
- return occt.edgeToFaceMap(brep);
12124
- }
12125
- function sharedEdges(faceA, faceB) {
12126
- return occtOrThrow("sharedEdges").sharedEdges(faceA, faceB);
13144
+ if (type !== "edge" && type !== "face") return [];
13145
+ if (type === "face") {
13146
+ const solid = unwrap(s);
13147
+ if (solid && typeof solid.getMesh === "function") return extractFaces(solid.getMesh()).map((f, index) => ({
13148
+ ...f,
13149
+ __manifoldSub: true,
13150
+ index,
13151
+ box: {
13152
+ min: f.min,
13153
+ max: f.max
13154
+ },
13155
+ parent: s.node,
13156
+ subType: "face"
13157
+ }));
12127
13158
  }
12128
- function adjacentFaces(shape, face) {
12129
- const { occt, brep } = brepOf(shape, "adjacentFaces");
12130
- return occt.adjacentFaces(brep, face);
13159
+ if (type === "edge") {
13160
+ const solid = unwrap(s);
13161
+ if (solid && typeof solid.getMesh === "function") return extractEdges(solid.getMesh()).map((e, index) => ({
13162
+ ...e,
13163
+ __manifoldSub: true,
13164
+ index,
13165
+ box: {
13166
+ min: e.min,
13167
+ max: e.max
13168
+ },
13169
+ parent: s.node,
13170
+ subType: "edge"
13171
+ }));
12131
13172
  }
13173
+ if (!s.node.replayable) return [];
13174
+ const occt = resolveOcct();
13175
+ if (!occt) return [];
13176
+ const brep = brepCache.get(s.node) ?? (() => {
13177
+ const b = replay(s.node, occt);
13178
+ brepCache.set(s.node, b);
13179
+ return b;
13180
+ })();
13181
+ return occt.iterShapes(brep, type).map((sub, index) => ({
13182
+ __manifoldSub: true,
13183
+ index,
13184
+ box: occt.boundingBox(sub),
13185
+ occt: sub,
13186
+ parent: s.node,
13187
+ subType: type
13188
+ }));
13189
+ }
13190
+ function edgeToFaceMap(shape) {
13191
+ const { occt, brep } = brepOf(shape, "edgeToFaceMap");
13192
+ return occt.edgeToFaceMap(brep);
13193
+ }
13194
+ function adjacentFaces(shape, face) {
13195
+ const { occt, brep } = brepOf(shape, "adjacentFaces");
13196
+ return occt.adjacentFaces(brep, face);
13197
+ }
13198
+ function makeTopologyOps(_module) {
12132
13199
  return {
12133
13200
  iterShapes,
12134
- iterShapeList,
13201
+ iterShapeList: (list, callback) => {
13202
+ occtOrThrow("iterShapeList").iterShapeList(list, callback);
13203
+ },
12135
13204
  shapeType,
12136
13205
  isSame,
12137
- isEqual,
13206
+ isEqual: isSame,
12138
13207
  downcast: (shape) => shape,
12139
13208
  hashCode,
12140
13209
  isNull,
12141
- shapeOrientation,
13210
+ shapeOrientation: (_shape) => "forward",
12142
13211
  edgeToFaceMap,
12143
- sharedEdges,
13212
+ sharedEdges: (faceA, faceB) => occtOrThrow("sharedEdges").sharedEdges(faceA, faceB),
12144
13213
  adjacentFaces,
12145
13214
  sew: () => {
12146
13215
  throw new Error("manifold: sew is unsupported on the mesh kernel; use a B-rep kernel");
@@ -12690,6 +13759,12 @@ function vertexAt(mesh, index) {
12690
13759
  ];
12691
13760
  }
12692
13761
  function viaOcct(shape, query) {
13762
+ const witness = shape;
13763
+ if (witness && witness.__manifoldSub && witness.occt) {
13764
+ const occt = resolveOcct();
13765
+ if (!occt) throw new Error("manifold: sub-shape geometry query requires a registered occt kernel");
13766
+ return query(witness.occt, occt);
13767
+ }
12693
13768
  const ms = asManifoldShape(shape);
12694
13769
  if (!ms) throw new Error("manifold: exact geometry query requires a manifold shape handle");
12695
13770
  const occt = resolveOcct();
@@ -12704,10 +13779,13 @@ function viaOcct(shape, query) {
12704
13779
  }
12705
13780
  function makeGeometryOps(_module) {
12706
13781
  return {
12707
- curveType: (shape) => viaOcct(shape, (s, occt) => occt.curveType(s)),
12708
- curveParameters: (shape) => viaOcct(shape, (s, occt) => occt.curveParameters(s)),
12709
- curvePointAtParam: (shape, param) => viaOcct(shape, (s, occt) => occt.curvePointAtParam(s, param)),
12710
- curveTangent: (shape, param) => viaOcct(shape, (s, occt) => occt.curveTangent(s, param)),
13782
+ curveType: (shape) => isNativeEdge(shape) ? shape.curveType : viaOcct(shape, (s, occt) => occt.curveType(s)),
13783
+ curveParameters: (shape) => isNativeEdge(shape) ? [0, shape.length] : viaOcct(shape, (s, occt) => occt.curveParameters(s)),
13784
+ curvePointAtParam: (shape, param) => isNativeEdge(shape) ? edgePointAt(shape, param) : viaOcct(shape, (s, occt) => occt.curvePointAtParam(s, param)),
13785
+ curveTangent: (shape, param) => isNativeEdge(shape) ? {
13786
+ point: edgePointAt(shape, param),
13787
+ tangent: edgeTangentAt(shape, param)
13788
+ } : viaOcct(shape, (s, occt) => occt.curveTangent(s, param)),
12711
13789
  curveIsClosed: (shape) => viaOcct(shape, (s, occt) => occt.curveIsClosed(s)),
12712
13790
  curveIsPeriodic: (shape) => viaOcct(shape, (s, occt) => occt.curveIsPeriodic(s)),
12713
13791
  curvePeriod: (shape) => viaOcct(shape, (s, occt) => occt.curvePeriod(s)),
@@ -12721,13 +13799,24 @@ function makeGeometryOps(_module) {
12721
13799
  getBezierPenultimatePole: (edge) => viaOcct(edge, (s, occt) => occt.getBezierPenultimatePole(s)),
12722
13800
  getNurbsCurveData: (edge) => viaOcct(edge, (s, occt) => occt.getNurbsCurveData?.(s) ?? null),
12723
13801
  vertexPosition: (vertex) => {
13802
+ if (isNativeVertex(vertex)) return vertex.point;
12724
13803
  if (!asManifoldShape(vertex)) return viaOcct(vertex, (s, occt) => occt.vertexPosition(s));
12725
13804
  return vertexAt(meshOf(vertex), 0);
12726
13805
  },
12727
- surfaceType: (face) => viaOcct(face, (s, occt) => occt.surfaceType(s)),
12728
- uvBounds: (face) => viaOcct(face, (s, occt) => occt.uvBounds(s)),
13806
+ surfaceType: (face) => {
13807
+ if (isNativeFace(face)) return "plane";
13808
+ const ms = asManifoldShape(face);
13809
+ if (ms && ms.node.op === "profileFace") return "plane";
13810
+ return viaOcct(face, (s, occt) => occt.surfaceType(s));
13811
+ },
13812
+ uvBounds: (face) => isNativeFace(face) ? {
13813
+ uMin: 0,
13814
+ uMax: 1,
13815
+ vMin: 0,
13816
+ vMax: 1
13817
+ } : viaOcct(face, (s, occt) => occt.uvBounds(s)),
12729
13818
  outerWire: (face) => viaOcct(face, (s, occt) => occt.outerWire(s)),
12730
- surfaceNormal: (face, u, v) => viaOcct(face, (s, occt) => occt.surfaceNormal(s, u, v)),
13819
+ surfaceNormal: (face, u, v) => isNativeFace(face) ? face.normal : viaOcct(face, (s, occt) => occt.surfaceNormal(s, u, v)),
12731
13820
  pointOnSurface: (face, u, v) => viaOcct(face, (s, occt) => occt.pointOnSurface(s, u, v)),
12732
13821
  uvFromPoint: (face, point) => viaOcct(face, (s, occt) => occt.uvFromPoint(s, point)),
12733
13822
  projectPointOnFace: (face, point) => viaOcct(face, (s, occt) => occt.projectPointOnFace(s, point)),
@@ -12821,6 +13910,614 @@ function makeRepairOps(_module) {
12821
13910
  };
12822
13911
  }
12823
13912
  //#endregion
13913
+ //#region src/kernel/manifold/kernel2dNative.ts
13914
+ var CHORD_TOL = .004;
13915
+ function isNative(x) {
13916
+ return !!x && typeof x === "object" && x.__nativeC2d === true;
13917
+ }
13918
+ function line(p1, p2) {
13919
+ return {
13920
+ __nativeC2d: true,
13921
+ k: "line",
13922
+ p1,
13923
+ p2
13924
+ };
13925
+ }
13926
+ function conic(c, u, v, a0, a1) {
13927
+ return {
13928
+ __nativeC2d: true,
13929
+ k: "conic",
13930
+ c,
13931
+ u,
13932
+ v,
13933
+ a0,
13934
+ a1
13935
+ };
13936
+ }
13937
+ function applyPt(m, [x, y]) {
13938
+ return [m.a * x + m.b * y + m.tx, m.c * x + m.d * y + m.ty];
13939
+ }
13940
+ function applyVec(m, [x, y]) {
13941
+ return [m.a * x + m.b * y, m.c * x + m.d * y];
13942
+ }
13943
+ function compose(p, q) {
13944
+ return {
13945
+ a: p.a * q.a + p.b * q.c,
13946
+ b: p.a * q.b + p.b * q.d,
13947
+ c: p.c * q.a + p.d * q.c,
13948
+ d: p.c * q.b + p.d * q.d,
13949
+ tx: p.a * q.tx + p.b * q.ty + p.tx,
13950
+ ty: p.c * q.tx + p.d * q.ty + p.ty
13951
+ };
13952
+ }
13953
+ function transform(curve, m) {
13954
+ if (curve.k === "line") return line(applyPt(m, curve.p1), applyPt(m, curve.p2));
13955
+ if (curve.k === "bezier") return {
13956
+ __nativeC2d: true,
13957
+ k: "bezier",
13958
+ pts: curve.pts.map((p) => applyPt(m, p))
13959
+ };
13960
+ return conic(applyPt(m, curve.c), applyVec(m, curve.u), applyVec(m, curve.v), curve.a0, curve.a1);
13961
+ }
13962
+ function bezierAt(pts, t) {
13963
+ const tmp = pts.map((p) => [p[0], p[1]]);
13964
+ for (let k = 1; k < tmp.length; k++) for (let i = 0; i < tmp.length - k; i++) {
13965
+ const a = tmp[i] ?? [0, 0];
13966
+ const b = tmp[i + 1] ?? [0, 0];
13967
+ tmp[i] = [a[0] * (1 - t) + b[0] * t, a[1] * (1 - t) + b[1] * t];
13968
+ }
13969
+ return tmp[0] ?? [0, 0];
13970
+ }
13971
+ function conicPoint(c, t) {
13972
+ const ct = Math.cos(t);
13973
+ const st = Math.sin(t);
13974
+ return [c.c[0] + c.u[0] * ct + c.v[0] * st, c.c[1] + c.u[1] * ct + c.v[1] * st];
13975
+ }
13976
+ /** Bezier first derivative B'(t) = n·Σ(P_{i+1}−P_i)·b_{i,n−1}(t). */
13977
+ function bezierD1(pts, t) {
13978
+ const n = pts.length - 1;
13979
+ if (n < 1) return [0, 0];
13980
+ const diff = [];
13981
+ for (let i = 0; i < n; i++) {
13982
+ const a = pts[i] ?? [0, 0];
13983
+ const b = pts[i + 1] ?? [0, 0];
13984
+ diff.push([n * (b[0] - a[0]), n * (b[1] - a[1])]);
13985
+ }
13986
+ return bezierAt(diff, t);
13987
+ }
13988
+ /**
13989
+ * Segment count for a conic arc, driven by chord-height tolerance rather than a
13990
+ * fixed count: the polygon's sagitta is `ρ(1 − cos(Δθ/2))`, so to keep it under
13991
+ * CHORD_TOL the per-segment angle must satisfy `Δθ ≤ 2·acos(1 − tol/ρ)`. This
13992
+ * gives tiny gridfinity corner arcs few segments (fast) while large circles get
13993
+ * enough to match OCCT's exact volume (parity). `ρ` is the larger conjugate
13994
+ * radius (ellipse worst case).
13995
+ */
13996
+ function conicSegments(curve) {
13997
+ const span = Math.abs(curve.a1 - curve.a0);
13998
+ const ru = Math.hypot(curve.u[0], curve.u[1]);
13999
+ const rv = Math.hypot(curve.v[0], curve.v[1]);
14000
+ const rho = Math.max(ru, rv);
14001
+ if (rho <= CHORD_TOL) return Math.max(2, Math.ceil(span / (2 * Math.PI) * 8));
14002
+ const maxAngle = 2 * Math.acos(Math.max(-1, 1 - CHORD_TOL / rho));
14003
+ return Math.max(2, Math.ceil(span / maxAngle));
14004
+ }
14005
+ function sample(curve) {
14006
+ if (curve.k === "line") return [curve.p1, curve.p2];
14007
+ if (curve.k === "bezier") {
14008
+ const out = [];
14009
+ for (let i = 0; i <= 24; i++) out.push(bezierAt(curve.pts, i / 24));
14010
+ return out;
14011
+ }
14012
+ const span = curve.a1 - curve.a0;
14013
+ const n = conicSegments(curve);
14014
+ const pts = [];
14015
+ for (let i = 0; i <= n; i++) {
14016
+ const t = curve.a0 + span * i / n;
14017
+ const ct = Math.cos(t);
14018
+ const st = Math.sin(t);
14019
+ pts.push([curve.c[0] + curve.u[0] * ct + curve.v[0] * st, curve.c[1] + curve.u[1] * ct + curve.v[1] * st]);
14020
+ }
14021
+ return pts;
14022
+ }
14023
+ /** Circle/arc through param helpers. */
14024
+ function circleThrough3(p1, pm, p2) {
14025
+ const ax = p1[0];
14026
+ const ay = p1[1];
14027
+ const bx = pm[0];
14028
+ const by = pm[1];
14029
+ const cx2 = p2[0];
14030
+ const cy2 = p2[1];
14031
+ const d = 2 * (ax * (by - cy2) + bx * (cy2 - ay) + cx2 * (ay - by));
14032
+ if (Math.abs(d) < 1e-12) return line(p1, p2);
14033
+ const ux = ((ax * ax + ay * ay) * (by - cy2) + (bx * bx + by * by) * (cy2 - ay) + (cx2 * cx2 + cy2 * cy2) * (ay - by)) / d;
14034
+ const uy = ((ax * ax + ay * ay) * (cx2 - bx) + (bx * bx + by * by) * (ax - cx2) + (cx2 * cx2 + cy2 * cy2) * (bx - ax)) / d;
14035
+ const center = [ux, uy];
14036
+ const r = Math.hypot(ax - ux, ay - uy);
14037
+ const ang = (p) => Math.atan2(p[1] - uy, p[0] - ux);
14038
+ const a0 = ang(p1);
14039
+ const am = ang(pm);
14040
+ let a1 = ang(p2);
14041
+ const norm = (x) => (x % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
14042
+ const dm = norm(am - a0);
14043
+ const d1 = norm(a1 - a0);
14044
+ if (dm <= d1) a1 = a0 + d1;
14045
+ else a1 = a0 - norm(a0 - a1);
14046
+ return conic(center, [r, 0], [0, r], a0, a1);
14047
+ }
14048
+ function makeNativeKernel2DOps(module, occt) {
14049
+ const PLACEHOLDER = {
14050
+ delete: () => {},
14051
+ isEmpty: () => false
14052
+ };
14053
+ function delegate(method, ...args) {
14054
+ const o = occt();
14055
+ const fn = o?.[method];
14056
+ if (!fn) throw new Error(`manifold 2D: ${method} needs an OCCT kernel (none registered)`);
14057
+ return fn.call(o, ...args);
14058
+ }
14059
+ const asC = (h) => h;
14060
+ function occtOr(method) {
14061
+ const o = occt();
14062
+ if (!o) throw new Error(`manifold 2D: ${method} needs an OCCT kernel (none registered)`);
14063
+ return o;
14064
+ }
14065
+ /**
14066
+ * Reconstruct a native curve descriptor as an *exact* OCCT 2D curve so it can
14067
+ * flow into OCCT-only paths (2D booleans, intersection, NURBS). Curve type is
14068
+ * preserved — line→line, circle→circle, arc→3-point arc, ellipse→ellipse —
14069
+ * which keeps OCCT's shared-edge detection working in blueprint booleans.
14070
+ * Skewed/odd cases fall back to a dense B-spline. OCCT handles pass through.
14071
+ */
14072
+ function toOcct(h) {
14073
+ if (!isNative(h)) return h;
14074
+ const o = occtOr("toOcct");
14075
+ const c = asC(h);
14076
+ if (c.k === "line") return o.makeLine2d(c.p1[0], c.p1[1], c.p2[0], c.p2[1]);
14077
+ if (c.k === "bezier") return o.makeBezier2d(c.pts);
14078
+ const ru = Math.hypot(c.u[0], c.u[1]);
14079
+ const rv = Math.hypot(c.v[0], c.v[1]);
14080
+ const full = Math.abs(Math.abs(c.a1 - c.a0) - 2 * Math.PI) < 1e-9;
14081
+ const sense = c.u[0] * c.v[1] - c.u[1] * c.v[0] >= 0;
14082
+ if (Math.abs(ru - rv) < 1e-9 * Math.max(1, ru)) {
14083
+ if (full) return o.makeCircle2d(c.c[0], c.c[1], ru, sense);
14084
+ const p0 = conicPoint(c, c.a0);
14085
+ const pm = conicPoint(c, (c.a0 + c.a1) / 2);
14086
+ const p1 = conicPoint(c, c.a1);
14087
+ return o.makeArc2dThreePoints(p0[0], p0[1], pm[0], pm[1], p1[0], p1[1]);
14088
+ }
14089
+ const dotUV = c.u[0] * c.v[0] + c.u[1] * c.v[1];
14090
+ if (Math.abs(dotUV) < 1e-9 * Math.max(1, ru * rv)) {
14091
+ const xdx = c.u[0] / ru;
14092
+ const xdy = c.u[1] / ru;
14093
+ 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);
14094
+ }
14095
+ return o.makeBSpline2d(sample(c).map((p) => [p[0], p[1]]));
14096
+ }
14097
+ const impl = {
14098
+ createPoint2d: (x, y) => ({
14099
+ x,
14100
+ y
14101
+ }),
14102
+ createDirection2d: (x, y) => ({
14103
+ x,
14104
+ y
14105
+ }),
14106
+ createVector2d: (x, y) => ({
14107
+ x,
14108
+ y
14109
+ }),
14110
+ createAxis2d: (px, py, dx, dy) => ({
14111
+ px,
14112
+ py,
14113
+ dx,
14114
+ dy
14115
+ }),
14116
+ wrapCurve2dHandle: (h) => h,
14117
+ createCurve2dAdaptor: (h) => h,
14118
+ makeLine2d: (x1, y1, x2, y2) => line([x1, y1], [x2, y2]),
14119
+ makeCircle2d: (cx, cy, r, sense = true) => conic([cx, cy], [r, 0], sense ? [0, r] : [0, -r], 0, 2 * Math.PI),
14120
+ makeArc2dThreePoints: (x1, y1, xm, ym, x2, y2) => circleThrough3([x1, y1], [xm, ym], [x2, y2]),
14121
+ makeArc2dTangent: (sx, sy, tx, ty, ex, ey) => {
14122
+ const tlen = Math.hypot(tx, ty) || 1;
14123
+ const nx = -ty / tlen;
14124
+ const ny = tx / tlen;
14125
+ const chord = [ex - sx, ey - sy];
14126
+ const ndotc = nx * chord[0] + ny * chord[1];
14127
+ if (Math.abs(ndotc) < 1e-12) return line([sx, sy], [ex, ey]);
14128
+ const t = (chord[0] * chord[0] + chord[1] * chord[1]) / (2 * ndotc);
14129
+ const cx = sx + nx * t;
14130
+ const cy = sy + ny * t;
14131
+ const r = Math.hypot(sx - cx, sy - cy);
14132
+ const a0 = Math.atan2(sy - cy, sx - cx);
14133
+ let a1 = Math.atan2(ey - cy, ex - cx);
14134
+ nx * tx + ny * ty;
14135
+ const norm = (x) => (x % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
14136
+ const tangCCW = [-(sy - cy), sx - cx];
14137
+ if (tangCCW[0] * tx + tangCCW[1] * ty >= 0) a1 = a0 + norm(a1 - a0);
14138
+ else a1 = a0 - norm(a0 - a1);
14139
+ return conic([cx, cy], [r, 0], [0, r], a0, a1);
14140
+ },
14141
+ makeEllipse2d: (cx, cy, maj, min, xdx = 1, xdy = 0, sense = true) => {
14142
+ const xl = Math.hypot(xdx, xdy) || 1;
14143
+ const ux = xdx / xl * maj;
14144
+ const uy = xdy / xl * maj;
14145
+ const vx = -xdy / xl * min * (sense ? 1 : -1);
14146
+ const vy = xdx / xl * min * (sense ? 1 : -1);
14147
+ return conic([cx, cy], [ux, uy], [vx, vy], 0, 2 * Math.PI);
14148
+ },
14149
+ makeEllipseArc2d: (cx, cy, maj, min, a0, a1, xdx = 1, xdy = 0, sense = true) => {
14150
+ const xl = Math.hypot(xdx, xdy) || 1;
14151
+ const ux = xdx / xl * maj;
14152
+ const uy = xdy / xl * maj;
14153
+ const vx = -xdy / xl * min * (sense ? 1 : -1);
14154
+ const vy = xdx / xl * min * (sense ? 1 : -1);
14155
+ return conic([cx, cy], [ux, uy], [vx, vy], a0, a1);
14156
+ },
14157
+ makeBezier2d: (points) => ({
14158
+ __nativeC2d: true,
14159
+ k: "bezier",
14160
+ pts: points.map((p) => [p[0], p[1]])
14161
+ }),
14162
+ evaluateCurve2d: (h, param) => {
14163
+ const c = asC(h);
14164
+ if (c.k === "line") {
14165
+ const dx = c.p2[0] - c.p1[0];
14166
+ const dy = c.p2[1] - c.p1[1];
14167
+ const len = Math.hypot(dx, dy) || 1;
14168
+ return [c.p1[0] + dx * param / len, c.p1[1] + dy * param / len];
14169
+ }
14170
+ if (c.k === "bezier") return bezierAt(c.pts, param);
14171
+ 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)];
14172
+ },
14173
+ evaluateCurve2dD1: (h, param) => {
14174
+ const c = asC(h);
14175
+ if (c.k === "line") {
14176
+ const dx = c.p2[0] - c.p1[0];
14177
+ const dy = c.p2[1] - c.p1[1];
14178
+ const len = Math.hypot(dx, dy) || 1;
14179
+ return {
14180
+ point: [c.p1[0] + dx * param / len, c.p1[1] + dy * param / len],
14181
+ tangent: [dx / len, dy / len]
14182
+ };
14183
+ }
14184
+ if (c.k === "bezier") return {
14185
+ point: bezierAt(c.pts, param),
14186
+ tangent: bezierD1(c.pts, param)
14187
+ };
14188
+ const ct = Math.cos(param);
14189
+ const st = Math.sin(param);
14190
+ return {
14191
+ point: [c.c[0] + c.u[0] * ct + c.v[0] * st, c.c[1] + c.u[1] * ct + c.v[1] * st],
14192
+ tangent: [-c.u[0] * st + c.v[0] * ct, -c.u[1] * st + c.v[1] * ct]
14193
+ };
14194
+ },
14195
+ getCurve2dBounds: (h) => {
14196
+ const c = asC(h);
14197
+ if (c.k === "conic") return {
14198
+ first: c.a0,
14199
+ last: c.a1
14200
+ };
14201
+ if (c.k === "line") return {
14202
+ first: 0,
14203
+ last: Math.hypot(c.p2[0] - c.p1[0], c.p2[1] - c.p1[1])
14204
+ };
14205
+ return {
14206
+ first: 0,
14207
+ last: 1
14208
+ };
14209
+ },
14210
+ getCurve2dType: (h) => {
14211
+ const c = asC(h);
14212
+ return c.k === "line" ? "LINE" : c.k === "bezier" ? "BEZIER" : "CIRCLE";
14213
+ },
14214
+ reverseCurve2d: (h) => {
14215
+ const c = asC(h);
14216
+ if (c.k === "line") {
14217
+ const t = c.p1;
14218
+ c.p1 = c.p2;
14219
+ c.p2 = t;
14220
+ } else if (c.k === "bezier") c.pts.reverse();
14221
+ else {
14222
+ const t = c.a0;
14223
+ c.a0 = c.a1;
14224
+ c.a1 = t;
14225
+ }
14226
+ },
14227
+ copyCurve2d: (h) => structuredClone(asC(h)),
14228
+ trimCurve2d: (h, start, end) => {
14229
+ const c = asC(h);
14230
+ if (c.k === "conic") return conic(c.c, c.u, c.v, start, end);
14231
+ if (c.k === "line") {
14232
+ const dx = c.p2[0] - c.p1[0];
14233
+ const dy = c.p2[1] - c.p1[1];
14234
+ const len = Math.hypot(dx, dy) || 1;
14235
+ 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]);
14236
+ }
14237
+ return structuredClone(c);
14238
+ },
14239
+ createIdentityGTrsf2d: () => ({
14240
+ a: 1,
14241
+ b: 0,
14242
+ c: 0,
14243
+ d: 1,
14244
+ tx: 0,
14245
+ ty: 0
14246
+ }),
14247
+ createTranslationGTrsf2d: (dx, dy) => ({
14248
+ a: 1,
14249
+ b: 0,
14250
+ c: 0,
14251
+ d: 1,
14252
+ tx: dx,
14253
+ ty: dy
14254
+ }),
14255
+ createRotationGTrsf2d: (angle, cx, cy) => {
14256
+ const co = Math.cos(angle);
14257
+ const si = Math.sin(angle);
14258
+ return {
14259
+ a: co,
14260
+ b: -si,
14261
+ c: si,
14262
+ d: co,
14263
+ tx: cx - (co * cx - si * cy),
14264
+ ty: cy - (si * cx + co * cy)
14265
+ };
14266
+ },
14267
+ createScaleGTrsf2d: (f, cx, cy) => ({
14268
+ a: f,
14269
+ b: 0,
14270
+ c: 0,
14271
+ d: f,
14272
+ tx: cx - f * cx,
14273
+ ty: cy - f * cy
14274
+ }),
14275
+ createMirrorGTrsf2d: (cx, cy, mode, ox = 0, oy = 0, dx = 1, dy = 0) => {
14276
+ if (mode === "point") return {
14277
+ a: -1,
14278
+ b: 0,
14279
+ c: 0,
14280
+ d: -1,
14281
+ tx: 2 * cx,
14282
+ ty: 2 * cy
14283
+ };
14284
+ const l = Math.hypot(dx, dy) || 1;
14285
+ const ux = dx / l;
14286
+ const uy = dy / l;
14287
+ const a = ux * ux - uy * uy;
14288
+ const b = 2 * ux * uy;
14289
+ return {
14290
+ a,
14291
+ b,
14292
+ c: b,
14293
+ d: -a,
14294
+ tx: ox - (a * ox + b * oy),
14295
+ ty: oy - (b * ox - a * oy)
14296
+ };
14297
+ },
14298
+ createAffinityGTrsf2d: (ox, oy, dx, dy, ratio) => {
14299
+ const l = Math.hypot(dx, dy) || 1;
14300
+ const ux = dx / l;
14301
+ const uy = dy / l;
14302
+ const nx = -uy;
14303
+ const ny = ux;
14304
+ const a = ux * ux + ratio * nx * nx;
14305
+ const b = ux * uy + ratio * nx * ny;
14306
+ const c = uy * ux + ratio * ny * nx;
14307
+ const d = uy * uy + ratio * ny * ny;
14308
+ return {
14309
+ a,
14310
+ b,
14311
+ c,
14312
+ d,
14313
+ tx: ox - (a * ox + b * oy),
14314
+ ty: oy - (c * ox + d * oy)
14315
+ };
14316
+ },
14317
+ setGTrsf2dTranslationPart: (g, dx, dy) => {
14318
+ const m = g;
14319
+ m.tx = dx;
14320
+ m.ty = dy;
14321
+ },
14322
+ multiplyGTrsf2d: (base, other) => {
14323
+ const r = compose(base, other);
14324
+ Object.assign(base, r);
14325
+ },
14326
+ transformCurve2dGeneral: (h, g) => transform(asC(h), g),
14327
+ translateCurve2d: (h, dx, dy) => transform(asC(h), {
14328
+ a: 1,
14329
+ b: 0,
14330
+ c: 0,
14331
+ d: 1,
14332
+ tx: dx,
14333
+ ty: dy
14334
+ }),
14335
+ rotateCurve2d: (h, angle, cx, cy) => {
14336
+ const co = Math.cos(angle);
14337
+ const si = Math.sin(angle);
14338
+ return transform(asC(h), {
14339
+ a: co,
14340
+ b: -si,
14341
+ c: si,
14342
+ d: co,
14343
+ tx: cx - (co * cx - si * cy),
14344
+ ty: cy - (si * cx + co * cy)
14345
+ });
14346
+ },
14347
+ scaleCurve2d: (h, f, cx, cy) => transform(asC(h), {
14348
+ a: f,
14349
+ b: 0,
14350
+ c: 0,
14351
+ d: f,
14352
+ tx: cx - f * cx,
14353
+ ty: cy - f * cy
14354
+ }),
14355
+ mirrorCurve2dAtPoint: (h, cx, cy) => transform(asC(h), {
14356
+ a: -1,
14357
+ b: 0,
14358
+ c: 0,
14359
+ d: -1,
14360
+ tx: 2 * cx,
14361
+ ty: 2 * cy
14362
+ }),
14363
+ mirrorCurve2dAcrossAxis: (h, ox, oy, dx, dy) => {
14364
+ const l = Math.hypot(dx, dy) || 1;
14365
+ const ux = dx / l;
14366
+ const uy = dy / l;
14367
+ const a = ux * ux - uy * uy;
14368
+ const b = 2 * ux * uy;
14369
+ return transform(asC(h), {
14370
+ a,
14371
+ b,
14372
+ c: b,
14373
+ d: -a,
14374
+ tx: ox - (a * ox + b * oy),
14375
+ ty: oy - (b * ox - a * oy)
14376
+ });
14377
+ },
14378
+ affinityTransform2d: (h, ox, oy, dx, dy, ratio) => {
14379
+ const l = Math.hypot(dx, dy) || 1;
14380
+ const ux = dx / l;
14381
+ const uy = dy / l;
14382
+ const nx = -uy;
14383
+ const ny = ux;
14384
+ const a = ux * ux + ratio * nx * nx;
14385
+ const b = ux * uy + ratio * nx * ny;
14386
+ const c = uy * ux + ratio * ny * nx;
14387
+ const d = uy * uy + ratio * ny * ny;
14388
+ return transform(asC(h), {
14389
+ a,
14390
+ b,
14391
+ c,
14392
+ d,
14393
+ tx: ox - (a * ox + b * oy),
14394
+ ty: oy - (c * ox + d * oy)
14395
+ });
14396
+ },
14397
+ createBoundingBox2d: () => ({
14398
+ min: [Infinity, Infinity],
14399
+ max: [-Infinity, -Infinity]
14400
+ }),
14401
+ addCurveToBBox2d: (bb, h) => {
14402
+ const box = bb;
14403
+ let pts;
14404
+ if (isNative(h)) pts = sample(asC(h));
14405
+ else {
14406
+ const o = occtOr("addCurveToBBox2d");
14407
+ const { first, last } = o.getCurve2dBounds(h);
14408
+ pts = [];
14409
+ for (let i = 0; i <= 32; i++) pts.push(o.evaluateCurve2d(h, first + (last - first) * i / 32));
14410
+ }
14411
+ for (const [x, y] of pts) {
14412
+ if (x < box.min[0]) box.min[0] = x;
14413
+ if (y < box.min[1]) box.min[1] = y;
14414
+ if (x > box.max[0]) box.max[0] = x;
14415
+ if (y > box.max[1]) box.max[1] = y;
14416
+ }
14417
+ },
14418
+ getBBox2dBounds: (bb) => {
14419
+ const box = bb;
14420
+ return {
14421
+ xMin: box.min[0],
14422
+ yMin: box.min[1],
14423
+ xMax: box.max[0],
14424
+ yMax: box.max[1]
14425
+ };
14426
+ },
14427
+ mergeBBox2d: (t, o) => {
14428
+ const a = t;
14429
+ const b = o;
14430
+ a.min[0] = Math.min(a.min[0], b.min[0]);
14431
+ a.min[1] = Math.min(a.min[1], b.min[1]);
14432
+ a.max[0] = Math.max(a.max[0], b.max[0]);
14433
+ a.max[1] = Math.max(a.max[1], b.max[1]);
14434
+ },
14435
+ isBBox2dOut: (a, b) => {
14436
+ const x = a;
14437
+ const y = b;
14438
+ 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];
14439
+ },
14440
+ isBBox2dOutPoint: (bb, x, y) => {
14441
+ const box = bb;
14442
+ return x < box.min[0] || x > box.max[0] || y < box.min[1] || y > box.max[1];
14443
+ },
14444
+ getCurve2dCircleData: (h) => {
14445
+ const c = asC(h);
14446
+ if (c.k !== "conic") return null;
14447
+ const ru = Math.hypot(c.u[0], c.u[1]);
14448
+ const rv = Math.hypot(c.v[0], c.v[1]);
14449
+ if (Math.abs(ru - rv) > 1e-6) return null;
14450
+ return {
14451
+ cx: c.c[0],
14452
+ cy: c.c[1],
14453
+ radius: ru,
14454
+ isDirect: true
14455
+ };
14456
+ },
14457
+ liftCurve2dToPlane: (h, origin, zDir, xDir) => {
14458
+ const zx = zDir[0];
14459
+ const zy = zDir[1];
14460
+ const zz = zDir[2];
14461
+ const xx = xDir[0];
14462
+ const xy = xDir[1];
14463
+ const xz = xDir[2];
14464
+ const yx = zy * xz - zz * xy;
14465
+ const yy = zz * xx - zx * xz;
14466
+ const yz = zx * xy - zy * xx;
14467
+ return wrap(PLACEHOLDER, makeNode("profileEdge", { pts: sample(asC(h)).map(([u, v]) => [
14468
+ origin[0] + xx * u + yx * v,
14469
+ origin[1] + xy * u + yy * v,
14470
+ origin[2] + xz * u + yz * v
14471
+ ]) }, []));
14472
+ },
14473
+ makeBSpline2d: (points, options) => occtOr("makeBSpline2d").makeBSpline2d(points, options),
14474
+ offsetCurve2d: (c, offset) => occtOr("offsetCurve2d").offsetCurve2d(toOcct(c), offset),
14475
+ intersectCurves2d: (c1, c2, tol) => occtOr("intersectCurves2d").intersectCurves2d(toOcct(c1), toOcct(c2), tol),
14476
+ projectPointOnCurve2d: (c, x, y) => occtOr("projectPointOnCurve2d").projectPointOnCurve2d(toOcct(c), x, y),
14477
+ distanceBetweenCurves2d: (c1, c2, s1, e1, s2, e2) => occtOr("distanceBetweenCurves2d").distanceBetweenCurves2d(toOcct(c1), toOcct(c2), s1, e1, s2, e2),
14478
+ approximateCurve2dAsBSpline: (c, tol, cont, maxSeg) => occtOr("approximateCurve2dAsBSpline").approximateCurve2dAsBSpline(toOcct(c), tol, cont, maxSeg),
14479
+ decomposeBSpline2dToBeziers: (c) => occtOr("decomposeBSpline2dToBeziers").decomposeBSpline2dToBeziers(toOcct(c)),
14480
+ serializeCurve2d: (c) => occtOr("serializeCurve2d").serializeCurve2d(toOcct(c)),
14481
+ deserializeCurve2d: (data) => occtOr("deserializeCurve2d").deserializeCurve2d(data),
14482
+ splitCurve2d: (c, params) => occtOr("splitCurve2d").splitCurve2d(toOcct(c), params),
14483
+ getCurve2dEllipseData: (c) => occtOr("getCurve2dEllipseData").getCurve2dEllipseData(toOcct(c)),
14484
+ getCurve2dBezierPoles: (c) => occtOr("getCurve2dBezierPoles").getCurve2dBezierPoles(toOcct(c)),
14485
+ getCurve2dBezierDegree: (c) => occtOr("getCurve2dBezierDegree").getCurve2dBezierDegree(toOcct(c)),
14486
+ getCurve2dBSplineData: (c) => occtOr("getCurve2dBSplineData").getCurve2dBSplineData(toOcct(c)),
14487
+ buildEdgeOnSurface: (c, surface) => occtOr("buildEdgeOnSurface").buildEdgeOnSurface(toOcct(c), surface),
14488
+ extractSurfaceFromFace: (face) => occtOr("extractSurfaceFromFace").extractSurfaceFromFace(face),
14489
+ extractCurve2dFromEdge: (edge, face) => occtOr("extractCurve2dFromEdge").extractCurve2dFromEdge(edge, face),
14490
+ buildCurves3d: (wire) => {
14491
+ occtOr("buildCurves3d").buildCurves3d(wire);
14492
+ },
14493
+ fixWireOnFace: (wire, face, tol) => occtOr("fixWireOnFace").fixWireOnFace(wire, face, tol),
14494
+ fillSurface: (wires, opts) => occtOr("fillSurface").fillSurface(wires, opts)
14495
+ };
14496
+ for (const m of [
14497
+ "evaluateCurve2d",
14498
+ "evaluateCurve2dD1",
14499
+ "getCurve2dBounds",
14500
+ "getCurve2dType",
14501
+ "reverseCurve2d",
14502
+ "copyCurve2d",
14503
+ "trimCurve2d",
14504
+ "transformCurve2dGeneral",
14505
+ "translateCurve2d",
14506
+ "rotateCurve2d",
14507
+ "scaleCurve2d",
14508
+ "mirrorCurve2dAtPoint",
14509
+ "mirrorCurve2dAcrossAxis",
14510
+ "affinityTransform2d",
14511
+ "getCurve2dCircleData",
14512
+ "liftCurve2dToPlane"
14513
+ ]) {
14514
+ const nativeFn = impl[m];
14515
+ if (!nativeFn) continue;
14516
+ impl[m] = ((h, ...rest) => isNative(h) ? nativeFn(h, ...rest) : delegate(m, h, ...rest));
14517
+ }
14518
+ return impl;
14519
+ }
14520
+ //#endregion
12824
14521
  //#region src/kernel/manifold/kernel2dOps.ts
12825
14522
  var KERNEL_2D_METHODS = [
12826
14523
  "createPoint2d",
@@ -12888,17 +14585,15 @@ var KERNEL_2D_METHODS = [
12888
14585
  "fillSurface"
12889
14586
  ];
12890
14587
  function resolveOcct2D(method) {
12891
- let occt;
12892
- try {
12893
- occt = getKernel("occt");
12894
- } catch {
12895
- throw new Error(`manifold: ${method} unsupported on manifold kernel; no B-rep kernel registered`);
12896
- }
14588
+ const occt = resolveOcct();
14589
+ if (!occt) throw new Error(`manifold: ${method} unsupported on manifold kernel; no B-rep kernel registered`);
12897
14590
  return occt;
12898
14591
  }
12899
- function makeKernel2DOps(_module) {
14592
+ function makeKernel2DOps(module) {
12900
14593
  const ops = {};
12901
14594
  for (const method of KERNEL_2D_METHODS) ops[method] = (...args) => resolveOcct2D(method)[method](...args);
14595
+ const native = makeNativeKernel2DOps(module, () => resolveOcct());
14596
+ Object.assign(ops, native);
12902
14597
  return ops;
12903
14598
  }
12904
14599
  //#endregion