brepjs-sheetmetal 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -300,7 +300,10 @@ authored path. It reads only the B-rep:
300
300
  other surface type (cone, sphere, spline, …) is reported `UNSUPPORTED_FACE` and skipped (the unfold never
301
301
  silently invents a flat for an unrecognised face).
302
302
  - **Pair flats**: the two large parallel planar faces of a panel, a sheet thickness apart, are the
303
- mid-surface flat. Thickness is detected as the smallest opposed-planar-pair gap.
303
+ mid-surface flat. Two antiparallel faces count as opposed only when they face each other (an interior
304
+ point of one projects inside the other), so a flange's end cap and the adjacent flange's inner face, a
305
+ bend radius apart across a corner, never pair. Thickness is detected as the smallest opposed-planar-pair
306
+ gap.
304
307
  - **Fit bends**: `fitCylinder(face)` recovers a bend's axis + radius + swept angle from sampled points and
305
308
  normals. The axis direction is the common perpendicular of the sampled normals (every cylinder normal is
306
309
  ⟂ the axis), recovered as the sign-aligned average of cross products of non-parallel normal pairs; the
@@ -320,8 +323,9 @@ non-uniform thickness, conical/spline bend faces, non-manifold / non-sheet topol
320
323
  fit a cylinder within tolerance all surface as warnings inside the `Ok` payload (or a clear `Err` when no
321
324
  sheet-metal structure is detected at all, e.g. a bare sphere). The detection is validated by an oracle:
322
325
  author a part, take only its `.solid`, run `unfoldSolid`, and confirm the detected unfold reproduces the
323
- authored unfold's developed area, bend count, flat count, and total flat bbox (single bend, L-bracket,
324
- U-channel), reading only the solid, never the feature tree.
326
+ authored unfold's developed area, bend count, flat count, and total flat bbox with no warnings (single bend,
327
+ plus the L-bracket, U-channel, and four-sided tray at thicknesses 1, 1.5, 2, and 3 against a 2 mm inner
328
+ radius), reading only the solid, never the feature tree.
325
329
 
326
330
  ## Bend tables
327
331
 
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let brepjs = require("brepjs");
3
- //#region src/materials.ts
3
+ // src/materials.ts
4
4
  var DEFAULT_K_FACTOR = .44;
5
5
  function gauge(name, thickness, kFactor = DEFAULT_K_FACTOR) {
6
6
  return {
@@ -38,8 +38,7 @@ var MATERIALS = {
38
38
  function getMaterial(name) {
39
39
  return MATERIALS[name];
40
40
  }
41
- //#endregion
42
- //#region src/bendTableFns.ts
41
+ // src/bendTableFns.ts
43
42
  var DEG_TO_RAD = Math.PI / 180;
44
43
  var registry = /* @__PURE__ */ new Map();
45
44
  /**
@@ -378,8 +377,7 @@ function ensureStarterTables() {
378
377
  });
379
378
  }
380
379
  }
381
- //#endregion
382
- //#region src/allowanceFns.ts
380
+ // src/allowanceFns.ts
383
381
  function validateRule(rule) {
384
382
  if (!Number.isFinite(rule.innerRadius) || rule.innerRadius < 0) return (0, brepjs.err)((0, brepjs.validationError)("INVALID_RADIUS", `innerRadius must be a finite, non-negative number, got ${rule.innerRadius}`));
385
383
  if (!Number.isFinite(rule.kFactor) || rule.kFactor < 0 || rule.kFactor > 1) return (0, brepjs.err)((0, brepjs.validationError)("INVALID_K_FACTOR", `kFactor must be in [0, 1], got ${rule.kFactor}`));
@@ -413,8 +411,7 @@ function neutralRadius(thickness, rule) {
413
411
  if (!ruleCheck.ok) return ruleCheck;
414
412
  return (0, brepjs.ok)(rule.innerRadius + rule.kFactor * thickness);
415
413
  }
416
- //#endregion
417
- //#region src/featureTreeFns.ts
414
+ // src/featureTreeFns.ts
418
415
  var ROOT_FLAT_ID = "root";
419
416
  /**
420
417
  * Build the feature graph for an authored part. Each flange contributes a flat
@@ -559,8 +556,7 @@ function featureTree(part, rootId = ROOT_FLAT_ID) {
559
556
  if (!graph.ok) return graph;
560
557
  return buildFeatureTree(graph.value, rootId);
561
558
  }
562
- //#endregion
563
- //#region src/internal.ts
559
+ // src/internal.ts
564
560
  /**
565
561
  * OCCT boolean fuse returns a compound wrapping the unioned solid. Extract the
566
562
  * single solid so a part's cached shape is a true `Solid` (clean STEP/GLB
@@ -583,8 +579,7 @@ function sideFromAxisDir(axisDir) {
583
579
  if (Math.abs(axisDir[1]) > .5) return "xmax";
584
580
  if (Math.abs(axisDir[0]) > .5) return "ymax";
585
581
  }
586
- //#endregion
587
- //#region src/unfoldFns.ts
582
+ // src/unfoldFns.ts
588
583
  /**
589
584
  * Tree-driven analytic unfold of an arbitrary straight-bend part into its flat
590
585
  * pattern. Walks the feature tree in BFS order placing every flat in the flat
@@ -1291,8 +1286,62 @@ function reportFromLayout(layout) {
1291
1286
  totalFlatSize: [maxX - minX, maxY - minY]
1292
1287
  };
1293
1288
  }
1294
- //#endregion
1295
- //#region src/foreignUnfoldFns.ts
1289
+ // \0@oxc-project+runtime@0.155.0/helpers/esm/usingCtx.js
1290
+ function _usingCtx() {
1291
+ var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
1292
+ var n = Error();
1293
+ return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
1294
+ }, e = {}, n = [];
1295
+ function using(r, e) {
1296
+ if (null != e) {
1297
+ if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
1298
+ if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
1299
+ if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
1300
+ if ("function" != typeof o) throw new TypeError("Object is not disposable.");
1301
+ t && (o = function o() {
1302
+ try {
1303
+ t.call(e);
1304
+ } catch (r) {
1305
+ return Promise.reject(r);
1306
+ }
1307
+ }), n.push({
1308
+ v: e,
1309
+ d: o,
1310
+ a: r
1311
+ });
1312
+ } else r && n.push({
1313
+ d: e,
1314
+ a: r
1315
+ });
1316
+ return e;
1317
+ }
1318
+ return {
1319
+ e,
1320
+ u: using.bind(null, !1),
1321
+ a: using.bind(null, !0),
1322
+ d: function d() {
1323
+ var o, t = this.e, s = 0;
1324
+ function next() {
1325
+ for (; o = n.pop();) try {
1326
+ if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
1327
+ if (o.d) {
1328
+ var r = o.d.call(o.v);
1329
+ if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
1330
+ } else s |= 1;
1331
+ } catch (r) {
1332
+ return err(r);
1333
+ }
1334
+ if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
1335
+ if (t !== e) throw t;
1336
+ }
1337
+ function err(n) {
1338
+ return t = t !== e ? new r(n, t) : n, next();
1339
+ }
1340
+ return next();
1341
+ }
1342
+ };
1343
+ }
1344
+ // src/foreignUnfoldFns.ts
1296
1345
  /** Default neutral-axis K-factor for a purely geometric unfold (mid-surface). */
1297
1346
  var DEFAULT_FOREIGN_K = .5;
1298
1347
  /** Relative tolerance for the cylinder-fit residual (fraction of the radius). */
@@ -1602,10 +1651,11 @@ function unfoldForeignSolid(solid, opts) {
1602
1651
  message: `UNSUPPORTED_FACE: surface type '${t.value}' is not a planar panel or cylindrical bend; the unfold ignores it and may be incomplete`
1603
1652
  });
1604
1653
  }
1605
- const thicknessResult = detectThickness(planarFaces);
1654
+ const planar = planarFaces.map(describePlanar);
1655
+ const thicknessResult = detectThickness(planar);
1606
1656
  if (!thicknessResult.ok) return thicknessResult;
1607
1657
  const thickness = thicknessResult.value;
1608
- const flats = pairFlats(planarFaces, thickness, warnings);
1658
+ const flats = pairFlats(planar, thickness, warnings);
1609
1659
  if (flats.length === 0) return (0, brepjs.err)((0, brepjs.validationError)("NO_FLATS", "no planar panel pairs detected; not a recognizable sheet-metal solid"));
1610
1660
  const layout = layoutForeign(flats, buildBendGraph(flats, pairBends(cylFaces, warnings), warnings), thickness, kFactor, warnings);
1611
1661
  if (!layout.ok) return layout;
@@ -1615,59 +1665,106 @@ function unfoldForeignSolid(solid, opts) {
1615
1665
  warnings
1616
1666
  });
