brepjs 20.13.4 → 20.13.6

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 (95) hide show
  1. package/dist/2d.cjs +6 -6
  2. package/dist/2d.js +6 -6
  3. package/dist/{adjacencyFns-DjblerpV.cjs → adjacencyFns-BLtq-nUN.cjs} +3 -3
  4. package/dist/{adjacencyFns-Ce4lxSWp.js → adjacencyFns-C1Hxy0tk.js} +3 -3
  5. package/dist/{blueprint-CeIbNzdp.js → blueprint-BzI6waZa.js} +5 -5
  6. package/dist/{blueprint-D4YxCDeX.cjs → blueprint-DlpFhHbU.cjs} +5 -5
  7. package/dist/{blueprintFns-fc9ZUSqe.cjs → blueprintFns-Cc9OyvKE.cjs} +2 -2
  8. package/dist/{blueprintFns-DEeIBzwN.js → blueprintFns-HFd0lu9A.js} +2 -2
  9. package/dist/{blueprintSketcher-BQ6ApvaQ.js → blueprintSketcher-BrQee61I.js} +3 -3
  10. package/dist/{blueprintSketcher-DID7kq5g.cjs → blueprintSketcher-DUT3-Cuo.cjs} +3 -3
  11. package/dist/{boolean2D-BsrkJbMe.cjs → boolean2D-BC0TwxyB.cjs} +4 -4
  12. package/dist/{boolean2D-QMPt29HH.js → boolean2D-CvBeghsg.js} +4 -4
  13. package/dist/{booleanFns-BuxgJmu2.js → booleanFns-Dtg2jqzi.js} +5 -5
  14. package/dist/{booleanFns-CQIepT9X.cjs → booleanFns-Dti1CK1F.cjs} +5 -5
  15. package/dist/brepjs.cjs +30 -30
  16. package/dist/brepjs.js +30 -30
  17. package/dist/{cameraFns-DL3zo3ol.cjs → cameraFns--Ab792oE.cjs} +3 -3
  18. package/dist/{cameraFns-s8km1l0J.js → cameraFns-CXONX5Da.js} +3 -3
  19. package/dist/core/disposal.d.ts +4 -1
  20. package/dist/core.cjs +2 -2
  21. package/dist/core.js +2 -2
  22. package/dist/{cornerFinder-D7P4vdaD.js → cornerFinder-C7f43oKE.js} +1 -1
  23. package/dist/{cornerFinder-uPZDWiyt.cjs → cornerFinder-DUzrQsfW.cjs} +1 -1
  24. package/dist/{curveFns-CQtl1RzI.js → curveFns-Dzgl3_MC.js} +1 -1
  25. package/dist/{curveFns-DkG5mlUj.cjs → curveFns-QaNfJzko.cjs} +1 -1
  26. package/dist/{drawFns-CSYj0D8x.cjs → drawFns-BRXk9fLg.cjs} +12 -12
  27. package/dist/{drawFns-BKnz1pa1.js → drawFns-WvF0fehK.js} +12 -12
  28. package/dist/{faceFns-f8AWRrzK.js → faceFns-BG1CBQUM.js} +2 -2
  29. package/dist/{faceFns-C_iC_WkC.cjs → faceFns-dj_c8LVc.cjs} +2 -2
  30. package/dist/{healingFns-W4_K9hXo.cjs → healingFns-BSNNLyXP.cjs} +12 -14
  31. package/dist/{healingFns-BRXArH8z.js → healingFns-CfkUEqcL.js} +12 -14
  32. package/dist/{helpers-m-lbbfaz.cjs → helpers-6Emek9dE.cjs} +6 -6
  33. package/dist/{helpers-COdYBjWD.js → helpers-DolHn2_s.js} +6 -6
  34. package/dist/{importFns-Kr02P3cy.cjs → importFns-CZb21dj7.cjs} +5 -5
  35. package/dist/{importFns-D8U3wLhV.js → importFns-D1AxkVC3.js} +5 -5
  36. package/dist/io.cjs +2 -2
  37. package/dist/io.js +2 -2
  38. package/dist/kernel/brepkit/brepkitWasmTypes.d.ts +17 -19
  39. package/dist/kernel/brepkit/constructionOps.d.ts +1 -1
  40. package/dist/kernel/brepkit/helpers.d.ts +25 -4
  41. package/dist/kernel/brepkit/modifierOps.d.ts +3 -3
  42. package/dist/kernel/occtWasm/occtWasmAdapter.cjs +1 -1
  43. package/dist/kernel/occtWasm/occtWasmAdapter.js +1 -1
  44. package/dist/{loftFns-CVk6mzZj.cjs → loftFns-BD0na-M4.cjs} +4 -4
  45. package/dist/{loftFns-MN9TgyG4.js → loftFns-Cua-MamP.js} +4 -4
  46. package/dist/{measureFns-9aeYKFiR.cjs → measureFns-FrNeGmXh.cjs} +4 -4
  47. package/dist/{measureFns-9GzKgCdl.js → measureFns-WGYWWnQ1.js} +4 -4
  48. package/dist/measurement.cjs +2 -2
  49. package/dist/measurement.js +2 -2
  50. package/dist/{meshFns-BwsjspzI.cjs → meshFns-Dy1a9wl_.cjs} +4 -4
  51. package/dist/{meshFns-un9EDg7X.js → meshFns-Lie8oAac.js} +4 -4
  52. package/dist/{occtWasmAdapter-4YnvqZOW.cjs → occtWasmAdapter-B9ZXx36v.cjs} +1 -1
  53. package/dist/{occtWasmAdapter-CGsnjMOe.js → occtWasmAdapter-CGkZIHyM.js} +1 -1
  54. package/dist/operations/sweepFns.d.ts +3 -3
  55. package/dist/operations.cjs +2 -2
  56. package/dist/operations.js +2 -2
  57. package/dist/{primitiveFns-CIPm9vsq.cjs → primitiveFns-Dpn3ZtUE.cjs} +5 -5
  58. package/dist/{primitiveFns-BICas2bb.js → primitiveFns-w-ST-RDJ.js} +5 -5
  59. package/dist/{principalBoxFns-CQZRNYI3.cjs → principalBoxFns-3ZURcOFP.cjs} +3 -3
  60. package/dist/{principalBoxFns-DQytdykp.js → principalBoxFns-CjfJnZfH.js} +3 -3
  61. package/dist/projection.cjs +1 -1
  62. package/dist/projection.js +1 -1
  63. package/dist/query.cjs +2 -2
  64. package/dist/query.js +2 -2
  65. package/dist/{refResolveFns-DNnNqpyn.cjs → refResolveFns-ROMmhWfH.cjs} +6 -6
  66. package/dist/{refResolveFns-CpPBZkjT.js → refResolveFns-Yy1n2BHJ.js} +6 -6
  67. package/dist/{shapeFns-D6-l0Nwn.js → shapeFns-BRRrZc1X.js} +2 -2
  68. package/dist/{shapeFns-Dr_0yhYg.cjs → shapeFns-CljM3iMI.cjs} +2 -2
  69. package/dist/shapeRef.cjs +1 -1
  70. package/dist/shapeRef.js +1 -1
  71. package/dist/{shapeTypes-B23U9msC.cjs → shapeTypes-DEe6gtTR.cjs} +460 -89
  72. package/dist/{shapeTypes-CSWkqhe9.js → shapeTypes-w2wyXEis.js} +460 -89
  73. package/dist/sketching.cjs +3 -3
  74. package/dist/sketching.js +3 -3
  75. package/dist/{solidBuilders-CQ1oQdBY.js → solidBuilders-BxIYXIv-.js} +3 -3
  76. package/dist/{solidBuilders-DIlNbnwK.cjs → solidBuilders-yQFZe6jf.cjs} +3 -3
  77. package/dist/{surfaceBuilders-ZW6W8S1a.js → surfaceBuilders-BZ7BW8I0.js} +3 -3
  78. package/dist/{surfaceBuilders-BV-QYvCL.cjs → surfaceBuilders-dWcQ_BpM.cjs} +3 -3
  79. package/dist/text.cjs +2 -2
  80. package/dist/text.js +2 -2
  81. package/dist/{textBlueprints-CwsfEEBS.js → textBlueprints-CxhhPEaF.js} +8 -8
  82. package/dist/{textBlueprints-DYrv0GjE.cjs → textBlueprints-Tx73Xex5.cjs} +8 -8
  83. package/dist/{textMetrics-gC0qYCN1.cjs → textMetrics-9q5S5II9.cjs} +1 -1
  84. package/dist/{textMetrics-B_WRck8_.js → textMetrics-B5dKPalE.js} +1 -1
  85. package/dist/{threadFns-srKixiXB.cjs → threadFns-BnNo5eND.cjs} +7 -7
  86. package/dist/{threadFns-D9m_ywSR.js → threadFns-Dfu2jV41.js} +7 -7
  87. package/dist/topology/apiTypes.d.ts +0 -3
  88. package/dist/topology/modifierFns.d.ts +4 -6
  89. package/dist/topology.cjs +9 -9
  90. package/dist/topology.js +9 -9
  91. package/dist/{topologyQueryFns-D8Du44mm.js → topologyQueryFns-CTl4cWYD.js} +1 -1
  92. package/dist/{topologyQueryFns-DJJoFJPW.cjs → topologyQueryFns-I_m8o-s7.cjs} +1 -1
  93. package/dist/{validityTypes-DSzSU07u.js → validityTypes-C2K0Eepw.js} +1 -1
  94. package/dist/{validityTypes-BkX2hYwP.cjs → validityTypes-DgknRAAp.cjs} +1 -1
  95. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- import { A as UnsupportedKernelOperationError, C as reverseCurve2d$2, D as tangentCurve2d, E as serializeCurve2d$2, N as EXACT_BREP_CAPABILITIES, O as translateCurve2d$2, P as OCCT_CAPABILITIES, S as offsetCurve2d$2, T as scaleCurve2d$2, _ as makeCircle2d$2, a as buildAsciiSTL, b as mirrorAcrossAxis, c as createBBox2d, d as deserializeCurve2d$2, f as evaluateCurve2d$2, g as makeBezier2d$2, h as makeArc2dThreePoints, i as DEFAULT_STL_TOLERANCE, j as isUnsupportedKernelOperationError, k as quickHull, l as curveBounds, m as makeArc2dTangent, o as buildBinarySTL, p as intersectCurves2dFn, r as DEFAULT_STL_ANGULAR_TOLERANCE, s as addCurveToBBox, t as OcctWasmAdapter, u as curveTypeName, v as makeEllipse2d$2, w as rotateCurve2d$2, x as mirrorAtPoint, y as makeLine2d$2 } from "./occtWasmAdapter-CGsnjMOe.js";
