brepjs-sheetmetal 0.3.0 → 0.4.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.
@@ -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
@@ -505,7 +502,7 @@ function buildFeatureTree(graph, rootId = ROOT_FLAT_ID) {
505
502
  adjacency.get(edge.parent)?.push(edge);
506
503
  adjacency.get(edge.child)?.push(edge);
507
504
  }
508
- const visited = new Set([rootId]);
505
+ const visited = /* @__PURE__ */ new Set([rootId]);
509
506
  const treeEdges = /* @__PURE__ */ new Set();
510
507
  const bends = [];
511
508
  const queue = [rootId];
@@ -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
@@ -674,18 +669,20 @@ function unfold(part) {
674
669
  direction: p.direction,
675
670
  inward: [-p.frame.v[0], -p.frame.v[1]]
676
671
  })).concat(contourResult.value.bendLines).concat(hemJogResult.value.bendLines);
672
+ const pattern = {
673
+ outline: outlineResult.value,
674
+ bendLines,
675
+ holes: holesResult.value,
676
+ formCuts: formsResult.value.cuts,
677
+ formMarkers: formsResult.value.markers,
678
+ formHinges: formsResult.value.hinges,
679
+ loftedDevelopments: loftedResult.value,
680
+ developedArea: layout.developedArea
681
+ };
682
+ const report = reportFromLayout(layout);
677
683
  return (0, brepjs.ok)({
678
- pattern: {
679
- outline: outlineResult.value,
680
- bendLines,
681
- holes: holesResult.value,
682
- formCuts: formsResult.value.cuts,
683
- formMarkers: formsResult.value.markers,
684
- formHinges: formsResult.value.hinges,
685
- loftedDevelopments: loftedResult.value,
686
- developedArea: layout.developedArea
687
- },
688
- report: reportFromLayout(layout),
684
+ pattern,
685
+ report,
689
686
  warnings
690
687
  });
691
688
  }
@@ -1289,8 +1286,7 @@ function reportFromLayout(layout) {
1289
1286
  totalFlatSize: [maxX - minX, maxY - minY]
1290
1287
  };
1291
1288
  }
1292
- //#endregion
1293
- //#region src/foreignUnfoldFns.ts
1289
+ // src/foreignUnfoldFns.ts
1294
1290
  /** Default neutral-axis K-factor for a purely geometric unfold (mid-surface). */
1295
1291
  var DEFAULT_FOREIGN_K = .5;
1296
1292
  /** Relative tolerance for the cylinder-fit residual (fraction of the radius). */