1617
1667
  }
1668
+ function describePlanar(face) {
1669
+ const center = (0, brepjs.faceCenter)(face);
1670
+ const n = 4;
1671
+ const candidates = [center];
1672
+ for (let i = 0; i < n; i += 1) for (let j = 0; j < n; j += 1) candidates.push((0, brepjs.pointOnSurface)(face, (i + .5) / n, (j + .5) / n));
1673
+ const interior = candidates.filter((p) => liesOnFace(face, p));
1674
+ return {
1675
+ face,
1676
+ normal: faceNormal(face),
1677
+ center,
1678
+ interior
1679
+ };
1680
+ }
1681
+ /** True if `point` (already on the face's surface) lies within the face's trimmed boundary. */
1682
+ function liesOnFace(face, point) {
1683
+ try {
1684
+ var _usingCtx$1 = _usingCtx();
1685
+ const probe = _usingCtx$1.u((0, brepjs.vertex)(point));
1686
+ const d = (0, brepjs.measureDistance)(probe, face);
1687
+ return d.ok && d.value < GEOM_TOL;
1688
+ } catch (_) {
1689
+ _usingCtx$1.e = _;
1690
+ } finally {
1691
+ _usingCtx$1.d();
1692
+ }
1693
+ }
1694
+ /** Normal-direction gap between two planar faces, or `null` unless they are antiparallel. */
1695
+ function antiparallelGap(a, b) {
1696
+ if ((0, brepjs.vecDot)(a.normal, b.normal) > -.99) return null;
1697
+ return Math.abs((0, brepjs.vecDot)((0, brepjs.vecSub)(b.center, a.center), a.normal));
1698
+ }
1618
1699
  /**
1619
- * Detect the sheet thickness as the smallest gap between an antiparallel pair of
1620
- * planar faces (the two faces of one flat panel). For a roughly-uniform-thickness
1621
- * part this gap is the same across panels; a spread beyond tolerance warns.
1700
+ * Antiparallel faces are the two sides of one panel only if they face each other:
1701
+ * an interior point of one projects inside the other. The normal-direction gap
1702
+ * alone also matches faces that merely sit on parallel planes, e.g. a flange's end
1703
+ * cap and the adjacent flange's inner face across a corner. Their gap is the bend's
1704
+ * inner radius, which passes for the sheet thickness as T approaches or exceeds R.
1705
+ */
1706
+ function facesOppose(a, b) {
1707
+ return b.interior.some((p) => liesOnFace(a.face, projectToPlane(p, a.normal, a.center))) || a.interior.some((p) => liesOnFace(b.face, projectToPlane(p, b.normal, b.center)));
1708
+ }
1709
+ /**
1710
+ * Detect the sheet thickness as the smallest gap between two antiparallel planar
1711
+ * faces that oppose each other (the two faces of one flat panel).
1622
1712
  */
1623
1713
  function detectThickness(planar) {
1624
- const gaps = [];
1714
+ const pairs = [];
1625
1715
  for (let i = 0; i < planar.length; i += 1) for (let j = i + 1; j < planar.length; j += 1) {
1626
- const fa = planar[i];
1627
- const fb = planar[j];
1628
- if (fa === void 0 || fb === void 0) continue;
1629
- const na = faceNormal(fa);
1630
- const nb = faceNormal(fb);
1631
- if ((0, brepjs.vecDot)(na, nb) > -.99) continue;
1632
- const ca = (0, brepjs.faceCenter)(fa);
1633
- const cb = (0, brepjs.faceCenter)(fb);
1634
- const gap = Math.abs((0, brepjs.vecDot)((0, brepjs.vecSub)(cb, ca), na));
1635
- if (gap > GEOM_TOL) gaps.push(gap);
1716
+ const a = planar[i];
1717
+ const b = planar[j];
1718
+ if (a === void 0 || b === void 0) continue;
1719
+ const gap = antiparallelGap(a, b);
1720
+ if (gap !== null && gap > GEOM_TOL) pairs.push({
1721
+ gap,
1722
+ a,
1723
+ b
1724
+ });
1725
+ }
1726
+ pairs.sort((p, q) => p.gap - q.gap);
1727
+ const thinnest = pairs.find((p) => facesOppose(p.a, p.b));
1728
+ if (thinnest === void 0) return (0, brepjs.err)((0, brepjs.validationError)("NO_THICKNESS", "could not detect a sheet thickness (no opposed planar pair)"));
1729
+ return (0, brepjs.ok)(thinnest.gap);
1730
+ }
1731
+ /** Unused face opposing `a` whose gap is nearest the sheet thickness, within tolerance. */
1732
+ function bestOpposedPartner(planar, i, thickness, used) {
1733
+ const a = planar[i];
1734
+ if (a === void 0) return null;
1735
+ const candidates = [];
1736
+ for (let j = 0; j < planar.length; j += 1) {
1737
+ const b = planar[j];
1738
+ if (j === i || used.has(j) || b === void 0) continue;
1739
+ const gap = antiparallelGap(a, b);
1740
+ if (gap === null) continue;
1741
+ const e = Math.abs(gap - thickness);
1742
+ if (e <= thickness * .5 + GEOM_TOL) candidates.push({
1743
+ j,
1744
+ e
1745
+ });
1636
1746
  }
1637
- if (gaps.length === 0) return (0, brepjs.err)((0, brepjs.validationError)("NO_THICKNESS", "could not detect a sheet thickness (no opposed planar pair)"));
1638
- gaps.sort((a, b) => a - b);
1639
- return (0, brepjs.ok)(gaps[0]);
1747
+ candidates.sort((p, q) => p.e - q.e);
1748
+ const match = candidates.find((c) => {
1749
+ const b = planar[c.j];
1750
+ return b !== void 0 && facesOppose(a, b);
1751
+ });
1752
+ return match === void 0 ? null : match.j;
1640
1753
  }