1
+ import { A as UnsupportedKernelOperationError, C as reverseCurve2d$2, D as tangentCurve2d, E as serializeCurve2d$2, N as EXACT_BREP_CAPABILITIES, O as translateCurve2d$2, P as OCCT_CAPABILITIES, S as offsetCurve2d$2, T as scaleCurve2d$2, _ as makeCircle2d$2, a as buildAsciiSTL, b as mirrorAcrossAxis, c as createBBox2d, d as deserializeCurve2d$2, f as evaluateCurve2d$2, g as makeBezier2d$2, h as makeArc2dThreePoints, i as DEFAULT_STL_TOLERANCE, j as isUnsupportedKernelOperationError, k as quickHull, l as curveBounds, m as makeArc2dTangent, o as buildBinarySTL, p as intersectCurves2dFn, r as DEFAULT_STL_ANGULAR_TOLERANCE, s as addCurveToBBox, t as OcctWasmAdapter, u as curveTypeName, v as makeEllipse2d$2, w as rotateCurve2d$2, x as mirrorAtPoint, y as makeLine2d$2 } from "./occtWasmAdapter-CGkZIHyM.js";
2
2
  import { i as wasmIndex, r as vec3At } from "./bug-ClMTAJhe.js";
3
3
  // src/kernel/quality.ts
4
4
  /**
@@ -5401,14 +5401,13 @@ var TAU$1 = 2 * Math.PI;
5401
5401
  /**
5402
5402
  * The frame of an analytic circle or ellipse edge, evaluated at t as
5403
5403
  * center + U·cos t + V·sin t, recovered from its points at 0, π/2 and π.
5404
- * brepkit reports such an edge's domain as exactly [0, 2π]; a NURBS edge that
5405
- * happens to be a circle reports its knot domain and gets no frame.
5404
+ * A NURBS edge that happens to be a circle reports CIRCLE too but runs over
5405
+ * its knots, so it gets no frame; only an analytic edge has no NURBS data.
5406
5406
  */