@@ -1308,7 +1304,8 @@ function sampleFace(face, n) {
1308
1304
  for (let i = 0; i < n; i += 1) {
1309
1305
  const u = n === 1 ? .5 : i / (n - 1);
1310
1306
  for (let j = 0; j < n; j += 1) {
1311
- const point = (0, brepjs.pointOnSurface)(face, u, n === 1 ? .5 : j / (n - 1));
1307
+ const v = n === 1 ? .5 : j / (n - 1);
1308
+ const point = (0, brepjs.pointOnSurface)(face, u, v);
1312
1309
  const normal = (0, brepjs.normalAt)(face, point);
1313
1310
  const len = (0, brepjs.vecLength)(normal);
1314
1311
  if (len < GEOM_TOL) continue;
@@ -1399,7 +1396,7 @@ function axisFromNormals(normals) {
1399
1396
  }
1400
1397
  /** An orthonormal pair spanning the plane perpendicular to `axis`. */
1401
1398
  function perpBasis(axis) {
1402
- const e1 = (0, brepjs.vecNormalize)((0, brepjs.vecCross)(axis, Math.abs(axis[0]) < .9 ? [
1399
+ const seed = Math.abs(axis[0]) < .9 ? [
1403
1400
  1,
1404
1401
  0,
1405
1402
  0
@@ -1407,7 +1404,8 @@ function perpBasis(axis) {
1407
1404
  0,
1408
1405
  1,
1409
1406
  0
1410
- ]));
1407
+ ];
1408
+ const e1 = (0, brepjs.vecNormalize)((0, brepjs.vecCross)(axis, seed));
1411
1409
  return {
1412
1410
  e1,
1413
1411
  e2: (0, brepjs.vecNormalize)((0, brepjs.vecCross)(axis, e1))
@@ -1416,7 +1414,9 @@ function perpBasis(axis) {
1416
1414
  /** Perpendicular distance from `p` to the line through `o` with unit direction `d`. */
1417
1415
  function distanceToAxis(p, o, d) {
1418
1416
  const w = (0, brepjs.vecSub)(p, o);
1419
- return (0, brepjs.vecLength)((0, brepjs.vecSub)(w, (0, brepjs.vecScale)(d, (0, brepjs.vecDot)(w, d))));
1417
+ const along = (0, brepjs.vecDot)(w, d);
1418
+ const perp = (0, brepjs.vecSub)(w, (0, brepjs.vecScale)(d, along));
1419
+ return (0, brepjs.vecLength)(perp);
1420
1420
  }
1421
1421
  /** Algebraic (Kåsa) least-squares circle fit to 2D points. */
1422
1422
  function fitCircle2(pts) {
@@ -1621,7 +1621,8 @@ function detectThickness(planar) {
1621
1621
  const fb = planar[j];
1622
1622
  if (fa === void 0 || fb === void 0) continue;
1623
1623
  const na = faceNormal(fa);
1624
- if ((0, brepjs.vecDot)(na, faceNormal(fb)) > -.99) continue;
1624
+ const nb = faceNormal(fb);
1625
+ if ((0, brepjs.vecDot)(na, nb) > -.99) continue;
1625
1626
  const ca = (0, brepjs.faceCenter)(fa);
1626
1627
  const cb = (0, brepjs.faceCenter)(fb);
1627
1628
  const gap = Math.abs((0, brepjs.vecDot)((0, brepjs.vecSub)(cb, ca), na));
@@ -1650,7 +1651,8 @@ function pairFlats(planar, thickness, warnings) {
1650
1651
  if (j === i || used.has(j)) continue;
1651
1652
  const fb = planar[j];
1652
1653
  if (fb === void 0) continue;
1653
- if ((0, brepjs.vecDot)(na, faceNormal(fb)) > -.99) continue;
1654
+ const nb = faceNormal(fb);
1655
+ if ((0, brepjs.vecDot)(na, nb) > -.99) continue;
1654
1656
  const cb = (0, brepjs.faceCenter)(fb);
1655
1657
  const gap = Math.abs((0, brepjs.vecDot)((0, brepjs.vecSub)(cb, ca), na));
1656
1658
  const e = Math.abs(gap - thickness);
@@ -1819,7 +1821,7 @@ function layoutForeign(flats, graph, thickness, kFactor, warnings) {
1819
1821
  adjacency.get(e.flats[0])?.push(e);
1820
1822
  adjacency.get(e.flats[1])?.push(e);
1821
1823
  }
1822
- const visited = new Set([0]);
1824
+ const visited = /* @__PURE__ */ new Set([0]);
1823
1825
  const treeEdges = /* @__PURE__ */ new Set();
1824
1826
  const treeBends = [];
1825
1827
  const queue = [0];
@@ -1913,23 +1915,26 @@ function layoutForeign(flats, graph, thickness, kFactor, warnings) {
1913
1915
  });
1914
1916
  }
1915
1917
  const bbox = aabb2(rects);
1916
- const outlineWire = (0, brepjs.wireLoop)(buildRectOutline2(bbox));
1918
+ const outline = buildRectOutline2(bbox);
1919
+ const outlineWire = (0, brepjs.wireLoop)(outline);
1917
1920
  if (!outlineWire.ok) return outlineWire;
1921
+ const pattern = {
1922
+ outline: outlineWire.value,
1923
+ bendLines,
1924
+ holes: [],
1925
+ formCuts: [],
1926
+ formMarkers: [],
1927
+ formHinges: [],
1928
+ loftedDevelopments: [],
1929
+ developedArea
1930
+ };
1931
+ const report = {
1932
+ bends: bendReportRows,
1933
+ totalFlatSize: [bbox.x1 - bbox.x0, bbox.y1 - bbox.y0]
1934
+ };
1918
1935
  return (0, brepjs.ok)({
1919
- pattern: {
1920
- outline: outlineWire.value,
1921
- bendLines,
1922
- holes: [],
1923
- formCuts: [],
1924
- formMarkers: [],
1925
- formHinges: [],
1926
- loftedDevelopments: [],
1927
- developedArea
1928
- },
1929
- report: {
1930
- bends: bendReportRows,
1931
- totalFlatSize: [bbox.x1 - bbox.x0, bbox.y1 - bbox.y0]
1932
- }
1936
+ pattern,
1937
+ report
1933
1938
  });
1934
1939
  }
1935
1940
  /** Map a 3D in-plane point through a placed flat's affine map. */
@@ -2019,7 +2024,9 @@ function parentFootprintCentreAlong(parent, axisInPlane, axisRef) {
2019
2024
  min = Math.min(min, v);
2020
2025
  max = Math.max(max, v);
2021
2026
  }
2022
- return (0, brepjs.vecAdd)(axisRef, (0, brepjs.vecScale)(axisInPlane, (min + max) / 2 - (0, brepjs.vecDot)(axisRef, axisInPlane)));
2027
+ const mid = (min + max) / 2;
2028
+ const refAlong = (0, brepjs.vecDot)(axisRef, axisInPlane);
2029
+ return (0, brepjs.vecAdd)(axisRef, (0, brepjs.vecScale)(axisInPlane, mid - refAlong));
2023
2030
  }
2024
2031
  /** In-plane run extent of a flat along `out` (perpendicular to the bend axis). */
2025
2032
  function inPlaneRun(flat, out) {
@@ -2035,7 +2042,8 @@ function inPlaneRun(flat, out) {
2035
2042
  }
2036
2043
  /** Run direction of a child flat pointing away from the bend axis. */
2037
2044
  function runDirectionAway(flat, out, axisOrigin) {
2038
- return (0, brepjs.vecDot)((0, brepjs.vecSub)(flat.center, axisOrigin), out) >= 0 ? out : (0, brepjs.vecScale)(out, -1);
2045
+ const toCenter = (0, brepjs.vecSub)(flat.center, axisOrigin);
2046
+ return (0, brepjs.vecDot)(toCenter, out) >= 0 ? out : (0, brepjs.vecScale)(out, -1);
2039
2047
  }
2040
2048
  /**
2041
2049
  * The child corner that develops onto the strip-far near point: minimum along the
@@ -2161,13 +2169,13 @@ function bendAxialExtent(bend) {
2161
2169
  * parent flat's mid-surface, 'down' otherwise.
2162
2170
  */
2163
2171
  function bendDirection(bend, parent) {
2164
- return (0, brepjs.vecDot)((0, brepjs.vecSub)(bend.fit.axisOrigin, parent.center), parent.normal) >= 0 ? "up" : "down";
2172
+ const toAxis = (0, brepjs.vecSub)(bend.fit.axisOrigin, parent.center);
2173
+ return (0, brepjs.vecDot)(toAxis, parent.normal) >= 0 ? "up" : "down";
2165
2174
  }
2166
2175
  function faceNormal(face) {
2167
2176
  return (0, brepjs.vecNormalize)((0, brepjs.normalAt)(face, (0, brepjs.faceCenter)(face)));
2168
2177
  }
2169
- //#endregion
2170
- //#region src/authorFns.ts
2178
+ // src/authorFns.ts
2171
2179
  /**
2172
2180
  * Stable edge reference for flange attachment. The base flat is `face-0`; every
2173
2181
  * flange face is `face-<n+1>` in authoring order. The leading edge of a flat is
@@ -2495,10 +2503,10 @@ function parentEdge(f, side) {
2495
2503
  length = f.uLen;
2496
2504
  a = o;
2497
2505
  b = uTop;
2498
- break;
2499
2506
  }
2500
2507
  const dir = (0, brepjs.vecNormalize)((0, brepjs.vecCross)(f.n, out));
2501
- const start = (0, brepjs.vecDot)((0, brepjs.vecSub)(b, a), dir) >= 0 ? a : b;
2508
+ const toB = (0, brepjs.vecSub)(b, a);
2509
+ const start = (0, brepjs.vecDot)(toB, dir) >= 0 ? a : b;
2502
2510
  return {
2503
2511
  dir,
2504
2512
  out,
@@ -2611,14 +2619,15 @@ function frameTransform$1(axisT, runT, nT, edgeStart, offset) {
2611
2619
  const applyR = (v) => (0, brepjs.vecAdd)((0, brepjs.vecAdd)((0, brepjs.vecScale)(runT, v[0]), (0, brepjs.vecScale)(axisT, v[1])), (0, brepjs.vecScale)(nT, v[2]));
2612
2620
  return {
2613
2621
  solid: (s) => {
2614
- return (0, brepjs.translate)(aa.angleDeg === 0 ? s : (0, brepjs.rotate)(s, aa.angleDeg, {
2622
+ const rotated = aa.angleDeg === 0 ? s : (0, brepjs.rotate)(s, aa.angleDeg, {
2615
2623
  at: [
2616
2624
  0,
2617
2625
  0,
2618
2626
  0
2619
2627
  ],
2620
2628
  axis: aa.axis
2621
- }), target);
2629
+ });
2630
+ return (0, brepjs.translate)(rotated, target);
2622
2631
  },
2623
2632
  point: (p) => (0, brepjs.vecAdd)(applyR(p), target),
2624
2633
  vector: applyR
@@ -2680,12 +2689,13 @@ function matrixToAxisAngle$2(m) {
2680
2689
  };
2681
2690
  }
2682
2691
  const denom = 2 * Math.sin(angle);
2692
+ const axis = [
2693
+ (m21 - m12) / denom,
2694
+ (m02 - m20) / denom,
2695
+ (m10 - m01) / denom
2696
+ ];
2683
2697
  return {
2684
- axis: (0, brepjs.vecNormalize)([
2685
- (m21 - m12) / denom,
2686
- (m02 - m20) / denom,
2687
- (m10 - m01) / denom
2688
- ]),
2698
+ axis: (0, brepjs.vecNormalize)(axis),
2689
2699
  angleDeg
2690
2700
  };
2691
2701
  }
@@ -2707,14 +2717,15 @@ function buildBendPatch$1(axisOrigin, thetaDeg, r, width, thickness, direction)
2707
2717
  });
2708
2718
  let tube = outer;
2709
2719
  if (r > 1e-9) {
2710
- const carved = (0, brepjs.cut)(outer, (0, brepjs.cylinder)(r, width, {
2720
+ const inner = (0, brepjs.cylinder)(r, width, {
2711
2721
  at: axisOrigin,
2712
2722
  axis: [
2713
2723
  0,
2714
2724
  1,
2715
2725
  0
2716
2726
  ]
2717
- }));
2727
+ });
2728
+ const carved = (0, brepjs.cut)(outer, inner);
2718
2729
  if (!carved.ok) return carved;
2719
2730
  tube = carved.value;
2720
2731
  }
@@ -2732,39 +2743,47 @@ function buildBendPatch$1(axisOrigin, thetaDeg, r, width, thickness, direction)
2732
2743
  function buildWedge$1(axisOrigin, thetaDeg, outerR, width, direction) {
2733
2744
  const span = outerR * 2 + 2;
2734
2745
  const margin = 1;
2735
- const halfA = (0, brepjs.translate)((0, brepjs.box)(span, width + 2 * margin, 2 * span), [
2746
+ const blockA = (0, brepjs.box)(span, width + 2, 2 * span);
2747
+ const halfA = (0, brepjs.translate)(blockA, [
2736
2748
  axisOrigin[0],
2737
2749
  axisOrigin[1] - margin,
2738
2750
  axisOrigin[2] - span
2739
2751
  ]);
2740
- return (0, brepjs.intersect)(halfA, (0, brepjs.rotate)(halfA, (direction === "up" ? 1 : -1) * (180 - thetaDeg), {
2752
+ const halfB = (0, brepjs.rotate)(halfA, (direction === "up" ? 1 : -1) * (180 - thetaDeg), {
2741
2753
  at: axisOrigin,
2742
2754
  axis: [
2743
2755
  0,
2744
2756
  1,
2745
2757
  0
2746
2758
  ]
2747
- }));
2759
+ });
2760
+ return (0, brepjs.intersect)(halfA, halfB);
2748
2761
  }
2749
2762
  /** The flange flat, built lying along +X past the bend, then folded to angle θ. */
2750
2763
  function buildFlangeFlat(axisOrigin, thetaDeg, width, thickness, length, direction) {
2751
- if (direction === "up") return (0, brepjs.rotate)((0, brepjs.translate)((0, brepjs.box)(length, width, thickness), [
2752
- axisOrigin[0],
2753
- axisOrigin[1],
2754
- 0
2755
- ]), -thetaDeg, {
2756
- at: axisOrigin,
2757
- axis: [
2758
- 0,
2759
- 1,
2764
+ if (direction === "up") {
2765
+ const flat = (0, brepjs.box)(length, width, thickness);
2766
+ const positioned = (0, brepjs.translate)(flat, [
2767
+ axisOrigin[0],
2768
+ axisOrigin[1],
2760
2769
  0
2761
- ]
2762
- });
2763
- return (0, brepjs.rotate)((0, brepjs.translate)((0, brepjs.box)(length, width, thickness), [
2770
+ ]);
2771
+ return (0, brepjs.rotate)(positioned, -thetaDeg, {
2772
+ at: axisOrigin,
2773
+ axis: [
2774
+ 0,
2775
+ 1,
2776
+ 0
2777
+ ]
2778
+ });
2779
+ }
2780
+ const flat = (0, brepjs.box)(length, width, thickness);
2781
+ const positioned = (0, brepjs.translate)(flat, [
2764
2782
  axisOrigin[0],
2765
2783
  axisOrigin[1],
2766
2784
  -thickness
2767
- ]), thetaDeg, {
2785
+ ]);
2786
+ return (0, brepjs.rotate)(positioned, thetaDeg, {
2768
2787
  at: axisOrigin,
2769
2788
  axis: [
2770
2789
  0,
@@ -2773,8 +2792,7 @@ function buildFlangeFlat(axisOrigin, thetaDeg, width, thickness, length, directi
2773
2792
  ]
2774
2793
  });
2775
2794
  }
2776
- //#endregion
2777
- //#region src/reliefFns.ts
2795
+ // src/reliefFns.ts
2778
2796
  var EPS$6 = 1e-6;
2779
2797
  var Z_AXIS$1 = [
2780
2798
  0,
@@ -3048,7 +3066,8 @@ function cornerPoint$1(originA, dirA, originB, dirB) {
3048
3066
  const e = (0, brepjs.vecDot)(dirB, w0);
3049
3067
  const denom = a * c - b * b;
3050
3068
  if (Math.abs(denom) < 1e-9) return originA;
3051
- return (0, brepjs.vecAdd)(originA, (0, brepjs.vecScale)(dirA, (b * e - c * d) / denom));
3069
+ const sc = (b * e - c * d) / denom;
3070
+ return (0, brepjs.vecAdd)(originA, (0, brepjs.vecScale)(dirA, sc));
3052
3071
  }
3053
3072
  /** In-plane perpendicular to `axis` pointing toward the part center (into the parent). */
3054
3073
  function inwardRun(axisOrigin, axis, center) {
@@ -3081,11 +3100,12 @@ function parentEdgeLength(part, flange) {
3081
3100
  */
3082
3101
  function slotTool3d(at, along, inward, width, depth, thickness) {
3083
3102
  const margin = thickness + 2;
3084
- const y0 = (0, brepjs.vecDot)([
3103
+ const rotPerp = [
3085
3104
  -along[1],
3086
3105
  along[0],
3087
3106
  0
3088
- ], inward) >= 0 ? 0 : -depth;
3107
+ ];
3108
+ const y0 = (0, brepjs.vecDot)(rotPerp, inward) >= 0 ? 0 : -depth;
3089
3109
  let tool = (0, brepjs.box)(width, depth, thickness + 2 * margin);
3090
3110
  tool = (0, brepjs.translate)(tool, [
3091
3111
  -width / 2,
@@ -3110,7 +3130,8 @@ function squareTool(solid, cornerXY, side) {
3110
3130
  const b = (0, brepjs.getBounds)(solid);
3111
3131
  const margin = 2;
3112
3132
  const h = side / 2;
3113
- return (0, brepjs.translate)((0, brepjs.box)(side, side, b.zMax - b.zMin + 2 * margin), [
3133
+ const tool = (0, brepjs.box)(side, side, b.zMax - b.zMin + 4);
3134
+ return (0, brepjs.translate)(tool, [
3114
3135
  cornerXY[0] - h,
3115
3136
  cornerXY[1] - h,
3116
3137
  b.zMin - margin
@@ -3135,8 +3156,7 @@ function toPt2$1(v) {
3135
3156
  function dist2(a, b) {
3136
3157
  return Math.hypot(a[0] - b[0], a[1] - b[1]);
3137
3158
  }
3138
- //#endregion
3139
- //#region src/cutoutFns.ts
3159
+ // src/cutoutFns.ts
3140
3160
  var EPS$5 = 1e-6;
3141
3161
  /** A hair of over-cut so the tool pokes through both faces cleanly. */
3142
3162
  var HAIR$1 = .05;
@@ -3364,8 +3384,7 @@ function polygonArea(loop) {
3364
3384
  }
3365
3385
  return Math.abs(sum) / 2;
3366
3386
  }
3367
- //#endregion
3368
- //#region src/tabFns.ts
3387
+ // src/tabFns.ts
3369
3388
  var EPS$4 = 1e-6;
3370
3389
  /**
3371
3390
  * Fuse a rectangular tab onto a named flat region's edge — additive material (the
@@ -3507,8 +3526,7 @@ function devRect(local, dev) {
3507
3526
  y1
3508
3527
  ];
3509
3528
  }
3510
- //#endregion
3511
- //#region src/formFns.ts
3529
+ // src/formFns.ts
3512
3530
  var EPS$3 = 1e-6;
3513
3531
  var HAIR = .05;
3514
3532
  var CIRCLE_SEGMENTS = 48;
@@ -3603,18 +3621,20 @@ function embossForm(part, partSolid, spec, regionId, world, dev, ext) {
3603
3621
  let solid;
3604
3622
  if (spec.form === "emboss") {
3605
3623
  const base = (0, brepjs.vecAdd)(faceCentre, (0, brepjs.vecScale)(world.n, -.05));
3606
- const fused = (0, brepjs.fuse)(partSolid, (0, brepjs.cylinder)(r, spec.height + HAIR, {
3624
+ const bump = (0, brepjs.cylinder)(r, spec.height + HAIR, {
3607
3625
  at: base,
3608
3626
  axis: world.n
3609
- }));
3627
+ });
3628
+ const fused = (0, brepjs.fuse)(partSolid, bump);
3610
3629
  if (!fused.ok) return fused;
3611
3630
  solid = normalizeSolid(fused.value);
3612
3631
  } else {
3613
3632
  const top = (0, brepjs.vecAdd)(faceCentre, (0, brepjs.vecScale)(world.n, HAIR));
3614
- const cutResult = (0, brepjs.cut)(partSolid, (0, brepjs.cylinder)(r, spec.height + HAIR, {
3633
+ const tool = (0, brepjs.cylinder)(r, spec.height + HAIR, {
3615
3634
  at: top,
3616
3635
  axis: (0, brepjs.vecScale)(world.n, -1)
3617
- }));
3636
+ });
3637
+ const cutResult = (0, brepjs.cut)(partSolid, tool);
3618
3638
  if (!cutResult.ok) return cutResult;
3619
3639
  solid = normalizeSolid(cutResult.value);
3620
3640
  }
@@ -3706,7 +3726,8 @@ function buildLouverFlap(f, x0, x1, y0, width, height, thickness, sign) {
3706
3726
  const n = (0, brepjs.vecNormalize)(f.n);
3707
3727
  const v = (0, brepjs.vecNormalize)((0, brepjs.vecCross)(n, u));
3708
3728
  const hingeCorner = (0, brepjs.vecAdd)((0, brepjs.vecAdd)((0, brepjs.vecAdd)(f.origin, (0, brepjs.vecScale)(f.u, x0)), (0, brepjs.vecScale)(f.v, y0)), (0, brepjs.vecScale)(f.n, thickness));
3709
- return (0, brepjs.ok)(mapLocalToWorld(flap, u, v, n, hingeCorner));
3729
+ const placed = mapLocalToWorld(flap, u, v, n, hingeCorner);
3730
+ return (0, brepjs.ok)(placed);
3710
3731
  }
3711
3732
  /** Map a solid built in a canonical XYZ frame onto world axes (uT, vT, nT) at origin. */
3712
3733
  function mapLocalToWorld(s, uT, vT, nT, origin) {
@@ -3721,14 +3742,15 @@ function mapLocalToWorld(s, uT, vT, nT, origin) {
3721
3742
  vT[2],
3722
3743
  nT[2]
3723
3744
  ]);
3724
- return (0, brepjs.translate)(aa.angleDeg === 0 ? s : (0, brepjs.rotate)(s, aa.angleDeg, {
3745
+ const rotated = aa.angleDeg === 0 ? s : (0, brepjs.rotate)(s, aa.angleDeg, {
3725
3746
  at: [
3726
3747
  0,
3727
3748
  0,
3728
3749
  0
3729
3750
  ],
3730
3751
  axis: aa.axis
3731
- }), origin);
3752
+ });
3753
+ return (0, brepjs.translate)(rotated, origin);
3732
3754
  }
3733
3755
  /** Axis-angle of a 3×3 row-major rotation matrix (columns = target basis). */
3734
3756
  function matrixToAxisAngle$1(m) {
@@ -3786,12 +3808,13 @@ function matrixToAxisAngle$1(m) {
3786
3808
  };
3787
3809
  }
3788
3810
  const denom = 2 * Math.sin(angle);
3811
+ const axis = [
3812
+ (m21 - m12) / denom,
3813
+ (m02 - m20) / denom,
3814
+ (m10 - m01) / denom
3815
+ ];
3789
3816
  return {
3790
- axis: (0, brepjs.vecNormalize)([
3791
- (m21 - m12) / denom,
3792
- (m02 - m20) / denom,
3793
- (m10 - m01) / denom
3794
- ]),
3817
+ axis: (0, brepjs.vecNormalize)(axis),
3795
3818
  angleDeg
3796
3819
  };
3797
3820
  }
@@ -3804,8 +3827,7 @@ function circleLocal(c, r) {
3804
3827
  }
3805
3828
  return pts;
3806
3829
  }
3807
- //#endregion
3808
- //#region src/validateFns.ts
3830
+ // src/validateFns.ts
3809
3831
  var OVERLAP_TOL = 1e-6;
3810
3832
  /**
3811
3833
  * Manufacturability checks for an authored sheet-metal part. Returns a list of
@@ -3987,8 +4009,7 @@ function pairKey(a, b) {
3987
4009
  function boundsOverlap(a, b) {
3988
4010
  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;
3989
4011
  }
3990
- //#endregion
3991
- //#region src/foldFns.ts
4012
+ // src/foldFns.ts
3992
4013
  /**
3993
4014
  * Fold a flat pattern up into a 3D part. Each {@link FoldRegion} folds about its
3994
4015
  * fold line off its parent region, exactly inverting {@link unfold}: folding by an
@@ -4646,8 +4667,7 @@ function uniqueSorted(values) {
4646
4667
  }
4647
4668
  return out;
4648
4669
  }
4649
- //#endregion
4650
- //#region src/miterFns.ts
4670
+ // src/miterFns.ts
4651
4671
  /**
4652
4672
  * General miter primitive: cut a sheet-metal part by a plane, removing the
4653
4673
  * material on the `+normal` side. The tool is a half-space block sized to the
@@ -4706,7 +4726,8 @@ function cornerPoint(originA, dirA, originB, dirB) {
4706
4726
  const e = (0, brepjs.vecDot)(dirB, w0);
4707
4727
  const denom = a * c - b * b;
4708
4728
  if (Math.abs(denom) < 1e-9) return originA;
4709
- return (0, brepjs.vecAdd)(originA, (0, brepjs.vecScale)(dirA, (b * e - c * d) / denom));
4729
+ const sc = (b * e - c * d) / denom;
4730
+ return (0, brepjs.vecAdd)(originA, (0, brepjs.vecScale)(dirA, sc));
4710
4731
  }
4711
4732
  /**
4712
4733
  * Normal of the miter plane between two flanges whose bend axes are `dirA`/`dirB`.
@@ -4738,13 +4759,14 @@ function halfSpaceTool(solid, plane) {
4738
4759
  -size / 2,
4739
4760
  0
4740
4761
  ]);
4741
- return (0, brepjs.ok)((0, brepjs.translate)(alignZTo(tool, normal), plane.origin));
4762
+ const aligned = alignZTo(tool, normal);
4763
+ return (0, brepjs.ok)((0, brepjs.translate)(aligned, plane.origin));
4742
4764
  }
4743
4765
  /** Rotate a shape so its local +Z axis points along `target` (about the origin). */
4744
4766
  function alignZTo(shape, target) {
4745
4767
  const dot = (0, brepjs.vecDot)(Z_AXIS, target);
4746
- if (dot > .999999999) return shape;
4747
- if (dot < -.999999999) return (0, brepjs.rotate)(shape, 180, {
4768
+ if (dot > 1 - 1e-9) return shape;
4769
+ if (dot < -1 + 1e-9) return (0, brepjs.rotate)(shape, 180, {
4748
4770
  at: [
4749
4771
  0,
4750
4772
  0,
@@ -4757,7 +4779,8 @@ function alignZTo(shape, target) {
4757
4779
  ]
4758
4780
  });
4759
4781
  const axis = (0, brepjs.vecNormalize)((0, brepjs.vecCross)(Z_AXIS, target));
4760
- return (0, brepjs.rotate)(shape, Math.acos(Math.max(-1, Math.min(1, dot))) * 180 / Math.PI, {
4782
+ const angleDeg = Math.acos(Math.max(-1, Math.min(1, dot))) * 180 / Math.PI;
4783
+ return (0, brepjs.rotate)(shape, angleDeg, {
4761
4784
  at: [
4762
4785
  0,
4763
4786
  0,
@@ -4766,8 +4789,7 @@ function alignZTo(shape, target) {
4766
4789
  axis
4767
4790
  });
4768
4791
  }
4769
- //#endregion
4770
- //#region src/contourFlangeFns.ts
4792
+ // src/contourFlangeFns.ts
4771
4793
  /**
4772
4794
  * Author a contour flange: an OPEN 2D profile (alternating line/arc segments) swept
4773
4795
  * along one straight edge of the base flat. Each arc is a cylindrical bend, each
@@ -4888,7 +4910,8 @@ function authorContourFlange(part, spec) {
4888
4910
  function buildLineLeg(base, frame, span, thickness, legLength) {
4889
4911
  const place = frameTransform(frame.run, frame.axis, frame.n, frame.origin);
4890
4912
  const canon = (0, brepjs.box)(legLength, span, thickness);
4891
- const fused = (0, brepjs.fuse)(base, place.solid(canon));
4913
+ const placed = place.solid(canon);
4914
+ const fused = (0, brepjs.fuse)(base, placed);
4892
4915
  if (!fused.ok) return fused;
4893
4916
  const nextOrigin = (0, brepjs.vecAdd)(frame.origin, (0, brepjs.vecScale)(frame.run, legLength));
4894
4917
  return (0, brepjs.ok)({
@@ -4920,7 +4943,8 @@ function buildArcBend(base, frame, span, thickness, seg) {
4920
4943
  const place = frameTransform(frame.run, frame.axis, frame.n, frame.origin);
4921
4944
  const patch = buildBendPatch(canonOrigin, seg.angleDeg, r, span, thickness, seg.direction);
4922
4945
  if (!patch.ok) return patch;
4923
- const fused = (0, brepjs.fuse)(base, place.solid(patch.value));
4946
+ const placed = place.solid(patch.value);
4947
+ const fused = (0, brepjs.fuse)(base, placed);
4924
4948
  if (!fused.ok) return fused;
4925
4949
  const theta = seg.angleDeg * Math.PI / 180;
4926
4950
  const a = -sign * theta;
@@ -4977,14 +5001,15 @@ function frameTransform(runT, axisT, nT, origin) {
4977
5001
  const applyR = (v) => (0, brepjs.vecAdd)((0, brepjs.vecAdd)((0, brepjs.vecScale)(runT, v[0]), (0, brepjs.vecScale)(axisT, v[1])), (0, brepjs.vecScale)(nT, v[2]));
4978
5002
  return {
4979
5003
  solid: (s) => {
4980
- return (0, brepjs.translate)(aa.angleDeg === 0 ? s : (0, brepjs.rotate)(s, aa.angleDeg, {
5004
+ const rotated = aa.angleDeg === 0 ? s : (0, brepjs.rotate)(s, aa.angleDeg, {
4981
5005
  at: [
4982
5006
  0,
4983
5007
  0,
4984
5008
  0
4985
5009
  ],
4986
5010
  axis: aa.axis
4987
- }), origin);
5011
+ });
5012
+ return (0, brepjs.translate)(rotated, origin);
4988
5013
  },
4989
5014
  point: (p) => (0, brepjs.vecAdd)(applyR(p), origin),
4990
5015
  vector: applyR
@@ -5045,10 +5070,10 @@ function baseEdge(part, side) {
5045
5070
  length = uLen;
5046
5071
  a = o;
5047
5072
  b = uTop;
5048
- break;
5049
5073
  }
5050
5074
  const dir = (0, brepjs.vecNormalize)((0, brepjs.vecCross)(n, out));
5051
- const start = (0, brepjs.vecDot)((0, brepjs.vecSub)(b, a), dir) >= 0 ? a : b;
5075
+ const toB = (0, brepjs.vecSub)(b, a);
5076
+ const start = (0, brepjs.vecDot)(toB, dir) >= 0 ? a : b;
5052
5077
  return {
5053
5078
  dir,
5054
5079
  out,
@@ -5095,10 +5120,10 @@ function regionEdge(frame, side) {
5095
5120
  length = frame.uLen;
5096
5121
  a = o;
5097
5122
  b = uTop;
5098
- break;
5099
5123
  }
5100
5124
  const dir = (0, brepjs.vecNormalize)((0, brepjs.vecCross)(frame.n, out));
5101
- const start = (0, brepjs.vecDot)((0, brepjs.vecSub)(b, a), dir) >= 0 ? a : b;
5125
+ const toB = (0, brepjs.vecSub)(b, a);
5126
+ const start = (0, brepjs.vecDot)(toB, dir) >= 0 ? a : b;
5102
5127
  return {
5103
5128
  dir,
5104
5129
  out,
@@ -5132,14 +5157,15 @@ function buildBendPatch(axisOrigin, thetaDeg, r, width, thickness, direction) {
5132
5157
  });
5133
5158
  let tube = outer;
5134
5159
  if (r > 1e-9) {
5135
- const carved = (0, brepjs.cut)(outer, (0, brepjs.cylinder)(r, width, {
5160
+ const inner = (0, brepjs.cylinder)(r, width, {
5136
5161
  at: axisOrigin,
5137
5162
  axis: [
5138
5163
  0,
5139
5164
  1,
5140
5165
  0
5141
5166
  ]
5142
- }));
5167
+ });
5168
+ const carved = (0, brepjs.cut)(outer, inner);
5143
5169
  if (!carved.ok) return carved;
5144
5170
  tube = carved.value;
5145
5171
  }
@@ -5150,19 +5176,21 @@ function buildBendPatch(axisOrigin, thetaDeg, r, width, thickness, direction) {
5150
5176
  function buildWedge(axisOrigin, thetaDeg, outerR, width, direction) {
5151
5177
  const span = outerR * 2 + 2;
5152
5178
  const margin = 1;
5153
- const halfA = (0, brepjs.translate)((0, brepjs.box)(span, width + 2 * margin, 2 * span), [
5179
+ const blockA = (0, brepjs.box)(span, width + 2, 2 * span);
5180
+ const halfA = (0, brepjs.translate)(blockA, [
5154
5181
  axisOrigin[0],
5155
5182
  axisOrigin[1] - margin,
5156
5183
  axisOrigin[2] - span
5157
5184
  ]);
5158
- return (0, brepjs.intersect)(halfA, (0, brepjs.rotate)(halfA, (direction === "up" ? 1 : -1) * (180 - thetaDeg), {
5185
+ const halfB = (0, brepjs.rotate)(halfA, (direction === "up" ? 1 : -1) * (180 - thetaDeg), {
5159
5186
  at: axisOrigin,
5160
5187
  axis: [
5161
5188
  0,
5162
5189
  1,
5163
5190
  0
5164
5191
  ]
5165
- }));
5192
+ });
5193
+ return (0, brepjs.intersect)(halfA, halfB);
5166
5194
  }
5167
5195
  /** Axis-angle of a 3×3 rotation matrix (row-major). */
5168
5196
  function matrixToAxisAngle(m) {
@@ -5220,17 +5248,17 @@ function matrixToAxisAngle(m) {
5220
5248
  };
5221
5249
  }
5222
5250
  const denom = 2 * Math.sin(angle);
5251
+ const axis = [
5252
+ (m21 - m12) / denom,
5253
+ (m02 - m20) / denom,
5254
+ (m10 - m01) / denom
5255
+ ];
5223
5256
  return {
5224
- axis: (0, brepjs.vecNormalize)([
5225
- (m21 - m12) / denom,
5226
- (m02 - m20) / denom,
5227
- (m10 - m01) / denom
5228
- ]),
5257
+ axis: (0, brepjs.vecNormalize)(axis),
5229
5258
  angleDeg
5230
5259
  };
5231
5260
  }
5232
- //#endregion
5233
- //#region src/loftedFlangeFns.ts
5261
+ // src/loftedFlangeFns.ts
5234
5262
  /**
5235
5263
  * Relative out-of-plane tolerance for the per-quad developability test: a quad is
5236
5264
  * twisted (non-developable) when its far corner sits off the diagonal-triangle plane
@@ -5350,10 +5378,13 @@ function buildQuadPlate(id, a0, a1, b1, b0, thickness) {
5350
5378
  }
5351
5379
  /** A flat triangle (p0,p1,p2) extruded by `thickness` along its own normal. */
5352
5380
  function buildTrianglePlate(id, p0, p1, p2, thickness) {
5381
+ const e0 = (0, brepjs.line)(p0, p1);
5382
+ const e1 = (0, brepjs.line)(p1, p2);
5383
+ const e2 = (0, brepjs.line)(p2, p0);
5353
5384
  const loopResult = (0, brepjs.wireLoop)([
5354
- (0, brepjs.line)(p0, p1),
5355
- (0, brepjs.line)(p1, p2),
5356
- (0, brepjs.line)(p2, p0)
5385
+ e0,
5386
+ e1,
5387
+ e2
5357
5388
  ]);
5358
5389
  if (!loopResult.ok) return loopResult;
5359
5390
  const loop = loopResult.value;
@@ -5539,8 +5570,7 @@ function closedLoopWire(loop) {
5539
5570
  }
5540
5571
  return (0, brepjs.wireLoop)(edges);
5541
5572
  }
5542
- //#endregion
5543
- //#region src/hemFns.ts
5573
+ // src/hemFns.ts
5544
5574
  /**
5545
5575
  * A small clearance baked into a CLOSED hem's curl radius. A true closed hem folds
5546
5576
  * the return flat against the parent with zero gap; modelling that exactly makes the
@@ -5733,8 +5763,7 @@ function splitAngle(totalDeg) {
5733
5763
  const per = totalDeg / n;
5734
5764
  return Array.from({ length: n }, () => per);
5735
5765
  }
5736
- //#endregion
5737
- //#region src/jogFns.ts
5766
+ // src/jogFns.ts
5738
5767
  /**
5739
5768
  * Author a jog (joggle): two opposite bends (`+θ` then `−θ`) that step a flat by
5740
5769
  * `offsetHeight` perpendicular to its plane, then continue parallel past the step.
@@ -5893,8 +5922,7 @@ function jogId(spec) {
5893
5922
  function resolveRegion(region) {
5894
5923
  return region === "base" || region === "face-0" ? ROOT_FLAT_ID : region;
5895
5924
  }
5896
- //#endregion
5897
- //#region src/dxfFns.ts
5925
+ // src/dxfFns.ts
5898
5926
  var DEFAULT_TEXT_HEIGHT = 2.5;
5899
5927
  var LAYER_OUTLINE = "OUTLINE";
5900
5928
  var LAYER_BEND_UP = "BEND_UP";
@@ -6103,8 +6131,7 @@ function pathPoints(wire) {
6103
6131
  function toPt2(v) {
6104
6132
  return [v[0], v[1]];
6105
6133
  }
6106
- //#endregion
6107
- //#region src/polygonFns.ts
6134
+ // src/polygonFns.ts
6108
6135
  /**
6109
6136
  * 2D simple-polygon geometry for true-shape (no-fit-polygon) nesting: an ordered
6110
6137
  * vertex loop plus the overlap predicate that is the correctness backbone of the
@@ -6347,8 +6374,7 @@ function pointSegmentDistance(p, a, b) {
6347
6374
  const dy = p[1] - (a[1] + t * vy);
6348
6375
  return Math.hypot(dx, dy);
6349
6376
  }
6350
- //#endregion
6351
- //#region src/nestFns.ts
6377
+ // src/nestFns.ts
6352
6378
  var EPS = 1e-9;
6353
6379
  /**
6354
6380
  * Bounding-box nest: arrange developed flat patterns onto stock sheets to reduce
@@ -6814,8 +6840,7 @@ function nestToDXF$1(result, patterns, sheetIndex, options) {
6814
6840
  }
6815
6841
  return multiPatternToDXF(placed, options ?? {});
6816
6842
  }
6817
- //#endregion
6818
- //#region src/reportFns.ts
6843
+ // src/reportFns.ts
6819
6844
  /**
6820
6845
  * Build the bend-table JSON report directly from an authored part. Walks the
6821
6846
  * same tree layout the unfold consumes so the per-bend values and the total flat
@@ -6858,8 +6883,7 @@ function reportFromUnfold(result) {
6858
6883
  function reportToJSON(report) {
6859
6884
  return JSON.stringify(report, null, 2);
6860
6885
  }
6861
- //#endregion
6862
- //#region src/api.ts
6886
+ // src/api.ts
6863
6887
  /** Author a straight-bend part: a base flat plus folded-up flanges. */
6864
6888
  function author(spec) {
6865
6889
  return authorPart(spec);
@@ -7021,8 +7045,7 @@ function bendTable(id) {
7021
7045
  function resolveAllowance(rule, angleDeg, thickness, onWarning) {
7022
7046
  return resolveBendAllowance(rule, angleDeg, thickness, onWarning);
7023
7047
  }
7024
- //#endregion
7025
- //#region src/facade.ts
7048
+ // src/facade.ts
7026
7049
  /** Thrown by the fluent facade when an underlying `Result<T>` is an `Err`. */
7027
7050
  var SheetMetalError = class extends Error {
7028
7051
  code;
@@ -7292,7 +7315,6 @@ function foldFlat(input) {
7292
7315
  function fromSolid(solid, opts) {
7293
7316
  return new ForeignSolidHandle(solid, opts);
7294
7317
  }
7295
- //#endregion
7296
7318
  exports.ALUMINUM_GAUGES = ALUMINUM_GAUGES;
7297
7319
  exports.DEFAULT_K_FACTOR = DEFAULT_K_FACTOR;
7298
7320
  exports.ForeignSolidHandle = ForeignSolidHandle;