1641
1754
  /**
1642
- * Pair planar faces into flat panels: each panel is two parallel faces a
1643
- * `thickness` apart with overlapping footprints. Greedy nearest-match by gap.
1755
+ * Pair planar faces into flat panels: each panel is two antiparallel faces a
1756
+ * `thickness` apart that oppose each other. Greedy nearest-match by gap.
1644
1757
  */
1645
1758
  function pairFlats(planar, thickness, warnings) {
1646
1759
  const used = /* @__PURE__ */ new Set();
1647
1760
  const flats = [];
1648
1761
  for (let i = 0; i < planar.length; i += 1) {
1649
1762
  if (used.has(i)) continue;
1650
- const fa = planar[i];
1651
- if (fa === void 0) continue;
1652
- const na = faceNormal(fa);
1653
- const ca = (0, brepjs.faceCenter)(fa);
1654
- let best = -1;
1655
- let bestErr = Infinity;
1656
- for (let j = 0; j < planar.length; j += 1) {
1657
- if (j === i || used.has(j)) continue;
1658
- const fb = planar[j];
1659
- if (fb === void 0) continue;
1660
- const nb = faceNormal(fb);
1661
- if ((0, brepjs.vecDot)(na, nb) > -.99) continue;
1662
- const cb = (0, brepjs.faceCenter)(fb);
1663
- const gap = Math.abs((0, brepjs.vecDot)((0, brepjs.vecSub)(cb, ca), na));
1664
- const e = Math.abs(gap - thickness);
1665
- if (e < bestErr) {
1666
- bestErr = e;
1667
- best = j;
1668
- }
1669
- }
1670
- if (best < 0 || bestErr > thickness * .5 + GEOM_TOL) {
1763
+ const pa = planar[i];
1764
+ if (pa === void 0) continue;
1765
+ const fa = pa.face;
1766
+ const best = bestOpposedPartner(planar, i, thickness, used);
1767
+ if (best === null) {
1671
1768
  const db = (0, brepjs.getBounds)(fa);
1672
1769
  const ext = [
1673
1770
  db.xMax - db.xMin,
@@ -1681,7 +1778,9 @@ function pairFlats(planar, thickness, warnings) {
1681
1778
  });
1682
1779
  continue;
1683
1780
  }
1684
- const fb = planar[best];
1781
+ const pb = planar[best];
1782
+ if (pb === void 0) continue;
1783
+ const fb = pb.face;
1685
1784
  used.add(i);
1686
1785
  used.add(best);
1687
1786
  const areaA = (0, brepjs.measureArea)(fa);
@@ -1704,8 +1803,8 @@ function pairFlats(planar, thickness, warnings) {
1704
1803
  flats.push({
1705
1804
  id: `flat-${flats.length}`,
1706
1805
  faces: [fa, fb],
1707
- normal: na,
1708
- center: (0, brepjs.vecScale)((0, brepjs.vecAdd)(ca, (0, brepjs.faceCenter)(fb)), .5),
1806
+ normal: pa.normal,
1807
+ center: (0, brepjs.vecScale)((0, brepjs.vecAdd)(pa.center, pb.center), .5),
1709
1808
  area
1710
1809
  });
1711
1810
  }
@@ -2181,8 +2280,7 @@ function bendDirection(bend, parent) {
2181
2280
  function faceNormal(face) {
2182
2281
  return (0, brepjs.vecNormalize)((0, brepjs.normalAt)(face, (0, brepjs.faceCenter)(face)));
2183
2282
  }
2184
- //#endregion
2185
- //#region src/authorFns.ts
2283
+ // src/authorFns.ts
2186
2284
  /**
2187
2285
  * Stable edge reference for flange attachment. The base flat is `face-0`; every
2188
2286
  * flange face is `face-<n+1>` in authoring order. The leading edge of a flat is
@@ -2799,8 +2897,7 @@ function buildFlangeFlat(axisOrigin, thetaDeg, width, thickness, length, directi
2799
2897
  ]
2800
2898
  });
2801
2899
  }
2802
- //#endregion
2803
- //#region src/reliefFns.ts
2900
+ // src/reliefFns.ts
2804
2901
  var EPS$6 = 1e-6;
2805
2902
  var Z_AXIS$1 = [
2806
2903
  0,
@@ -3164,8 +3261,7 @@ function toPt2$1(v) {
3164
3261
  function dist2(a, b) {
3165
3262
  return Math.hypot(a[0] - b[0], a[1] - b[1]);
3166
3263
  }
3167
- //#endregion
3168
- //#region src/cutoutFns.ts
3264
+ // src/cutoutFns.ts
3169
3265
  var EPS$5 = 1e-6;
3170
3266
  /** A hair of over-cut so the tool pokes through both faces cleanly. */
3171
3267
  var HAIR$1 = .05;
@@ -3393,8 +3489,7 @@ function polygonArea(loop) {
3393
3489
  }
3394
3490
  return Math.abs(sum) / 2;
3395
3491
  }
3396
- //#endregion
3397
- //#region src/tabFns.ts
3492
+ // src/tabFns.ts
3398
3493
  var EPS$4 = 1e-6;
3399
3494
  /**
3400
3495
  * Fuse a rectangular tab onto a named flat region's edge — additive material (the
@@ -3536,8 +3631,7 @@ function devRect(local, dev) {
3536
3631
  y1
3537
3632
  ];
3538
3633
  }
3539
- //#endregion
3540
- //#region src/formFns.ts
3634
+ // src/formFns.ts
3541
3635
  var EPS$3 = 1e-6;
3542
3636
  var HAIR = .05;
3543
3637
  var CIRCLE_SEGMENTS = 48;
@@ -3838,8 +3932,7 @@ function circleLocal(c, r) {
3838
3932
  }
3839
3933
  return pts;
3840
3934
  }
3841
- //#endregion
3842
- //#region src/validateFns.ts
3935
+ // src/validateFns.ts
3843
3936
  var OVERLAP_TOL = 1e-6;
3844
3937
  /**
3845
3938
  * Manufacturability checks for an authored sheet-metal part. Returns a list of
@@ -4021,8 +4114,7 @@ function pairKey(a, b) {
4021
4114
  function boundsOverlap(a, b) {
4022
4115
  return a.xMin < b.xMax - OVERLAP_TOL && b.xMin < a.xMax - OVERLAP_TOL && a.yMin < b.yMax - OVERLAP_TOL && b.yMin < a.yMax - OVERLAP_TOL && a.zMin < b.zMax - OVERLAP_TOL && b.zMin < a.zMax - OVERLAP_TOL;
4023
4116
  }
4024
- //#endregion
4025
- //#region src/foldFns.ts
4117
+ // src/foldFns.ts
4026
4118
  /**
4027
4119
  * Fold a flat pattern up into a 3D part. Each {@link FoldRegion} folds about its
4028
4120
  * fold line off its parent region, exactly inverting {@link unfold}: folding by an
@@ -4680,8 +4772,7 @@ function uniqueSorted(values) {
4680
4772
  }
4681
4773
  return out;
4682
4774
  }
4683
- //#endregion
4684
- //#region src/miterFns.ts
4775
+ // src/miterFns.ts
4685
4776
  /**
4686
4777
  * General miter primitive: cut a sheet-metal part by a plane, removing the
4687
4778
  * material on the `+normal` side. The tool is a half-space block sized to the
@@ -4803,8 +4894,7 @@ function alignZTo(shape, target) {
4803
4894
  axis
4804
4895
  });
4805
4896
  }
4806
- //#endregion
4807
- //#region src/contourFlangeFns.ts
4897
+ // src/contourFlangeFns.ts
4808
4898
  /**
4809
4899
  * Author a contour flange: an OPEN 2D profile (alternating line/arc segments) swept
4810
4900
  * along one straight edge of the base flat. Each arc is a cylindrical bend, each
@@ -5273,8 +5363,7 @@ function matrixToAxisAngle(m) {
5273
5363
  angleDeg
5274
5364
  };
5275
5365
  }
5276
- //#endregion
5277
- //#region src/loftedFlangeFns.ts
5366
+ // src/loftedFlangeFns.ts
5278
5367
  /**
5279
5368
  * Relative out-of-plane tolerance for the per-quad developability test: a quad is
5280
5369
  * twisted (non-developable) when its far corner sits off the diagonal-triangle plane
@@ -5586,8 +5675,7 @@ function closedLoopWire(loop) {
5586
5675
  }
5587
5676
  return (0, brepjs.wireLoop)(edges);
5588
5677
  }
5589
- //#endregion
5590
- //#region src/hemFns.ts
5678
+ // src/hemFns.ts
5591
5679
  /**
5592
5680
  * A small clearance baked into a CLOSED hem's curl radius. A true closed hem folds
5593
5681
  * the return flat against the parent with zero gap; modelling that exactly makes the
@@ -5780,8 +5868,7 @@ function splitAngle(totalDeg) {
5780
5868
  const per = totalDeg / n;
5781
5869
  return Array.from({ length: n }, () => per);
5782
5870
  }
5783
- //#endregion
5784
- //#region src/jogFns.ts
5871
+ // src/jogFns.ts
5785
5872
  /**
5786
5873
  * Author a jog (joggle): two opposite bends (`+θ` then `−θ`) that step a flat by
5787
5874
  * `offsetHeight` perpendicular to its plane, then continue parallel past the step.
@@ -5940,8 +6027,7 @@ function jogId(spec) {
5940
6027
  function resolveRegion(region) {
5941
6028
  return region === "base" || region === "face-0" ? ROOT_FLAT_ID : region;
5942
6029
  }
5943
- //#endregion
5944
- //#region src/dxfFns.ts
6030
+ // src/dxfFns.ts
5945
6031
  var DEFAULT_TEXT_HEIGHT = 2.5;
5946
6032
  var LAYER_OUTLINE = "OUTLINE";
5947
6033
  var LAYER_BEND_UP = "BEND_UP";
@@ -6150,8 +6236,7 @@ function pathPoints(wire) {
6150
6236
  function toPt2(v) {
6151
6237
  return [v[0], v[1]];
6152
6238
  }
6153
- //#endregion
6154
- //#region src/polygonFns.ts
6239
+ // src/polygonFns.ts
6155
6240
  /**
6156
6241
  * 2D simple-polygon geometry for true-shape (no-fit-polygon) nesting: an ordered
6157
6242
  * vertex loop plus the overlap predicate that is the correctness backbone of the
@@ -6394,8 +6479,7 @@ function pointSegmentDistance(p, a, b) {
6394
6479
  const dy = p[1] - (a[1] + t * vy);
6395
6480
  return Math.hypot(dx, dy);
6396
6481
  }
6397
- //#endregion
6398
- //#region src/nestFns.ts
6482
+ // src/nestFns.ts
6399
6483
  var EPS = 1e-9;
6400
6484
  /**
6401
6485
  * Bounding-box nest: arrange developed flat patterns onto stock sheets to reduce
@@ -6861,8 +6945,7 @@ function nestToDXF$1(result, patterns, sheetIndex, options) {
6861
6945
  }
6862
6946
  return multiPatternToDXF(placed, options ?? {});
6863
6947
  }
6864
- //#endregion
6865
- //#region src/reportFns.ts
6948
+ // src/reportFns.ts
6866
6949
  /**
6867
6950
  * Build the bend-table JSON report directly from an authored part. Walks the
6868
6951
  * same tree layout the unfold consumes so the per-bend values and the total flat
@@ -6905,8 +6988,7 @@ function reportFromUnfold(result) {
6905
6988
  function reportToJSON(report) {
6906
6989
  return JSON.stringify(report, null, 2);
6907
6990
  }
6908
- //#endregion
6909
- //#region src/api.ts
6991
+ // src/api.ts
6910
6992
  /** Author a straight-bend part: a base flat plus folded-up flanges. */
6911
6993
  function author(spec) {
6912
6994
  return authorPart(spec);
@@ -7068,8 +7150,7 @@ function bendTable(id) {
7068
7150
  function resolveAllowance(rule, angleDeg, thickness, onWarning) {
7069
7151
  return resolveBendAllowance(rule, angleDeg, thickness, onWarning);
7070
7152
  }
7071
- //#endregion
7072
- //#region src/facade.ts
7153
+ // src/facade.ts
7073
7154
  /** Thrown by the fluent facade when an underlying `Result<T>` is an `Err`. */
7074
7155
  var SheetMetalError = class extends Error {
7075
7156
  code;
@@ -7339,7 +7420,6 @@ function foldFlat(input) {
7339
7420
  function fromSolid(solid, opts) {
7340
7421
  return new ForeignSolidHandle(solid, opts);
7341
7422
  }
7342
- //#endregion
7343
7423
  exports.ALUMINUM_GAUGES = ALUMINUM_GAUGES;
7344
7424
  exports.DEFAULT_K_FACTOR = DEFAULT_K_FACTOR;
7345
7425
  exports.ForeignSolidHandle = ForeignSolidHandle;
@@ -1,5 +1,5 @@
1
- import { box, curveEndPoint, curveStartPoint, cut, cylinder, err, extrude, face, faceCenter, fuse, getBounds, getEdges, getFaces, getSolids, getSurfaceType, intersect, isErr, isOk, isPlanarWire, isSolid, isValid, line, measureArea, measureVolume, normalAt, ok, pointOnSurface, rotate, sharedEdges, translate, validationError, vecAdd, vecCross, vecDot, vecLength, vecNormalize, vecScale, vecSub, wire, wireLoop } from "brepjs";
2
- //#region src/materials.ts
1
+ import { box, curveEndPoint, curveStartPoint, cut, cylinder, err, extrude, face, faceCenter, fuse, getBounds, getEdges, getFaces, getSolids, getSurfaceType, intersect, isErr, isOk, isPlanarWire, isSolid, isValid, line, measureArea, measureDistance, measureVolume, normalAt, ok, pointOnSurface, rotate, sharedEdges, translate, validationError, vecAdd, vecCross, vecDot, vecLength, vecNormalize, vecScale, vecSub, vertex, wire, wireLoop } from "brepjs";
2
+ // src/materials.ts
3
3
  var DEFAULT_K_FACTOR = .44;
4
4
  function gauge(name, thickness, kFactor = DEFAULT_K_FACTOR) {
5
5
  return {
@@ -37,8 +37,7 @@ var MATERIALS = {
37
37
  function getMaterial(name) {
38
38
  return MATERIALS[name];
39
39
  }
40
- //#endregion
41
- //#region src/bendTableFns.ts
40
+ // src/bendTableFns.ts
42
41
  var DEG_TO_RAD = Math.PI / 180;
43
42
  var registry = /* @__PURE__ */ new Map();
44
43
  /**
@@ -377,8 +376,7 @@ function ensureStarterTables() {
377
376
  });
378
377
  }
379
378
  }
380
- //#endregion
381
- //#region src/allowanceFns.ts
379
+ // src/allowanceFns.ts
382
380
  function validateRule(rule) {
383
381
  if (!Number.isFinite(rule.innerRadius) || rule.innerRadius < 0) return err(validationError("INVALID_RADIUS", `innerRadius must be a finite, non-negative number, got ${rule.innerRadius}`));
384
382
  if (!Number.isFinite(rule.kFactor) || rule.kFactor < 0 || rule.kFactor > 1) return err(validationError("INVALID_K_FACTOR", `kFactor must be in [0, 1], got ${rule.kFactor}`));
@@ -412,8 +410,7 @@ function neutralRadius(thickness, rule) {
412
410
  if (!ruleCheck.ok) return ruleCheck;
413
411
  return ok(rule.innerRadius + rule.kFactor * thickness);
414
412
  }
415
- //#endregion
416
- //#region src/featureTreeFns.ts
413
+ // src/featureTreeFns.ts
417
414
  var ROOT_FLAT_ID = "root";
418
415
  /**
419
416
  * Build the feature graph for an authored part. Each flange contributes a flat
@@ -558,8 +555,7 @@ function featureTree(part, rootId = ROOT_FLAT_ID) {
558
555
  if (!graph.ok) return graph;
559
556
  return buildFeatureTree(graph.value, rootId);
560
557
  }
561
- //#endregion
562
- //#region src/internal.ts
558
+ // src/internal.ts
563
559
  /**
564
560
  * OCCT boolean fuse returns a compound wrapping the unioned solid. Extract the
565
561
  * single solid so a part's cached shape is a true `Solid` (clean STEP/GLB
@@ -582,8 +578,7 @@ function sideFromAxisDir(axisDir) {
582
578
  if (Math.abs(axisDir[1]) > .5) return "xmax";
583
579
  if (Math.abs(axisDir[0]) > .5) return "ymax";
584
580
  }
585
- //#endregion
586
- //#region src/unfoldFns.ts
581
+ // src/unfoldFns.ts
587
582
  /**
588
583
  * Tree-driven analytic unfold of an arbitrary straight-bend part into its flat
589
584
  * pattern. Walks the feature tree in BFS order placing every flat in the flat
@@ -1290,8 +1285,62 @@ function reportFromLayout(layout) {
1290
1285
  totalFlatSize: [maxX - minX, maxY - minY]
1291
1286
  };
1292
1287
  }
1293
- //#endregion
1294
- //#region src/foreignUnfoldFns.ts
1288
+ // \0@oxc-project+runtime@0.155.0/helpers/esm/usingCtx.js
1289
+ function _usingCtx() {
1290
+ var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
1291
+ var n = Error();
1292
+ return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
1293
+ }, e = {}, n = [];
1294
+ function using(r, e) {
1295
+ if (null != e) {
1296
+ if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
1297
+ if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
1298
+ if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
1299
+ if ("function" != typeof o) throw new TypeError("Object is not disposable.");
1300
+ t && (o = function o() {
1301
+ try {
1302
+ t.call(e);
1303
+ } catch (r) {
1304
+ return Promise.reject(r);
1305
+ }
1306
+ }), n.push({
1307
+ v: e,
1308
+ d: o,
1309
+ a: r
1310
+ });
1311
+ } else r && n.push({
1312
+ d: e,
1313
+ a: r
1314
+ });
1315
+ return e;
1316
+ }
1317
+ return {
1318
+ e,
1319
+ u: using.bind(null, !1),
1320
+ a: using.bind(null, !0),
1321
+ d: function d() {
1322
+ var o, t = this.e, s = 0;
1323
+ function next() {
1324
+ for (; o = n.pop();) try {
1325
+ if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
1326
+ if (o.d) {
1327
+ var r = o.d.call(o.v);
1328
+ if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
1329
+ } else s |= 1;
1330
+ } catch (r) {
1331
+ return err(r);
1332
+ }
1333
+ if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
1334
+ if (t !== e) throw t;
1335
+ }
1336
+ function err(n) {
1337
+ return t = t !== e ? new r(n, t) : n, next();
1338
+ }
1339
+ return next();
1340
+ }
1341
+ };
1342
+ }
1343
+ // src/foreignUnfoldFns.ts
1295
1344
  /** Default neutral-axis K-factor for a purely geometric unfold (mid-surface). */
1296
1345
  var DEFAULT_FOREIGN_K = .5;
1297
1346
  /** Relative tolerance for the cylinder-fit residual (fraction of the radius). */
@@ -1601,10 +1650,11 @@ function unfoldForeignSolid(solid, opts) {
1601
1650
  message: `UNSUPPORTED_FACE: surface type '${t.value}' is not a planar panel or cylindrical bend; the unfold ignores it and may be incomplete`
1602
1651
  });
1603
1652
  }
1604
- const thicknessResult = detectThickness(planarFaces);
1653
+ const planar = planarFaces.map(describePlanar);
1654
+ const thicknessResult = detectThickness(planar);
1605
1655
  if (!thicknessResult.ok) return thicknessResult;
1606
1656
  const thickness = thicknessResult.value;
1607
- const flats = pairFlats(planarFaces, thickness, warnings);
1657
+ const flats = pairFlats(planar, thickness, warnings);
1608
1658
  if (flats.length === 0) return err(validationError("NO_FLATS", "no planar panel pairs detected; not a recognizable sheet-metal solid"));
1609
1659
  const layout = layoutForeign(flats, buildBendGraph(flats, pairBends(cylFaces, warnings), warnings), thickness, kFactor, warnings);
1610
1660
  if (!layout.ok) return layout;
@@ -1614,59 +1664,106 @@ function unfoldForeignSolid(solid, opts) {
1614
1664
  warnings
1615
1665
  });
1616
1666
  }
1667
+ function describePlanar(face) {
1668
+ const center = faceCenter(face);
1669
+ const n = 4;
1670
+ const candidates = [center];
1671
+ for (let i = 0; i < n; i += 1) for (let j = 0; j < n; j += 1) candidates.push(pointOnSurface(face, (i + .5) / n, (j + .5) / n));
1672
+ const interior = candidates.filter((p) => liesOnFace(face, p));
1673
+ return {
1674
+ face,
1675
+ normal: faceNormal(face),
1676
+ center,
1677
+ interior
1678
+ };
1679
+ }
1680
+ /** True if `point` (already on the face's surface) lies within the face's trimmed boundary. */
1681
+ function liesOnFace(face, point) {
1682
+ try {
1683
+ var _usingCtx$1 = _usingCtx();
1684
+ const probe = _usingCtx$1.u(vertex(point));
1685
+ const d = measureDistance(probe, face);
1686
+ return d.ok && d.value < GEOM_TOL;
1687
+ } catch (_) {
1688
+ _usingCtx$1.e = _;
1689
+ } finally {
1690
+ _usingCtx$1.d();
1691
+ }
1692
+ }
1693
+ /** Normal-direction gap between two planar faces, or `null` unless they are antiparallel. */
1694
+ function antiparallelGap(a, b) {
1695
+ if (vecDot(a.normal, b.normal) > -.99) return null;
1696
+ return Math.abs(vecDot(vecSub(b.center, a.center), a.normal));
1697
+ }
1617
1698
  /**
1618
- * Detect the sheet thickness as the smallest gap between an antiparallel pair of
1619
- * planar faces (the two faces of one flat panel). For a roughly-uniform-thickness
1620
- * part this gap is the same across panels; a spread beyond tolerance warns.
1699
+ * Antiparallel faces are the two sides of one panel only if they face each other:
1700
+ * an interior point of one projects inside the other. The normal-direction gap
1701
+ * alone also matches faces that merely sit on parallel planes, e.g. a flange's end
1702
+ * cap and the adjacent flange's inner face across a corner. Their gap is the bend's
1703
+ * inner radius, which passes for the sheet thickness as T approaches or exceeds R.
1704
+ */
1705
+ function facesOppose(a, b) {
1706
+ return b.interior.some((p) => liesOnFace(a.face, projectToPlane(p, a.normal, a.center))) || a.interior.some((p) => liesOnFace(b.face, projectToPlane(p, b.normal, b.center)));
1707
+ }
1708
+ /**
1709
+ * Detect the sheet thickness as the smallest gap between two antiparallel planar
1710
+ * faces that oppose each other (the two faces of one flat panel).
1621
1711
  */
1622
1712
  function detectThickness(planar) {
1623
- const gaps = [];
1713
+ const pairs = [];
1624
1714
  for (let i = 0; i < planar.length; i += 1) for (let j = i + 1; j < planar.length; j += 1) {
1625
- const fa = planar[i];
1626
- const fb = planar[j];
1627
- if (fa === void 0 || fb === void 0) continue;
1628
- const na = faceNormal(fa);
1629
- const nb = faceNormal(fb);
1630
- if (vecDot(na, nb) > -.99) continue;
1631
- const ca = faceCenter(fa);
1632
- const cb = faceCenter(fb);
1633
- const gap = Math.abs(vecDot(vecSub(cb, ca), na));
1634
- if (gap > GEOM_TOL) gaps.push(gap);
1715
+ const a = planar[i];
1716
+ const b = planar[j];
1717
+ if (a === void 0 || b === void 0) continue;
1718
+ const gap = antiparallelGap(a, b);
1719
+ if (gap !== null && gap > GEOM_TOL) pairs.push({
1720
+ gap,
1721
+ a,
1722
+ b
1723
+ });
1724
+ }
1725
+ pairs.sort((p, q) => p.gap - q.gap);
1726
+ const thinnest = pairs.find((p) => facesOppose(p.a, p.b));
1727
+ if (thinnest === void 0) return err(validationError("NO_THICKNESS", "could not detect a sheet thickness (no opposed planar pair)"));
1728
+ return ok(thinnest.gap);
1729
+ }
1730
+ /** Unused face opposing `a` whose gap is nearest the sheet thickness, within tolerance. */
1731
+ function bestOpposedPartner(planar, i, thickness, used) {
1732
+ const a = planar[i];
1733
+ if (a === void 0) return null;
1734
+ const candidates = [];
1735
+ for (let j = 0; j < planar.length; j += 1) {
1736
+ const b = planar[j];
1737
+ if (j === i || used.has(j) || b === void 0) continue;
1738
+ const gap = antiparallelGap(a, b);
1739
+ if (gap === null) continue;
1740
+ const e = Math.abs(gap - thickness);
1741
+ if (e <= thickness * .5 + GEOM_TOL) candidates.push({
1742
+ j,
1743
+ e
1744
+ });
1635
1745
  }
1636
- if (gaps.length === 0) return err(validationError("NO_THICKNESS", "could not detect a sheet thickness (no opposed planar pair)"));
1637
- gaps.sort((a, b) => a - b);
1638
- return ok(gaps[0]);
1746
+ candidates.sort((p, q) => p.e - q.e);
1747
+ const match = candidates.find((c) => {
1748
+ const b = planar[c.j];
1749
+ return b !== void 0 && facesOppose(a, b);
1750
+ });
1751
+ return match === void 0 ? null : match.j;
1639
1752
  }
1640
1753
  /**
1641
- * Pair planar faces into flat panels: each panel is two parallel faces a
1642
- * `thickness` apart with overlapping footprints. Greedy nearest-match by gap.
1754
+ * Pair planar faces into flat panels: each panel is two antiparallel faces a
1755
+ * `thickness` apart that oppose each other. Greedy nearest-match by gap.
1643
1756
  */
1644
1757
  function pairFlats(planar, thickness, warnings) {
1645
1758
  const used = /* @__PURE__ */ new Set();
1646
1759
  const flats = [];
1647
1760
  for (let i = 0; i < planar.length; i += 1) {
1648
1761
  if (used.has(i)) continue;
1649
- const fa = planar[i];
1650
- if (fa === void 0) continue;
1651
- const na = faceNormal(fa);
1652
- const ca = faceCenter(fa);
1653
- let best = -1;
1654
- let bestErr = Infinity;
1655
- for (let j = 0; j < planar.length; j += 1) {
1656
- if (j === i || used.has(j)) continue;
1657
- const fb = planar[j];
1658
- if (fb === void 0) continue;
1659
- const nb = faceNormal(fb);
1660
- if (vecDot(na, nb) > -.99) continue;
1661
- const cb = faceCenter(fb);
1662
- const gap = Math.abs(vecDot(vecSub(cb, ca), na));
1663
- const e = Math.abs(gap - thickness);
1664
- if (e < bestErr) {
1665
- bestErr = e;
1666
- best = j;
1667
- }
1668
- }
1669
- if (best < 0 || bestErr > thickness * .5 + GEOM_TOL) {
1762
+ const pa = planar[i];
1763
+ if (pa === void 0) continue;
1764
+ const fa = pa.face;
1765
+ const best = bestOpposedPartner(planar, i, thickness, used);
1766
+ if (best === null) {
1670
1767
  const db = getBounds(fa);
1671
1768
  const ext = [
1672
1769
  db.xMax - db.xMin,
@@ -1680,7 +1777,9 @@ function pairFlats(planar, thickness, warnings) {
1680
1777
  });
1681
1778
  continue;
1682
1779
  }
1683
- const fb = planar[best];
1780
+ const pb = planar[best];
1781
+ if (pb === void 0) continue;
1782
+ const fb = pb.face;
1684
1783
  used.add(i);
1685
1784
  used.add(best);
1686
1785
  const areaA = measureArea(fa);
@@ -1703,8 +1802,8 @@ function pairFlats(planar, thickness, warnings) {
1703
1802
  flats.push({
1704
1803
  id: `flat-${flats.length}`,
1705
1804
  faces: [fa, fb],
1706
- normal: na,
1707
- center: vecScale(vecAdd(ca, faceCenter(fb)), .5),
1805
+ normal: pa.normal,
1806
+ center: vecScale(vecAdd(pa.center, pb.center), .5),
1708
1807
  area
1709
1808
  });
1710
1809
  }
@@ -2180,8 +2279,7 @@ function bendDirection(bend, parent) {
2180
2279
  function faceNormal(face) {
2181
2280
  return vecNormalize(normalAt(face, faceCenter(face)));
2182
2281
  }
2183
- //#endregion
2184
- //#region src/authorFns.ts
2282
+ // src/authorFns.ts
2185
2283
  /**
2186
2284
  * Stable edge reference for flange attachment. The base flat is `face-0`; every
2187
2285
  * flange face is `face-<n+1>` in authoring order. The leading edge of a flat is
@@ -2798,8 +2896,7 @@ function buildFlangeFlat(axisOrigin, thetaDeg, width, thickness, length, directi
2798
2896
  ]
2799
2897
  });
2800
2898
  }
2801
- //#endregion
2802
- //#region src/reliefFns.ts
2899
+ // src/reliefFns.ts
2803
2900
  var EPS$6 = 1e-6;
2804
2901
  var Z_AXIS$1 = [
2805
2902
  0,
@@ -3163,8 +3260,7 @@ function toPt2$1(v) {
3163
3260
  function dist2(a, b) {
3164
3261
  return Math.hypot(a[0] - b[0], a[1] - b[1]);
3165
3262
  }
3166
- //#endregion
3167
- //#region src/cutoutFns.ts
3263
+ // src/cutoutFns.ts
3168
3264
  var EPS$5 = 1e-6;
3169
3265
  /** A hair of over-cut so the tool pokes through both faces cleanly. */
3170
3266
  var HAIR$1 = .05;
@@ -3392,8 +3488,7 @@ function polygonArea(loop) {
3392
3488
  }
3393
3489
  return Math.abs(sum) / 2;
3394
3490
  }
3395
- //#endregion
3396
- //#region src/tabFns.ts
3491
+ // src/tabFns.ts
3397
3492
  var EPS$4 = 1e-6;
3398
3493
  /**
3399
3494
  * Fuse a rectangular tab onto a named flat region's edge — additive material (the
@@ -3535,8 +3630,7 @@ function devRect(local, dev) {
3535
3630
  y1
3536
3631
  ];
3537
3632
  }
3538
- //#endregion
3539
- //#region src/formFns.ts
3633
+ // src/formFns.ts
3540
3634
  var EPS$3 = 1e-6;
3541
3635
  var HAIR = .05;
3542
3636
  var CIRCLE_SEGMENTS = 48;
@@ -3837,8 +3931,7 @@ function circleLocal(c, r) {
3837
3931
  }
3838
3932
  return pts;
3839
3933
  }
3840
- //#endregion
3841
- //#region src/validateFns.ts
3934
+ // src/validateFns.ts
3842
3935
  var OVERLAP_TOL = 1e-6;
3843
3936
  /**
3844
3937
  * Manufacturability checks for an authored sheet-metal part. Returns a list of
@@ -4020,8 +4113,7 @@ function pairKey(a, b) {
4020
4113
  function boundsOverlap(a, b) {
4021
4114
  return a.xMin < b.xMax - OVERLAP_TOL && b.xMin < a.xMax - OVERLAP_TOL && a.yMin < b.yMax - OVERLAP_TOL && b.yMin < a.yMax - OVERLAP_TOL && a.zMin < b.zMax - OVERLAP_TOL && b.zMin < a.zMax - OVERLAP_TOL;
4022
4115
  }
4023
- //#endregion
4024
- //#region src/foldFns.ts
4116
+ // src/foldFns.ts
4025
4117
  /**
4026
4118
  * Fold a flat pattern up into a 3D part. Each {@link FoldRegion} folds about its
4027
4119
  * fold line off its parent region, exactly inverting {@link unfold}: folding by an
@@ -4679,8 +4771,7 @@ function uniqueSorted(values) {
4679
4771
  }
4680
4772
  return out;
4681
4773
  }
4682
- //#endregion
4683
- //#region src/miterFns.ts
4774
+ // src/miterFns.ts
4684
4775
  /**
4685
4776
  * General miter primitive: cut a sheet-metal part by a plane, removing the
4686
4777
  * material on the `+normal` side. The tool is a half-space block sized to the
@@ -4802,8 +4893,7 @@ function alignZTo(shape, target) {
4802
4893
  axis
4803
4894
  });
4804
4895
  }
4805
- //#endregion
4806
- //#region src/contourFlangeFns.ts
4896
+ // src/contourFlangeFns.ts
4807
4897
  /**
4808
4898
  * Author a contour flange: an OPEN 2D profile (alternating line/arc segments) swept
4809
4899
  * along one straight edge of the base flat. Each arc is a cylindrical bend, each
@@ -5272,8 +5362,7 @@ function matrixToAxisAngle(m) {
5272
5362
  angleDeg
5273
5363
  };
5274
5364
  }
5275
- //#endregion
5276
- //#region src/loftedFlangeFns.ts
5365
+ // src/loftedFlangeFns.ts
5277
5366
  /**
5278
5367
  * Relative out-of-plane tolerance for the per-quad developability test: a quad is
5279
5368
  * twisted (non-developable) when its far corner sits off the diagonal-triangle plane
@@ -5585,8 +5674,7 @@ function closedLoopWire(loop) {
5585
5674
  }
5586
5675
  return wireLoop(edges);
5587
5676
  }
5588
- //#endregion
5589
- //#region src/hemFns.ts
5677
+ // src/hemFns.ts
5590
5678
  /**
5591
5679
  * A small clearance baked into a CLOSED hem's curl radius. A true closed hem folds
5592
5680
  * the return flat against the parent with zero gap; modelling that exactly makes the
@@ -5779,8 +5867,7 @@ function splitAngle(totalDeg) {
5779
5867
  const per = totalDeg / n;
5780
5868
  return Array.from({ length: n }, () => per);
5781
5869
  }
5782
- //#endregion
5783
- //#region src/jogFns.ts
5870
+ // src/jogFns.ts
5784
5871
  /**
5785
5872
  * Author a jog (joggle): two opposite bends (`+θ` then `−θ`) that step a flat by
5786
5873
  * `offsetHeight` perpendicular to its plane, then continue parallel past the step.
@@ -5939,8 +6026,7 @@ function jogId(spec) {
5939
6026
  function resolveRegion(region) {
5940
6027
  return region === "base" || region === "face-0" ? ROOT_FLAT_ID : region;
5941
6028
  }
5942
- //#endregion
5943
- //#region src/dxfFns.ts
6029
+ // src/dxfFns.ts
5944
6030
  var DEFAULT_TEXT_HEIGHT = 2.5;
5945
6031
  var LAYER_OUTLINE = "OUTLINE";
5946
6032
  var LAYER_BEND_UP = "BEND_UP";
@@ -6149,8 +6235,7 @@ function pathPoints(wire) {
6149
6235
  function toPt2(v) {
6150
6236
  return [v[0], v[1]];
6151
6237
  }
6152
- //#endregion
6153
- //#region src/polygonFns.ts
6238
+ // src/polygonFns.ts
6154
6239
  /**
6155
6240
  * 2D simple-polygon geometry for true-shape (no-fit-polygon) nesting: an ordered
6156
6241
  * vertex loop plus the overlap predicate that is the correctness backbone of the
@@ -6393,8 +6478,7 @@ function pointSegmentDistance(p, a, b) {
6393
6478
  const dy = p[1] - (a[1] + t * vy);
6394
6479
  return Math.hypot(dx, dy);
6395
6480
  }
6396
- //#endregion
6397
- //#region src/nestFns.ts
6481
+ // src/nestFns.ts
6398
6482
  var EPS = 1e-9;
6399
6483
  /**
6400
6484
  * Bounding-box nest: arrange developed flat patterns onto stock sheets to reduce
@@ -6860,8 +6944,7 @@ function nestToDXF$1(result, patterns, sheetIndex, options) {
6860
6944
  }
6861
6945
  return multiPatternToDXF(placed, options ?? {});
6862
6946
  }
6863
- //#endregion
6864
- //#region src/reportFns.ts
6947
+ // src/reportFns.ts
6865
6948
  /**
6866
6949
  * Build the bend-table JSON report directly from an authored part. Walks the
6867
6950
  * same tree layout the unfold consumes so the per-bend values and the total flat
@@ -6904,8 +6987,7 @@ function reportFromUnfold(result) {
6904
6987
  function reportToJSON(report) {
6905
6988
  return JSON.stringify(report, null, 2);
6906
6989
  }
6907
- //#endregion
6908
- //#region src/api.ts
6990
+ // src/api.ts
6909
6991
  /** Author a straight-bend part: a base flat plus folded-up flanges. */
6910
6992
  function author(spec) {
6911
6993
  return authorPart(spec);
@@ -7067,8 +7149,7 @@ function bendTable(id) {
7067
7149
  function resolveAllowance(rule, angleDeg, thickness, onWarning) {
7068
7150
  return resolveBendAllowance(rule, angleDeg, thickness, onWarning);
7069
7151
  }
7070
- //#endregion
7071
- //#region src/facade.ts
7152
+ // src/facade.ts
7072
7153
  /** Thrown by the fluent facade when an underlying `Result<T>` is an `Err`. */
7073
7154
  var SheetMetalError = class extends Error {
7074
7155
  code;
@@ -7338,5 +7419,4 @@ function foldFlat(input) {
7338
7419
  function fromSolid(solid, opts) {
7339
7420
  return new ForeignSolidHandle(solid, opts);
7340
7421
  }
7341
- //#endregion
7342
7422
  export { ALUMINUM_GAUGES, DEFAULT_K_FACTOR, ForeignSolidHandle, MATERIALS, ROOT_FLAT_ID, STEEL_GAUGES, SheetMetalBuilder, SheetMetalError, SheetMetalPartHandle, addBendRelief, addBendTable, addCutout, addForm, addHole, addPolygonCutout, addSlot, addTab, allowance, author, authorContourFlange, authorLoftedFlange, authorPart, autoBendReliefs, autoMiterCorner, autoReliefs, baseEdgeRef, bendAllowance, bendRelief, bendTable, buildFeatureGraph, buildFeatureTree, buildReport, contourFlange, cornerRelief, developed, developedLength, emboss, featureTree, fitCylinder, flatPatternToDXF, flatPatternToPolylines, fold, foldFlat, fold$1 as foldPart, foldWithWarnings, fromPart, fromSolid, getBendTable, getMaterial, hem, jog, loftedFlange, louver, miter, miterCorner, miterCut, multiPatternToDXF, nest, nestToDXF, neutralRadius, partToFlatInput, patternBbox, patternToFlatInput, pointInPolygon, polygonBounds, polygonsOverlap, polygonsOverlapWithClearance, registerBendTable, relieveCorner, report, reportFrom, reportFromUnfold, reportJSON, reportToJSON, resolveAllowance, resolveBendAllowance, segmentsIntersect, sheetMetal, signedArea, tabAndSlot, toDXF, transformPolygon, unfold, unfoldForeignSolid, unfold$1 as unfoldPart, unfoldSolid, validate, validatePart, wireToPolygon };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs-sheetmetal",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Sheet-metal CAD domain for brepjs — flange authoring, auto-miter, fold/unfold flat patterns",
5
5
  "keywords": [
6
6
  "sheet-metal",
@@ -53,13 +53,13 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@eslint/js": "*",
56
- "@types/node": "26.2.0",
56
+ "@types/node": "26.6.5",
57
57
  "eslint": "*",
58
- "tsx": "4.23.11",
58
+ "tsx": "4.23.15",
59
59
  "typescript": "*",
60
60
  "typescript-eslint": "*",
61
61
  "vite": "^8.0.0",
62
62
  "vite-plugin-dts": "^5.0.0",
63
- "vitest": "^4.0.0"
63
+ "vitest": "^5.0.0"
64
64
  }
65
65
  }