5407
5407
  function conicFrame(bk, edgeId) {
5408
- const params = bk.getEdgeCurveParameters(edgeId);
5409
- if (wasmIndex(params, 0) !== 0 || wasmIndex(params, 1) !== TAU$1) return void 0;
5410
5408
  const curveType = bk.getEdgeCurveType(edgeId);
5411
5409
  if (curveType !== "CIRCLE" && curveType !== "ELLIPSE") return void 0;
5410
+ if (bk.getEdgeNurbsData(edgeId) !== null) return void 0;
5412
5411
  const at = (t) => {
5413
5412
  const p = bk.evaluateEdgeCurve(edgeId, t);
5414
5413
  return [
@@ -5452,10 +5451,11 @@ function edgeEnds(bk, edgeId) {
5452
5451
  ]];
5453
5452
  }
5454
5453
  /**
5455
- * The curve parameters an edge spans. brepkit reports a circle or ellipse edge's
5456
- * domain as the whole [0, 2π] even for an arc; the arc runs counterclockwise
5457
- * about the curve's axis from the angle of its start vertex to that of its end
5458
- * vertex (brepkit's own `circle_param_range`), angles read off the curve's frame.
5454
+ * The curve parameters an edge spans. brepkit before 4.5.10 reports a circle or
5455
+ * ellipse edge's domain as the whole [0, 2π] even for an arc, so an arc's span
5456
+ * is read off the curve's frame on every kernel: it runs counterclockwise about
5457
+ * the curve's axis from the angle of its start vertex to that of its end vertex
5458
+ * (brepkit's own `circle_param_range`).
5459
5459
  */
5460
5460
  function edgeParamRange(bk, edgeId) {
5461
5461
  const params = bk.getEdgeCurveParameters(edgeId);
@@ -5717,6 +5717,35 @@ function nextSyntheticId() {
5717
5717
  }
5718
5718
  /** JS-side storage for compound children (wires, faces, edges). */
5719
5719
  var syntheticCompounds = /* @__PURE__ */ new Map();
5720
+ /**
5721
+ * Each solid's piece ids, per kernel. getSolidPieces builds new solids and
5722
+ * shells for a solid in several pieces on every call, and the arena frees
5723
+ * nothing one handle at a time, so a solid is split once and its pieces keep
5724
+ * one identity until the solid changes in place or the arena rolls back.
5725
+ */
5726
+ var solidPiecesByKernel = /* @__PURE__ */ new WeakMap();
5727
+ /** A solid's piece ids, from `split` the first time they are asked for. */
5728
+ function cachedSolidPieces(bk, solidId, split) {
5729
+ let known = solidPiecesByKernel.get(bk);
5730
+ if (!known) {
5731
+ known = /* @__PURE__ */ new Map();
5732
+ solidPiecesByKernel.set(bk, known);
5733
+ }
5734
+ const cached = known.get(solidId);
5735
+ if (cached) return cached;
5736
+ const pieces = split();
5737
+ known.set(solidId, pieces);
5738
+ for (const piece of pieces) if (!known.has(piece)) known.set(piece, [piece]);
5739
+ return pieces;
5740
+ }
5741
+ /**
5742
+ * Forget one solid's pieces after the kernel changes it in place, or every
5743
+ * solid's after the arena rolls back and its ids can name new entities.
5744
+ */
5745
+ function forgetSolidPieces(bk, solidId) {
5746
+ if (solidId === void 0) solidPiecesByKernel.delete(bk);
5747
+ else solidPiecesByKernel.get(bk)?.delete(solidId);
5748
+ }
5720
5749
  var _warned = /* @__PURE__ */ new Set();
5721
5750
  /** Emit a console.warn once per key per session. */
5722
5751
  function warnOnce(key, message) {
@@ -5764,17 +5793,35 @@ function resolveUniformAngle(faces, angleDeg) {
5764
5793
  return uniform;
5765
5794
  }
5766
5795
  // src/kernel/brepkit/topologyOps.ts
5796
+ /**
5797
+ * One solid per separate piece of material in a solid: a boolean can leave
5798
+ * several pieces in one kernel solid, all in one shell. A solid in one piece
5799
+ * keeps its own handle, since the kernel answers a solid whose cavity shares
5800
+ * its shell with a rebuilt one.
5801
+ */
5802
+ function solidPieces(bk, solid) {
5803
+ if (typeof bk.getSolidPieces !== "function") return [solid];
5804
+ const id = unwrap$1(solid, "solid");
5805
+ const pieces = cachedSolidPieces(bk, id, () => {
5806
+ const found = toArray(bk.getSolidPieces(id));
5807
+ return found.length > 1 ? found : [id];
5808
+ });
5809
+ return pieces.length > 1 ? pieces.map(solidHandle) : [solid];
5810
+ }
5767
5811
  function iterCompound(bk, h, type) {
5768
5812
  const children = syntheticCompounds.get(h);
5769
- if (children) return children.flatMap((child) => child.type === type ? [child] : iterShapes$1(bk, child, type));
5770
- if (type === "solid") return toArray(bk.getCompoundSolids(h)).map(solidHandle);
5813
+ if (children) return children.flatMap((child) => {
5814
+ if (child.type === "solid" && type === "solid") return solidPieces(bk, child);
5815
+ return child.type === type ? [child] : iterShapes$1(bk, child, type);
5816
+ });
5817
+ if (type === "solid") return toArray(bk.getCompoundSolids(h)).flatMap((id) => solidPieces(bk, solidHandle(id)));
5771
5818
  if (type === "face" || type === "edge" || type === "vertex" || type === "wire") return toArray(bk.getCompoundSolids(h)).map(solidHandle).flatMap((s) => iterShapes$1(bk, s, type));
5772
5819
  return [];
5773
5820
  }
5774
5821
  function iterSolid(bk, shape, h, type) {
5775
5822
  switch (type) {
5776
- case "solid": return [shape];
5777
- case "shell": return toArray(bk.getSolidShells(h)).map(shellHandle);
5823
+ case "solid": return solidPieces(bk, shape);
5824
+ case "shell": return solidPieces(bk, shape).flatMap((piece) => toArray(bk.getSolidShells(unwrap$1(piece, "solid"))).map(shellHandle));
5778
5825
  case "face": return toArray(bk.getSolidFaces(h)).map(faceHandle);
5779
5826
  case "edge": return toArray(bk.getSolidEdges(h)).map(edgeHandle);
5780
5827
  case "vertex": return toArray(bk.getSolidVertices(h)).map(vertexHandle);
@@ -5910,12 +5957,23 @@ function faceIdsForSew(bk, shape) {
5910
5957
  function sew(bk, shapes, tolerance) {
5911
5958
  const faceIds = shapes.flatMap((s) => faceIdsForSew(bk, s));
5912
5959
  const tol = tolerance ?? 1e-7;
5960
+ let solidId;
5913
5961
  try {
5914
- return shellHandle(bk.weldShellsAndFaces(faceIds, tol));
5962
+ solidId = bk.weldShellsAndFaces(faceIds, tol);
5915
5963
  } catch (e) {
5916
5964
  console.warn("brepkit: weldShellsAndFaces failed, falling back to sewFaces:", e);
5965
+ solidId = bk.sewFaces(faceIds, tol);
5917
5966
  }
5918
- return shellHandle(bk.sewFaces(faceIds, tol));
5967
+ return sewnShell(bk, solidId);
5968
+ }
5969
+ /**
5970
+ * Both sewing bindings wrap the one shell they sew in a solid and return the
5971
+ * solid's handle; the shell has its own, which shell queries need.
5972
+ */
5973
+ function sewnShell(bk, solidId) {
5974
+ const [shellId] = toArray(bk.getSolidShells(solidId));
5975
+ if (shellId === void 0) throw new Error("brepkit: sewing returned a solid with no shell");
5976
+ return shellHandle(shellId);
5919
5977
  }
5920
5978
  /** Co-located factory: returns the topology-iteration slice of {@link KernelAdapter} bound to `bk`. */
5921
5979
  function makeTopologyOps$1(bk) {
@@ -6174,33 +6232,76 @@ function recognizeFeatures(bk, shape, tolerance) {
6174
6232
  const solidId = unwrapSolidOrThrow(shape, "recognizeFeatures");
6175
6233
  return bk.recognizeFeatures(solidId, tolerance);
6176
6234
  }
6177
- function projectEdges(bk, shape, cameraOrigin, cameraDirection, cameraXAxis) {
6178
- const solidId = unwrap$1(shape);
6179
- const [ox, oy, oz] = cameraOrigin;
6180
- const [dx, dy, dz] = cameraDirection;
6181
- const [xx, xy, xz] = cameraXAxis ?? [
6182
- 1,
6183
- 0,
6184
- 0
6235
+ /** The solids whose edges a projection draws: a solid itself, or every solid of a compound. */
6236
+ function projectedSolidIds(bk, shape) {
6237
+ const h = shape;
6238
+ if (h.type === "solid") return [h.id];
6239
+ const ids = h.type === "compound" ? iterShapes$1(bk, shape, "solid").map((s) => unwrap$1(s)) : [];
6240
+ if (ids.length === 0) throw new Error(`brepkit: projectEdges needs a solid or a compound of solids, got ${h.type}`);
6241
+ return ids;
6242
+ }
6243
+ /**
6244
+ * brepkit returns polyline approximations of the projection, so tie the
6245
+ * tessellation deflection to model scale (1/1000 of the bbox diagonal) to
6246
+ * stay resolution-independent across part sizes.
6247
+ */
6248
+ function projectionDeflection(bk, solidIds) {
6249
+ const min = [
6250
+ Infinity,
6251
+ Infinity,
6252
+ Infinity
6253
+ ];
6254
+ const max = [
6255
+ -Infinity,
6256
+ -Infinity,
6257
+ -Infinity
6185
6258
  ];
6186
- let deflection = .1;
6187
6259
  try {
6188
- const bb = bk.boundingBox(solidId);
6189
- const [minX, minY, minZ] = vec3At(bb, 0);
6190
- const [maxX, maxY, maxZ] = vec3At(bb, 3);
6191
- const diag = dist3(minX, minY, minZ, maxX, maxY, maxZ);
6192
- if (diag > 0) deflection = Math.max(diag * .001, 1e-6);
6193
- } catch {}
6194
- const json = bk.projectEdges(solidId, ox, oy, oz, dx, dy, dz, xx, xy, xz, true, deflection);
6195
- let parsed;
6260
+ for (const id of solidIds) {
6261
+ const bb = bk.boundingBox(id);
6262
+ for (let k = 0; k < 3; k++) {
6263
+ min[k] = Math.min(wasmIndex(min, k), wasmIndex(bb, k));
6264
+ max[k] = Math.max(wasmIndex(max, k), wasmIndex(bb, k + 3));
6265
+ }
6266
+ }
6267
+ } catch {
6268
+ return .1;
6269
+ }
6270
+ const diag = dist3(...min, ...max);
6271
+ return diag > 0 ? Math.max(diag * .001, 1e-6) : .1;
6272
+ }
6273
+ function parseProjection(json) {
6196
6274
  try {
6197
- parsed = JSON.parse(json);
6275
+ return JSON.parse(json);
6198
6276
  } catch {
6199
- parsed = {
6277
+ return {
6200
6278
  visible: [],
6201
6279
  hidden: []
6202
6280
  };
6203
6281
  }
6282
+ }
6283
+ /** Each solid's edges, hidden wherever any of the solids lies in front of them. */
6284
+ function projectSolids(bk, solidIds, view) {
6285
+ const deflection = projectionDeflection(bk, solidIds);
6286
+ const [only, ...others] = solidIds;
6287
+ if (only !== void 0 && others.length === 0) return parseProjection(bk.projectEdges(only, ...view, true, deflection));
6288
+ if (typeof bk.projectSolidsEdges === "function") return parseProjection(bk.projectSolidsEdges(solidIds, ...view, true, deflection));
6289
+ const parts = solidIds.map((id) => parseProjection(bk.projectEdges(id, ...view, true, deflection)));
6290
+ return {
6291
+ visible: parts.flatMap((p) => p.visible),
6292
+ hidden: parts.flatMap((p) => p.hidden)
6293
+ };
6294
+ }
6295
+ function projectEdges(bk, shape, cameraOrigin, cameraDirection, cameraXAxis) {
6296
+ const parsed = projectSolids(bk, projectedSolidIds(bk, shape), [
6297
+ ...cameraOrigin,
6298
+ ...cameraDirection,
6299
+ ...cameraXAxis ?? [
6300
+ 1,
6301
+ 0,
6302
+ 0
6303
+ ]
6304
+ ]);
6204
6305
  const polylinesToCompound = (polylines) => {
6205
6306
  const edges = [];
6206
6307
  for (const poly of polylines) for (let i = 0; i + 3 < poly.length; i += 2) {
@@ -6592,6 +6693,7 @@ function isValidStrict(bk, shape) {
6592
6693
  function healSolid(bk, shape) {
6593
6694
  const h = shape;
6594
6695
  if (h.type !== "solid") throw new Error(`brepkit: healSolid requires a solid, got ${h.type}. Consider using makeCompound() to combine shapes first.`);
6696
+ forgetSolidPieces(bk, h.id);
6595
6697
  try {
6596
6698
  const remaining = bk.repairSolid(unwrap$1(shape));
6597
6699
  if (remaining > 0) console.warn(`brepkit: repairSolid left ${remaining} error(s) on solid.`);
@@ -6614,19 +6716,25 @@ function healWire(_bk, wire, _face) {
6614
6716
  }
6615
6717
  function mergeCoincidentVertices(bk, shape, tolerance) {
6616
6718
  const solidId = unwrapSolidOrThrow(shape, "mergeCoincidentVertices");
6719
+ forgetSolidPieces(bk, solidId);
6617
6720
  return bk.mergeCoincidentVertices(solidId, tolerance);
6618
6721
  }
6619
6722
  function removeDegenerateEdges(bk, shape, tolerance) {
6620
6723
  const solidId = unwrapSolidOrThrow(shape, "removeDegenerateEdges");
6724
+ forgetSolidPieces(bk, solidId);
6621
6725
  return bk.removeDegenerateEdges(solidId, tolerance);
6622
6726
  }
6623
6727
  function fixFaceOrientations(bk, shape) {
6624
6728
  const solidId = unwrapSolidOrThrow(shape, "fixFaceOrientations");
6729
+ forgetSolidPieces(bk, solidId);
6625
6730
  return bk.fixFaceOrientations(solidId);
6626
6731
  }
6627
6732
  function fixShape(bk, shape) {
6628
6733
  const h = shape;
6629
- if (h.type === "solid") bk.healSolid(h.id);
6734
+ if (h.type === "solid") {
6735
+ forgetSolidPieces(bk, h.id);
6736
+ bk.healSolid(h.id);
6737
+ }
6630
6738
  return shape;
6631
6739
  }
6632
6740
  function fixSelfIntersection(_bk, wire) {
@@ -6973,7 +7081,7 @@ function makeWire(bk, edges) {
6973
7081
  if (h.type === "wire") for (const childEdgeId of toArray(bk.getWireEdges(h.id))) edgeIds.push(childEdgeId);
6974
7082
  else edgeIds.push(unwrap$1(e, "edge"));
6975
7083
  }
6976
- return wireHandle(bk.makeWire(edgeIds, true));
7084
+ return wireHandle(bk.makeWire(chainEdges$1(bk, edgeIds), true));
6977
7085
  }
6978
7086
  function makeFace(bk, wire, planar) {
6979
7087
  const h = wire;
@@ -7170,21 +7278,85 @@ function makeBezierEdge(bk, points) {
7170
7278
  function makeTangentArc(bk, startPoint, startTangent, endPoint) {
7171
7279
  return edgeHandle(bk.makeTangentArc3d(startPoint[0], startPoint[1], startPoint[2], startTangent[0], startTangent[1], startTangent[2], endPoint[0], endPoint[1], endPoint[2]));
7172
7280
  }
7173
- function makeHelixWire(bk, pitch, height, radius, center, _direction, leftHanded) {
7281
+ /** The X axis occt's `gp_Ax3(P, V)` picks when given only a direction, so a helix starts where occt-wasm's does. */
7282
+ function occtDefaultXDir(a, b, c) {
7283
+ const [aa, ba, ca] = [
7284
+ Math.abs(a),
7285
+ Math.abs(b),
7286
+ Math.abs(c)
7287
+ ];
7288
+ let x;
7289
+ if (ba <= aa && ba <= ca) x = aa > ca ? [
7290
+ -c,
7291
+ 0,
7292
+ a
7293
+ ] : [
7294
+ c,
7295
+ 0,
7296
+ -a
7297
+ ];
7298
+ else if (aa <= ba && aa <= ca) x = ba > ca ? [
7299
+ 0,
7300
+ -c,
7301
+ b
7302
+ ] : [
7303
+ 0,
7304
+ c,
7305
+ -b
7306
+ ];
7307
+ else x = aa > ba ? [
7308
+ -b,
7309
+ a,
7310
+ 0
7311
+ ] : [
7312
+ b,
7313
+ -a,
7314
+ 0
7315
+ ];
7316
+ const len = Math.hypot(x[0], x[1], x[2]);
7317
+ return [
7318
+ x[0] / len,
7319
+ x[1] / len,
7320
+ x[2] / len
7321
+ ];
7322
+ }
7323
+ function makeHelixWire(bk, pitch, height, radius, center, direction, leftHanded) {
7174
7324
  const turns = height / pitch;
7175
7325
  const nSamples = Math.max(4, Math.ceil(turns * 16));
7176
- const cx = center?.[0] ?? 0;
7177
- const cy = center?.[1] ?? 0;
7178
- const cz = center?.[2] ?? 0;
7326
+ const [cx, cy, cz] = center ?? [
7327
+ 0,
7328
+ 0,
7329
+ 0
7330
+ ];
7331
+ const [dx, dy, dz] = direction ?? [
7332
+ 0,
7333
+ 0,
7334
+ 1
7335
+ ];
7336
+ const dLen = Math.hypot(dx, dy, dz);
7337
+ const z = [
7338
+ dx / dLen,
7339
+ dy / dLen,
7340
+ dz / dLen
7341
+ ];
7342
+ const x = occtDefaultXDir(z[0], z[1], z[2]);
7343
+ const y = [
7344
+ z[1] * x[2] - z[2] * x[1],
7345
+ z[2] * x[0] - z[0] * x[2],
7346
+ z[0] * x[1] - z[1] * x[0]
7347
+ ];
7179
7348
  const sign = leftHanded ? -1 : 1;
7180
7349
  const points = [];
7181
7350
  for (let i = 0; i <= nSamples; i++) {
7182
7351
  const t = i / nSamples;
7183
7352
  const angle = sign * 2 * Math.PI * turns * t;
7353
+ const u = radius * Math.cos(angle);
7354
+ const v = radius * Math.sin(angle);
7355
+ const h = height * t;
7184
7356
  points.push([
7185
- cx + radius * Math.cos(angle),
7186
- cy + radius * Math.sin(angle),
7187
- cz + height * t
7357
+ cx + u * x[0] + v * y[0] + h * z[0],
7358
+ cy + u * x[1] + v * y[1] + h * z[1],
7359
+ cz + u * x[2] + v * y[2] + h * z[2]
7188
7360
  ]);
7189
7361
  }
7190
7362
  return makeWire(bk, [interpolatePoints(bk, points)]);
@@ -7197,10 +7369,110 @@ function makeWireFromMixed(bk, items) {
7197
7369
  else if (h.type === "wire") for (const childEdgeId of toArray(bk.getWireEdges(h.id))) edgeIds.push(childEdgeId);
7198
7370
  }
7199
7371
  if (edgeIds.length === 0) throw new Error("brepkit: makeWireFromMixed requires at least one edge");
7200
- return wireHandle(bk.makeWire(edgeIds, chainCloses(bk, edgeIds)));
7372
+ const chain = chainEdges$1(bk, edgeIds);
7373
+ return wireHandle(bk.makeWire(chain, chainCloses(bk, chain)));
7201
7374
  }
7202
7375
  /** Matches the 1e-7 linear tolerance brepkit's makeWire welds vertices within. */
7203
7376
  var WIRE_CLOSE_TOLERANCE = 1e-7;
7377
+ /**
7378
+ * The edges in an order where each starts where the one before ends, any edge
7379
+ * running the other way rebuilt to run on. brepkit's makeWire takes each edge
7380
+ * as it runs and a face refuses a wire whose edges do not run end to end, so a
7381
+ * profile closed by its mirror image, whose edges leave from the first half's
7382
+ * start, needs that half turned round. Edges that make no single chain, or
7383
+ * that cannot be rebuilt, come back as given.
7384
+ */
7385
+ function chainEdges$1(bk, edgeIds) {
7386
+ const [first, ...left] = edgeIds;
7387
+ if (first === void 0 || left.length === 0) return edgeIds;
7388
+ const ends = new Map(left.map((id) => [id, edgeEnds(bk, id)]));
7389
+ let [head, tail] = edgeEnds(bk, first);
7390
+ const chain = [first];
7391
+ while (left.length > 0) {
7392
+ const after = takeLink(bk, left, ends, tail, "start");
7393
+ if (after) {
7394
+ chain.push(after.id);
7395
+ tail = after.free;
7396
+ continue;
7397
+ }
7398
+ const before = takeLink(bk, left, ends, head, "end");
7399
+ if (!before) return edgeIds;
7400
+ chain.unshift(before.id);
7401
+ head = before.free;
7402
+ }
7403
+ return chain;
7404
+ }
7405
+ /**
7406
+ * Take from `left` the first edge with an end at `at`, to join the chain there
7407
+ * by its `side` end; an edge that meets `at` with its other end is rebuilt to
7408
+ * run the other way. Returns the joining edge and the end it leaves free.
7409
+ */
7410
+ function takeLink(bk, left, ends, at, side) {
7411
+ const touches = (p) => dist3(...p, ...at) < WIRE_CLOSE_TOLERANCE;
7412
+ for (const [i, id] of left.entries()) {
7413
+ const edge = ends.get(id);
7414
+ if (!edge) continue;
7415
+ const [start, end] = edge;
7416
+ const [joins, free] = side === "start" ? [start, end] : [end, start];
7417
+ if (touches(joins)) {
7418
+ left.splice(i, 1);
7419
+ return {
7420
+ id,
7421
+ free
7422
+ };
7423
+ }
7424
+ if (touches(free)) {
7425
+ const turned = reversedEdge(bk, id, start, end);
7426
+ if (turned === void 0) return void 0;
7427
+ left.splice(i, 1);
7428
+ return {
7429
+ id: turned,
7430
+ free: joins
7431
+ };
7432
+ }
7433
+ }
7434
+ }
7435
+ /**
7436
+ * A new edge on the same curve, running from `end` to `start`, or `undefined`
7437
+ * for a curve the adapter cannot rebuild.
7438
+ */
7439
+ function reversedEdge(bk, id, start, end) {
7440
+ const nurbs = bk.getEdgeNurbsData(id);
7441
+ if (nurbs !== null) return reversedNurbsEdge(bk, nurbs, start, end);
7442
+ const curveType = bk.getEdgeCurveType(id);
7443
+ if (curveType === "LINE") return bk.makeLineEdge(...end, ...start);
7444
+ const frame = conicFrame(bk, id);
7445
+ if (!frame) return void 0;
7446
+ const { center, u, v } = frame;
7447
+ const n = [
7448
+ v[1] * u[2] - v[2] * u[1],
7449
+ v[2] * u[0] - v[0] * u[2],
7450
+ v[0] * u[1] - v[1] * u[0]
7451
+ ];
7452
+ const nLen = Math.hypot(...n);
7453
+ const axis = [
7454
+ n[0] / nLen,
7455
+ n[1] / nLen,
7456
+ n[2] / nLen
7457
+ ];
7458
+ if (curveType === "CIRCLE") return bk.makeCircleArc3d(...end, ...start, ...center, ...axis);
7459
+ const major = Math.hypot(...u);
7460
+ const ref = [
7461
+ u[0] / major,
7462
+ u[1] / major,
7463
+ u[2] / major
7464
+ ];
7465
+ return bk.makeEllipseArc3d(...end, ...start, ...center, ...axis, ...ref, major, Math.hypot(...v));
7466
+ }
7467
+ function reversedNurbsEdge(bk, json, start, end) {
7468
+ const data = JSON.parse(json);
7469
+ const lo = wasmIndex(data.knots, 0);
7470
+ const hi = wasmIndex(data.knots, data.knots.length - 1);
7471
+ const knots = data.knots.map((k) => lo + hi - k).reverse();
7472
+ const poles = [];
7473
+ for (let i = data.controlPoints.length - 3; i >= 0; i -= 3) poles.push(...data.controlPoints.slice(i, i + 3));
7474
+ return bk.makeNurbsEdge(...end, ...start, data.degree, knots, poles, [...data.weights].reverse());
7475
+ }
7204
7476
  function chainCloses(bk, edgeIds) {
7205
7477
  const first = bk.getEdgeVertices(wasmIndex(edgeIds, 0));
7206
7478
  const last = bk.getEdgeVertices(wasmIndex(edgeIds, edgeIds.length - 1));
@@ -7239,15 +7511,7 @@ function makeBoxFromCorners(bk, p1, p2) {
7239
7511
  return box;
7240
7512
  }
7241
7513
  function solidFromShell(bk, shell) {
7242
- const h = shell;
7243
- if (h.type === "solid") return shell;
7244
- if (h.type === "shell") {
7245
- try {
7246
- bk.getSolidFaces(h.id);
7247
- return solidHandle(h.id);
7248
- } catch {}
7249
- return solidHandle(bk.solidFromShell(h.id));
7250
- }
7514
+ if (shell.type === "solid") return shell;
7251
7515
  return solidHandle(bk.solidFromShell(unwrap$1(shell, "shell")));
7252
7516
  }
7253
7517
  function makeNonPlanarFace(bk, wire) {
@@ -7734,45 +7998,80 @@ function fillet(bk, shape, edges, radius) {
7734
7998
  const solidId = unwrapSolidOrThrow(shape, "fillet");
7735
7999
  const edgeIds = edges.map((e) => unwrap$1(e, "edge"));
7736
8000
  if (typeof radius === "number") return solidHandle(bk.fillet(solidId, edgeIds, radius));
7737
- const radii = /* @__PURE__ */ new Set();
7738
- for (const edge of edges) {
8001
+ const laws = edges.map((edge, i) => {
7739
8002
  const r = typeof radius === "function" ? radius(edge) : radius;
7740
8003
  const [start, end] = Array.isArray(r) ? r : [r, r];
7741
- if (start !== end) throw varyingFilletUnsupported();
7742
- radii.add(start);
8004
+ return {
8005
+ edge: wasmIndex(edgeIds, i),
8006
+ start,
8007
+ end
8008
+ };
8009
+ });
8010
+ const [first] = laws;
8011
+ if (!first) throw new Error("brepkit: fillet needs at least one edge");
8012
+ if (laws.every((l) => l.start === first.start && l.end === first.start)) return solidHandle(bk.fillet(solidId, edgeIds, first.start));
8013
+ return filletAlongLaws(bk, solidId, laws);
8014
+ }
8015
+ var lawProbes = /* @__PURE__ */ new WeakMap();
8016
+ /**
8017
+ * brepkit-wasm before 4.6.9 cuts a flat bevel along a radius law, and the peer
8018
+ * range still admits those builds. Rounding a 10 mm cube edge from 1 to 2 tells
8019
+ * them apart: a round removes 5.0 mm3 and a bevel 11.7. The probe runs once per
8020
+ * kernel, inside a checkpoint it restores and discards, so it leaves nothing behind.
8021
+ */
8022
+ function roundsAlongLaws(bk) {
8023
+ const known = lawProbes.get(bk);
8024
+ if (known !== void 0) return known;
8025
+ let rounds;
8026
+ const mark = bk.checkpoint();
8027
+ try {
8028
+ const cube = bk.makeBox(10, 10, 10);
8029
+ const edge = wasmIndex(bk.getSolidEdges(cube), 0);
8030
+ const spec = JSON.stringify([{
8031
+ edge,
8032
+ law: "linear",
8033
+ start: 1,
8034
+ end: 2
8035
+ }]);
8036
+ rounds = 1e3 - bk.volume(bk.filletVariable(cube, spec), DEFAULT_DEFLECTION) < 8;
8037
+ } catch {
8038
+ rounds = false;
8039
+ } finally {
8040
+ bk.restore(mark);
8041
+ bk.discardCheckpoint(mark);
7743
8042
  }
7744
- if (radii.size > 1) throw perEdgeUnsupported("fillet radii");
7745
- const [only] = radii;
7746
- if (only === void 0) throw new Error("brepkit: fillet needs at least one edge");
7747
- return solidHandle(bk.fillet(solidId, edgeIds, only));
8043
+ lawProbes.set(bk, rounds);
8044
+ return rounds;
7748
8045
  }
7749
8046
  /**
7750
- * brepkit takes one value for every edge of a fillet or chamfer call, and a
7751
- * second call on the first one's result cannot name the input's other edges:
7752
- * a chamfer fails on them ("no manifold edges to chamfer"), and a fillet builds
7753
- * the right body but leaves the input solid corrupt, so that filleting the
7754
- * input's four vertical cube edges at 2 mm afterwards cuts a flat chamfer.
8047
+ * One filletVariable call for every edge, each along its own constant or
8048
+ * linear law, running from the edge's start vertex to its end.
7755
8049
  */
7756
- function perEdgeUnsupported(what) {
7757
- return new UnsupportedKernelOperationError(`brepkit: different ${what} on different edges in one call is not supported`);
8050
+ function filletAlongLaws(bk, solidId, laws) {
8051
+ if (!roundsAlongLaws(bk)) throw new UnsupportedKernelOperationError("brepkit: a fillet along a radius law needs brepkit-wasm 4.6.9 or newer");
8052
+ const spec = laws.map(({ edge, start, end }) => ({
8053
+ edge,
8054
+ law: start === end ? "constant" : "linear",
8055
+ start,
8056
+ end
8057
+ }));
8058
+ return solidHandle(bk.filletVariable(solidId, JSON.stringify(spec)));
7758
8059
  }
7759
8060
  /**
7760
- * brepkit-wasm's filletVariable does not build a rolling-ball blend: on the
7761
- * 40 mm top edge of a 40 x 30 x 20 box, a radius running 2 to 5 removes 260 mm3
7762
- * where the blend removes about 113, and even a constant law of 3 removes 180
7763
- * (the flat chamfer) where fillet removes 77.26. A radius that varies along an
7764
- * edge is refused rather than built that way.
8061
+ * brepkit takes one distance for every edge of a chamfer call, and a second
8062
+ * call on the first one's result cannot name the input's other edges: it fails
8063
+ * on them ("no manifold edges to chamfer").
7765
8064
  */
7766
- function varyingFilletUnsupported() {
7767
- return new UnsupportedKernelOperationError("brepkit: a fillet radius that varies along an edge is not supported");
8065
+ function perEdgeUnsupported(what) {
8066
+ return new UnsupportedKernelOperationError(`brepkit: different ${what} on different edges in one call is not supported`);
7768
8067
  }
7769
8068
  /** occt's hashCode upper bound, which the kernel-agnostic filletVariable spec hashes edges with. */
7770
8069
  var HASH_UPPER_BOUND = 2147483647;
7771
8070
  /**
7772
8071
  * A variable fillet spec on one edge: `{ edge: hashCode, radii: [{ param, radius }] }`
7773
- * from variableFillet, or `{ edgeId, startRadius, endRadius }`. A constant radius is
7774
- * an ordinary fillet; a radius that varies is unsupported (see
7775
- * {@link varyingFilletUnsupported}).
8072
+ * from variableFillet, or `{ edgeId, startRadius, endRadius }`. Like occt-wasm, a
8073
+ * profile runs linearly from its first radius to its last, so one of more than two
8074
+ * points is unsupported rather than flattened to a line.
7776
8075
  */
7777
8076
  function filletVariable$1(bk, shape, spec) {
7778
8077
  const solidId = unwrapSolidOrThrow(shape, "filletVariable");
@@ -7788,9 +8087,15 @@ function filletVariable$1(bk, shape, spec) {
7788
8087
  radii = [parsed.startRadius, parsed.endRadius];
7789
8088
  } else throw new UnsupportedKernelOperationError("brepkit: filletVariable: unrecognized spec");
7790
8089
  if (edgeId === void 0) throw new Error("brepkit: filletVariable: the edge is not an edge of the shape");
7791
- const [first = 0] = radii;
7792
- if (radii.some((r) => r !== first)) throw varyingFilletUnsupported();
7793
- return solidHandle(bk.fillet(solidId, [edgeId], first));
8090
+ if (radii.length > 2) throw new UnsupportedKernelOperationError("brepkit: filletVariable supports only a linear (<=2-point) radius profile");
8091
+ const start = wasmIndex(radii, 0);
8092
+ const end = wasmIndex(radii, radii.length - 1);
8093
+ if (start === end) return solidHandle(bk.fillet(solidId, [edgeId], start));
8094
+ return filletAlongLaws(bk, solidId, [{
8095
+ edge: edgeId,
8096
+ start,
8097
+ end
8098
+ }]);
7794
8099
  }
7795
8100
  /**
7796
8101
  * A symmetric chamfer for two equal distances, brepkit's two-distance chamfer
@@ -7941,10 +8246,13 @@ function offsetWire2D(bk, wire, offsetVal, joinType) {
7941
8246
  return wireHandle(bk.makePolygonWire(coords3d));
7942
8247
  }
7943
8248
  function simplify$1(bk, shape) {
7944
- if (shape.type === "solid") try {
7945
- bk.healSolid(unwrap$1(shape));
7946
- } catch (e) {
7947
- console.warn("brepkit: healing failed in simplify:", e);
8249
+ if (shape.type === "solid") {
8250
+ forgetSolidPieces(bk, unwrap$1(shape));
8251
+ try {
8252
+ bk.healSolid(unwrap$1(shape));
8253
+ } catch (e) {
8254
+ console.warn("brepkit: healing failed in simplify:", e);
8255
+ }
7948
8256
  }
7949
8257
  return shape;
7950
8258
  }
@@ -10190,7 +10498,7 @@ function area$1(bk, shape) {
10190
10498
  const h = shape;
10191
10499
  if (h.type === "face") return bk.faceArea(unwrap$1(shape), DEFAULT_DEFLECTION);
10192
10500
  if (h.type === "solid") return bk.surfaceArea(unwrap$1(shape), DEFAULT_DEFLECTION);
10193
- if (h.type === "compound") {
10501
+ if (h.type === "compound" || h.type === "shell") {
10194
10502
  const faces = iterShapes$1(bk, shape, "face");
10195
10503
  let total = 0;
10196
10504
  for (const face of faces) total += bk.faceArea(unwrap$1(face), DEFAULT_DEFLECTION);
@@ -10764,12 +11072,65 @@ function trySmoothPipeShell(bk, faceId, spine) {
10764
11072
  return;
10765
11073
  }
10766
11074
  }
11075
+ /**
11076
+ * The curve a guided sweep runs along: one line, or one edge brepkit stores
11077
+ * as NURBS. brepkit gives no NURBS for a circle or an ellipse, and reading one
11078
+ * by its end points would sweep along the chord.
11079
+ */
11080
+ function guideCurve(bk, shape, role) {
11081
+ const edges = shape.type === "edge" ? [shape] : iterShapes$1(bk, shape, "edge");
11082
+ const [edge] = edges;
11083
+ if (edges.length !== 1 || !edge) throw new UnsupportedKernelOperationError(`brepkit: a guided sweep takes a single-edge ${role}, got ${edges.length} edges`);
11084
+ const edgeId = unwrap$1(edge, "edge");
11085
+ const curveType = bk.getEdgeCurveType(edgeId);
11086
+ if (curveType !== "LINE" && bk.getEdgeNurbsData(edgeId) === null) throw new UnsupportedKernelOperationError(`brepkit: a guided sweep takes a line or NURBS ${role}, got ${curveType}`);
11087
+ const data = extractNurbsFromEdge(bk, edge);
11088
+ if (!data) throw new Error(`brepkit: guided sweep could not read the ${role}`);
11089
+ return data;
11090
+ }
11091
+ /**
11092
+ * brepkit places a guided sweep's sections at twice the spine's control points
11093
+ * (at least 4) and rules flat between them, so a straight spine twists through
11094
+ * 4 segments whatever the guide does: a square turned 90° comes out 2.5% light.
11095
+ * Splitting a straight spine into twice as many collinear spans as the guide
11096
+ * has control points samples the twist more finely than the guide is drawn.
11097
+ */
11098
+ function densifyStraightSpine(path, spans) {
11099
+ if (path.degree !== 1 || path.controlPoints.length !== 6 || spans <= 1) return path;
11100
+ const start = vec3At(path.controlPoints, 0);
11101
+ const end = vec3At(path.controlPoints, 3);
11102
+ const controlPoints = [];
11103
+ const interior = [];
11104
+ for (let i = 0; i <= spans; i++) {
11105
+ const t = i / spans;
11106
+ controlPoints.push(...start.map((s, k) => s + (wasmIndex(end, k) - s) * t));
11107
+ if (i > 0 && i < spans) interior.push(t);
11108
+ }
11109
+ return {
11110
+ degree: 1,
11111
+ knots: [
11112
+ 0,
11113
+ 0,
11114
+ ...interior,
11115
+ 1,
11116
+ 1
11117
+ ],
11118
+ controlPoints,
11119
+ weights: new Array(spans + 1).fill(1)
11120
+ };
11121
+ }
11122
+ function guidedPipeShell(bk, faceId, spine, auxiliary) {
11123
+ const aux = guideCurve(bk, auxiliary, "auxiliary spine");
11124
+ const path = densifyStraightSpine(guideCurve(bk, spine, "spine"), 2 * (aux.controlPoints.length / 3));
11125
+ return solidHandle(bk.guidedSweep(faceId, path.degree, path.knots, path.controlPoints, path.weights, aux.degree, aux.knots, aux.controlPoints, aux.weights));
11126
+ }
10767
11127
  function sweepPipeShell(bk, profile, spine, options) {
10768
- if (options?.["auxiliary"] !== void 0) throw new UnsupportedKernelOperationError("brepkit: sweepPipeShell has no auxiliary spine (twist or guided sweep)");
10769
11128
  if (options?.["law"] !== void 0) throw new UnsupportedKernelOperationError("brepkit: sweepPipeShell has no scaling law");
10770
11129
  const profileHandle = profile;
10771
11130
  const faceId = profileHandle.type === "wire" ? bk.makeFaceFromWire(profileHandle.id) : unwrap$1(profile, "face");
10772
11131
  const shellMode = !!(options && options["shellMode"]);
11132
+ const auxiliary = options?.["auxiliary"];
11133
+ if (auxiliary !== void 0) return wrapPipeShellResult(guidedPipeShell(bk, faceId, spine, auxiliary), profile, shellMode);
10773
11134
  const transitionMode = options?.["transitionMode"];
10774
11135
  const contactMode = transitionMode ? mapStringTransitionMode(transitionMode) : void 0;
10775
11136
  if (contactMode) {
@@ -10867,11 +11228,18 @@ function makeSweepOps$1(bk) {
10867
11228
  function makeCoreOps$1(bk) {
10868
11229
  return {
10869
11230
  dispose: () => {},
10870
- executeBatch: (json) => bk.executeBatch(json),
11231
+ executeBatch: (json) => {
11232
+ try {
11233
+ return bk.executeBatch(json);
11234
+ } finally {
11235
+ forgetSolidPieces(bk);
11236
+ }
11237
+ },
10871
11238
  checkpoint: () => bk.checkpoint(),
10872
11239
  checkpointCount: () => bk.checkpointCount(),
10873
11240
  restoreCheckpoint: (cp) => {
10874
11241
  bk.restore(cp);
11242
+ forgetSolidPieces(bk);
10875
11243
  },
10876
11244
  discardCheckpoint: (cp) => {
10877
11245
  bk.discardCheckpoint(cp);
@@ -17235,7 +17603,10 @@ function createBorrowedHandle(ocShape) {
17235
17603
  * frees *every* slot allocated after the checkpoint, so a live shape handle in
17236
17604
  * the return value would dangle. Use for data-returning batch analysis over many
17237
17605
  * bodies; never to wrap a shape builder. Inputs allocated before the call are
17238
- * untouched (restore only frees from the mark forward).
17606
+ * untouched (restore only frees from the mark forward), but `fn` must not query
17607
+ * their cached topology (`getFaces`, `getSolids`, adjacency): the cache keeps the
17608
+ * sub-shape handles it allocates, and the restore would leave it holding freed
17609
+ * slots.
17239
17610
  *
17240
17611
  * No-op wrapper on kernels without arena checkpoints (occt/manifold): `fn` runs
17241
17612
  * directly and those kernels' own disposal applies.