brepjs 20.1.5 → 20.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/2d.cjs +6 -6
  2. package/dist/2d.js +6 -6
  3. package/dist/{adjacencyFns-BrfM5HuO.cjs → adjacencyFns-DZsPfYIq.cjs} +1 -1
  4. package/dist/{adjacencyFns-BKNCTFyz.js → adjacencyFns-k4GWh4n7.js} +1 -1
  5. package/dist/{blueprint-cDEPhHO0.cjs → blueprint-CJuRunkd.cjs} +6 -6
  6. package/dist/{blueprint-DQaebcah.js → blueprint-Dsp4t2ul.js} +6 -6
  7. package/dist/{blueprintFns-41EvpjiC.cjs → blueprintFns-BRceCeZp.cjs} +3 -3
  8. package/dist/{blueprintFns-DdIWHIxE.js → blueprintFns-DNyUtopT.js} +3 -3
  9. package/dist/{blueprintSketcher-DX8NeWqA.cjs → blueprintSketcher-DRXOTYuD.cjs} +3 -3
  10. package/dist/{blueprintSketcher-BnVu9r7p.js → blueprintSketcher-kV7aFwRS.js} +3 -3
  11. package/dist/{boolean2D-B0jPhKO1.cjs → boolean2D-DmPqFj_V.cjs} +5 -5
  12. package/dist/{boolean2D-Dp7Bjzvc.js → boolean2D-TdJfMS8F.js} +5 -5
  13. package/dist/brepjs.cjs +299 -78
  14. package/dist/brepjs.js +299 -78
  15. package/dist/{cameraFns-DUKz-5_d.cjs → cameraFns-B8Ai6b3Q.cjs} +3 -3
  16. package/dist/{cameraFns-CcgzxZ-9.js → cameraFns-u08iOn7p.js} +3 -3
  17. package/dist/core/errors.d.ts +1 -0
  18. package/dist/core.cjs +3 -3
  19. package/dist/core.js +3 -3
  20. package/dist/{cornerFinder-B-eLajMt.cjs → cornerFinder-BVUuCBX-.cjs} +1 -1
  21. package/dist/{cornerFinder-D589drKe.js → cornerFinder-DfoRecV-.js} +1 -1
  22. package/dist/csg/builders.d.ts +13 -1
  23. package/dist/csg/evaluate.d.ts +10 -1
  24. package/dist/csg/evaluators/context.d.ts +2 -1
  25. package/dist/csg/evaluators/extension.d.ts +5 -0
  26. package/dist/csg/hash.d.ts +1 -0
  27. package/dist/csg/index.d.ts +2 -2
  28. package/dist/csg/jsonData.d.ts +8 -0
  29. package/dist/csg/serialize.d.ts +1 -1
  30. package/dist/csg/types.d.ts +50 -1
  31. package/dist/{curveFns-Q-tL9uNq.js → curveFns-D5V4jm7S.js} +1 -1
  32. package/dist/{curveFns-D-hc0_6t.cjs → curveFns-DciWOppG.cjs} +1 -1
  33. package/dist/{drawFns-CrBzPDfw.js → drawFns-BzuxoLHh.js} +13 -13
  34. package/dist/{drawFns-BGW37YtA.cjs → drawFns-DeUXFpjR.cjs} +13 -13
  35. package/dist/{errors-CTn8i3lY.js → errors-CDSezF9z.js} +1 -0
  36. package/dist/{errors-DWezb9DI.cjs → errors-gG-7hA7k.cjs} +1 -0
  37. package/dist/{faceFns-iyZwpUFe.cjs → faceFns-DaXVpMr5.cjs} +2 -2
  38. package/dist/{faceFns-DQ51W4Eg.js → faceFns-DhdweJGk.js} +2 -2
  39. package/dist/{healingFns-DlyPwsRq.cjs → healingFns-C_f03IV6.cjs} +4 -4
  40. package/dist/{healingFns-DxEg4twQ.js → healingFns-HekFOO_s.js} +4 -4
  41. package/dist/{helpers-Hna44lMW.js → helpers-BWNXoJz0.js} +6 -6
  42. package/dist/{helpers-DzuHzyWn.cjs → helpers-Dh4qTGcQ.cjs} +6 -6
  43. package/dist/{importFns-BygnKmG8.cjs → importFns-CKfKRdiq.cjs} +2 -2
  44. package/dist/{importFns-CzX0LzER.js → importFns-DGnmgb9G.js} +2 -2
  45. package/dist/io.cjs +2 -2
  46. package/dist/io.js +2 -2
  47. package/dist/{loftFns-DfF8LMRm.cjs → loftFns-CI8uJc1D.cjs} +1 -1
  48. package/dist/{loftFns-Bo6ngjcA.js → loftFns-DE1GYW9g.js} +1 -1
  49. package/dist/{measureFns-Czv8iOd1.cjs → measureFns-B8Yj_BMz.cjs} +3 -3
  50. package/dist/{measureFns-DnZsoSsE.js → measureFns-BoA-Fmwy.js} +3 -3
  51. package/dist/measurement.cjs +1 -1
  52. package/dist/measurement.js +1 -1
  53. package/dist/{meshFns-82W7072d.cjs → meshFns-BT7W4b1c.cjs} +3 -3
  54. package/dist/{meshFns-DegHraku.js → meshFns-o9MnZeir.js} +3 -3
  55. package/dist/operations.cjs +2 -2
  56. package/dist/operations.js +2 -2
  57. package/dist/{originTrackingFns-DP_yN44Z.cjs → originTrackingFns-B3-1bBED.cjs} +1 -1
  58. package/dist/{originTrackingFns-BbTSRuQj.js → originTrackingFns-BFmhk_Up.js} +1 -1
  59. package/dist/{planeOps-CkpMeheg.js → planeOps-BO6nvRja.js} +1 -1
  60. package/dist/{planeOps-CpupTCRM.cjs → planeOps-DqGSxcMr.cjs} +1 -1
  61. package/dist/{primitiveFns-D1ayK5yN.js → primitiveFns-DfhjU_X9.js} +4 -4
  62. package/dist/{primitiveFns-_Kid-gYL.cjs → primitiveFns-Ke-n8Bib.cjs} +4 -4
  63. package/dist/projection.cjs +1 -1
  64. package/dist/projection.js +1 -1
  65. package/dist/query.cjs +2 -2
  66. package/dist/query.js +2 -2
  67. package/dist/{refResolveFns-BL_6tDQG.cjs → refResolveFns-BPqbBoxy.cjs} +5 -5
  68. package/dist/{refResolveFns-BujrL8at.js → refResolveFns-C-6Xa_8k.js} +5 -5
  69. package/dist/result.cjs +1 -1
  70. package/dist/result.js +1 -1
  71. package/dist/{shapeFns-sbLSffyZ.js → shapeFns-CUTM2ZDR.js} +2 -2
  72. package/dist/{shapeFns-SdMLvQ7n.cjs → shapeFns-CmJNoPsh.cjs} +2 -2
  73. package/dist/shapeRef.cjs +1 -1
  74. package/dist/shapeRef.js +1 -1
  75. package/dist/sketching.cjs +3 -3
  76. package/dist/sketching.js +3 -3
  77. package/dist/{solidBuilders-CZdfdC4S.cjs → solidBuilders-B8t5h68e.cjs} +6 -6
  78. package/dist/{solidBuilders-BmUEjbhs.js → solidBuilders-p_ppeTkc.js} +6 -6
  79. package/dist/{surfaceBuilders-BujH-WLG.cjs → surfaceBuilders-Bn-c9xVR.cjs} +3 -3
  80. package/dist/{surfaceBuilders-DW56oYUA.js → surfaceBuilders-Dccm2kQX.js} +3 -3
  81. package/dist/text.cjs +2 -2
  82. package/dist/text.js +2 -2
  83. package/dist/{textBlueprints-BaRZlz2x.cjs → textBlueprints-DQi920gG.cjs} +8 -8
  84. package/dist/{textBlueprints-BlTo-FeX.js → textBlueprints-bFsyNLkO.js} +8 -8
  85. package/dist/{textMetrics-KJFOH0hq.js → textMetrics-CvTwh4tR.js} +2 -2
  86. package/dist/{textMetrics-C6AkkXLu.cjs → textMetrics-DhysRMr7.cjs} +2 -2
  87. package/dist/{threadFns-D-Qy8nQN.cjs → threadFns-DAKMitvE.cjs} +7 -7
  88. package/dist/{threadFns-CRq0YEAj.js → threadFns-DlGXBSK7.js} +7 -7
  89. package/dist/topology.cjs +9 -9
  90. package/dist/topology.js +9 -9
  91. package/dist/{topologyQueryFns-DbQBANFg.cjs → topologyQueryFns-CI4j3NTE.cjs} +1 -1
  92. package/dist/{topologyQueryFns-C8Dv3AdW.js → topologyQueryFns-fNWsM4Lr.js} +1 -1
  93. package/dist/{validityTypes-D6M4CiEI.js → validityTypes-2DNKAPEw.js} +1 -1
  94. package/dist/{validityTypes-4nhcQAdO.cjs → validityTypes-D9oxhu4U.cjs} +1 -1
  95. package/dist/vectors.cjs +1 -1
  96. package/dist/vectors.js +1 -1
  97. package/package.json +1 -1
package/dist/brepjs.cjs CHANGED
@@ -3,39 +3,39 @@ const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
3
3
  const require_shapeTypes = require("./shapeTypes-BWsKDxzf.cjs");
4
4
  const require_occtWasmAdapter = require("./occtWasmAdapter-wCKnmevS.cjs");
5
5
  const require_bug = require("./bug-O02CsAmt.cjs");
6
- const require_errors = require("./errors-DWezb9DI.cjs");
7
- const require_topologyQueryFns = require("./topologyQueryFns-DbQBANFg.cjs");
8
- const require_validityTypes = require("./validityTypes-4nhcQAdO.cjs");
6
+ const require_errors = require("./errors-gG-7hA7k.cjs");
7
+ const require_topologyQueryFns = require("./topologyQueryFns-CI4j3NTE.cjs");
8
+ const require_validityTypes = require("./validityTypes-D9oxhu4U.cjs");
9
9
  const require_constants = require("./constants-BOVyEYGH.cjs");
10
10
  const require_types = require("./types-KjA8tY4Y.cjs");
11
11
  const require_vecOps = require("./vecOps-CCnJt-yH.cjs");
12
- const require_planeOps = require("./planeOps-CpupTCRM.cjs");
13
- const require_faceFns = require("./faceFns-iyZwpUFe.cjs");
14
- const require_originTrackingFns = require("./originTrackingFns-DP_yN44Z.cjs");
15
- const require_solidBuilders = require("./solidBuilders-CZdfdC4S.cjs");
16
- const require_primitiveFns = require("./primitiveFns-_Kid-gYL.cjs");
17
- const require_shapeFns = require("./shapeFns-SdMLvQ7n.cjs");
18
- const require_curveFns = require("./curveFns-D-hc0_6t.cjs");
19
- const require_meshFns = require("./meshFns-82W7072d.cjs");
12
+ const require_planeOps = require("./planeOps-DqGSxcMr.cjs");
13
+ const require_faceFns = require("./faceFns-DaXVpMr5.cjs");
14
+ const require_originTrackingFns = require("./originTrackingFns-B3-1bBED.cjs");
15
+ const require_solidBuilders = require("./solidBuilders-B8t5h68e.cjs");
16
+ const require_primitiveFns = require("./primitiveFns-Ke-n8Bib.cjs");
17
+ const require_shapeFns = require("./shapeFns-CmJNoPsh.cjs");
18
+ const require_curveFns = require("./curveFns-DciWOppG.cjs");
19
+ const require_meshFns = require("./meshFns-BT7W4b1c.cjs");
20
20
  const require_arrayAccess = require("./arrayAccess-e4H9cBfh.cjs");
21
- const require_surfaceBuilders = require("./surfaceBuilders-BujH-WLG.cjs");
22
- const require_healingFns = require("./healingFns-DlyPwsRq.cjs");
23
- const require_threadFns = require("./threadFns-D-Qy8nQN.cjs");
24
- const require_blueprintSketcher = require("./blueprintSketcher-DX8NeWqA.cjs");
25
- const require_helpers = require("./helpers-DzuHzyWn.cjs");
26
- const require_blueprint = require("./blueprint-cDEPhHO0.cjs");
27
- const require_drawFns = require("./drawFns-BGW37YtA.cjs");
28
- const require_measureFns = require("./measureFns-Czv8iOd1.cjs");
29
- const require_cornerFinder = require("./cornerFinder-B-eLajMt.cjs");
30
- const require_boolean2D = require("./boolean2D-B0jPhKO1.cjs");
31
- const require_blueprintFns = require("./blueprintFns-41EvpjiC.cjs");
32
- const require_importFns = require("./importFns-BygnKmG8.cjs");
33
- const require_loftFns = require("./loftFns-DfF8LMRm.cjs");
34
- const require_textBlueprints = require("./textBlueprints-BaRZlz2x.cjs");
35
- const require_refResolveFns = require("./refResolveFns-BL_6tDQG.cjs");
36
- const require_adjacencyFns = require("./adjacencyFns-BrfM5HuO.cjs");
37
- const require_cameraFns = require("./cameraFns-DUKz-5_d.cjs");
38
- const require_textMetrics = require("./textMetrics-C6AkkXLu.cjs");
21
+ const require_surfaceBuilders = require("./surfaceBuilders-Bn-c9xVR.cjs");
22
+ const require_healingFns = require("./healingFns-C_f03IV6.cjs");
23
+ const require_threadFns = require("./threadFns-DAKMitvE.cjs");
24
+ const require_blueprintSketcher = require("./blueprintSketcher-DRXOTYuD.cjs");
25
+ const require_helpers = require("./helpers-Dh4qTGcQ.cjs");
26
+ const require_blueprint = require("./blueprint-CJuRunkd.cjs");
27
+ const require_drawFns = require("./drawFns-DeUXFpjR.cjs");
28
+ const require_measureFns = require("./measureFns-B8Yj_BMz.cjs");
29
+ const require_cornerFinder = require("./cornerFinder-BVUuCBX-.cjs");
30
+ const require_boolean2D = require("./boolean2D-DmPqFj_V.cjs");
31
+ const require_blueprintFns = require("./blueprintFns-BRceCeZp.cjs");
32
+ const require_importFns = require("./importFns-CKfKRdiq.cjs");
33
+ const require_loftFns = require("./loftFns-CI8uJc1D.cjs");
34
+ const require_textBlueprints = require("./textBlueprints-DQi920gG.cjs");
35
+ const require_refResolveFns = require("./refResolveFns-BPqbBoxy.cjs");
36
+ const require_adjacencyFns = require("./adjacencyFns-DZsPfYIq.cjs");
37
+ const require_cameraFns = require("./cameraFns-B8Ai6b3Q.cjs");
38
+ const require_textMetrics = require("./textMetrics-DhysRMr7.cjs");
39
39
  const require_workerPool = require("./workerPool-D30NsiIW.cjs");
40
40
  //#region src/kernel/types.ts
41
41
  /** Check if the kernel supports hidden-line-removal projection. */
@@ -1174,6 +1174,14 @@ function fnvMixInt32(h, n) {
1174
1174
  }
1175
1175
  return r;
1176
1176
  }
1177
+ function fnvMixText(h, s) {
1178
+ let r = fnvMixInt32(h, s.length);
1179
+ for (let i = 0; i < s.length; i++) {
1180
+ const c = s.charCodeAt(i);
1181
+ r = fnvMixByte(fnvMixByte(r, c), c >>> 8);
1182
+ }
1183
+ return r;
1184
+ }
1177
1185
  function fnvMixBool(h, b) {
1178
1186
  return fnvMixByte(h, b ? 1 : 0);
1179
1187
  }
@@ -1522,6 +1530,130 @@ function foldSegment(s, foldExpr) {
1522
1530
  }
1523
1531
  }
1524
1532
  //#endregion
1533
+ //#region src/csg/jsonData.ts
1534
+ /** Validate `value` as JSON-safe and return its canonical frozen copy, or a
1535
+ * message naming the first offending path (rooted at `path`). */
1536
+ function canonicalJson(value, path) {
1537
+ return copyJson(value, path, /* @__PURE__ */ new Set());
1538
+ }
1539
+ function copyJson(v, path, ancestors) {
1540
+ if (v === null || typeof v === "boolean" || typeof v === "string") return require_errors.ok(v);
1541
+ if (typeof v === "number") {
1542
+ if (!Number.isFinite(v)) return require_errors.err(`${path}: ${String(v)} is not JSON-safe`);
1543
+ return require_errors.ok(v === 0 ? 0 : v);
1544
+ }
1545
+ if (typeof v !== "object") return require_errors.err(`${path}: ${typeof v} is not JSON-safe`);
1546
+ if (ancestors.has(v)) return require_errors.err(`${path}: circular reference is not JSON-safe`);
1547
+ ancestors.add(v);
1548
+ const out = Array.isArray(v) ? copyArray(v, path, ancestors) : copyObject(v, path, ancestors);
1549
+ ancestors.delete(v);
1550
+ return out;
1551
+ }
1552
+ function copyArray(v, path, ancestors) {
1553
+ const out = [];
1554
+ for (let i = 0; i < v.length; i++) {
1555
+ const r = copyJson(v[i], `${path}[${i}]`, ancestors);
1556
+ if (!r.ok) return r;
1557
+ out.push(r.value);
1558
+ }
1559
+ return require_errors.ok(Object.freeze(out));
1560
+ }
1561
+ function copyObject(v, path, ancestors) {
1562
+ const proto = Object.getPrototypeOf(v);
1563
+ if (proto !== Object.prototype && proto !== null) {
1564
+ const ctor = v.constructor;
1565
+ const what = typeof ctor === "function" && ctor.name ? `${ctor.name} instance` : "object";
1566
+ return require_errors.err(`${path}: ${what} is not JSON-safe (use a plain object)`);
1567
+ }
1568
+ const record = v;
1569
+ const entries = [];
1570
+ for (const key of Object.keys(record).sort()) {
1571
+ const r = copyJson(record[key], `${path}.${key}`, ancestors);
1572
+ if (!r.ok) return r;
1573
+ entries.push([key, r.value]);
1574
+ }
1575
+ return require_errors.ok(Object.freeze(Object.fromEntries(entries)));
1576
+ }
1577
+ var TAG_NULL = 0;
1578
+ var TAG_FALSE = 1;
1579
+ var TAG_TRUE = 2;
1580
+ var TAG_NUMBER = 3;
1581
+ var TAG_STRING = 4;
1582
+ var TAG_ARRAY = 5;
1583
+ var TAG_OBJECT = 6;
1584
+ /** Type-tagged, length-prefixed Merkle mix of a JSON value; object keys are
1585
+ * hashed in sorted order, so key order never changes the result. */
1586
+ function hashJson(h, v) {
1587
+ if (v === null) return fnvMixByte(h, TAG_NULL);
1588
+ if (typeof v === "boolean") return fnvMixByte(h, v ? TAG_TRUE : TAG_FALSE);
1589
+ if (typeof v === "number") return fnvMixNumber(fnvMixByte(h, TAG_NUMBER), v);
1590
+ if (typeof v === "string") return fnvMixText(fnvMixByte(h, TAG_STRING), v);
1591
+ if (isJsonArray(v)) {
1592
+ let r = fnvMixInt32(fnvMixByte(h, TAG_ARRAY), v.length);
1593
+ for (const item of v) r = hashJson(r, item);
1594
+ return r;
1595
+ }
1596
+ const keys = Object.keys(v).sort();
1597
+ let r = fnvMixInt32(fnvMixByte(h, TAG_OBJECT), keys.length);
1598
+ for (const key of keys) {
1599
+ r = fnvMixText(r, key);
1600
+ r = hashJson(r, v[key] ?? null);
1601
+ }
1602
+ return r;
1603
+ }
1604
+ function isJsonArray(v) {
1605
+ return Array.isArray(v);
1606
+ }
1607
+ //#endregion
1608
+ //#region src/csg/types.ts
1609
+ var OUTPUT_KINDS = {
1610
+ Solid: true,
1611
+ Face: true,
1612
+ Wire: true,
1613
+ Edge: true,
1614
+ Vertex: true,
1615
+ Compound: true
1616
+ };
1617
+ function isOutputKind(v) {
1618
+ return typeof v === "string" && Object.hasOwn(OUTPUT_KINDS, v);
1619
+ }
1620
+ function outputKindOf(node) {
1621
+ switch (node.kind) {
1622
+ case "Box":
1623
+ case "Sphere":
1624
+ case "Cylinder":
1625
+ case "Cone":
1626
+ case "Torus": return "Solid";
1627
+ case "Polygon": return "Face";
1628
+ case "Circle":
1629
+ case "Line": return "Edge";
1630
+ case "Vertex": return "Vertex";
1631
+ case "Empty": return node.output;
1632
+ case "Fuse":
1633
+ case "Cut":
1634
+ case "Intersect": return outputKindOf(node.a);
1635
+ case "FuseAll": return node.shapes[0] ? outputKindOf(node.shapes[0]) : "Solid";
1636
+ case "CutAll": return outputKindOf(node.base);
1637
+ case "Translate":
1638
+ case "Rotate":
1639
+ case "Scale":
1640
+ case "Mirror":
1641
+ case "Color": return outputKindOf(node.target);
1642
+ case "Extrude":
1643
+ case "Revolve":
1644
+ case "Loft":
1645
+ case "Sweep":
1646
+ case "Fillet":
1647
+ case "Chamfer":
1648
+ case "Shell": return "Solid";
1649
+ case "Profile": return "Face";
1650
+ case "Path": return "Wire";
1651
+ case "Compound": return "Compound";
1652
+ case "Instance": return node.fuse ? "Solid" : "Compound";
1653
+ case "Extension": return node.output;
1654
+ }
1655
+ }
1656
+ //#endregion
1525
1657
  //#region src/csg/builders.ts
1526
1658
  var EMPTY_DEPS = /* @__PURE__ */ new Set();
1527
1659
  function startHash(tag) {
@@ -2025,42 +2157,47 @@ function instance$1(source, placements, fuse = false) {
2025
2157
  freeParams: depsOf(source)
2026
2158
  };
2027
2159
  }
2028
- //#endregion
2029
- //#region src/csg/types.ts
2030
- function outputKindOf(node) {
2031
- switch (node.kind) {
2032
- case "Box":
2033
- case "Sphere":
2034
- case "Cylinder":
2035
- case "Cone":
2036
- case "Torus": return "Solid";
2037
- case "Polygon": return "Face";
2038
- case "Circle":
2039
- case "Line": return "Edge";
2040
- case "Vertex": return "Vertex";
2041
- case "Empty": return node.output;
2042
- case "Fuse":
2043
- case "Cut":
2044
- case "Intersect": return outputKindOf(node.a);
2045
- case "FuseAll": return node.shapes[0] ? outputKindOf(node.shapes[0]) : "Solid";
2046
- case "CutAll": return outputKindOf(node.base);
2047
- case "Translate":
2048
- case "Rotate":
2049
- case "Scale":
2050
- case "Mirror":
2051
- case "Color": return outputKindOf(node.target);
2052
- case "Extrude":
2053
- case "Revolve":
2054
- case "Loft":
2055
- case "Sweep":
2056
- case "Fillet":
2057
- case "Chamfer":
2058
- case "Shell": return "Solid";
2059
- case "Profile": return "Face";
2060
- case "Path": return "Wire";
2061
- case "Compound": return "Compound";
2062
- case "Instance": return node.fuse ? "Solid" : "Compound";
2063
- }
2160
+ function isExpr(v) {
2161
+ return typeof v === "object" && v !== null && typeof v.structuralHash === "bigint";
2162
+ }
2163
+ function extensionParams(name, input) {
2164
+ const entries = [];
2165
+ for (const key of Object.keys(input).sort()) {
2166
+ const v = input[key];
2167
+ if (typeof v !== "number" && !isExpr(v)) throw new TypeError(`csg.extension(${name}): params.${key} must be a number or an Expr`);
2168
+ entries.push([key, asScalarExpr(v)]);
2169
+ }
2170
+ return entries;
2171
+ }
2172
+ /** A caller-defined operation, materialized at evaluation by the
2173
+ * `ExtensionEvaluator` registered under `name` in
2174
+ * `EvaluatorOptions.extensions`. Unlike the other builders this one validates
2175
+ * eagerly and throws a TypeError: a node whose data cannot be hashed
2176
+ * canonically has no content address. */
2177
+ function extension(name, output, options) {
2178
+ if (typeof name !== "string" || name.length === 0) throw new TypeError("csg.extension: name must be a non-empty string");
2179
+ if (!isOutputKind(output)) throw new TypeError(`csg.extension(${name}): unknown output kind ${String(output)}`);
2180
+ const data = canonicalJson(options?.data ?? null, "data");
2181
+ if (!data.ok) throw new TypeError(`csg.extension(${name}): ${data.error}`);
2182
+ const children = [...options?.children ?? []];
2183
+ const params = extensionParams(name, options?.params ?? {});
2184
+ let h = fnvMixText(startHash("Extension"), name);
2185
+ h = fnvMixText(h, output);
2186
+ h = fnvMixInt32(h, children.length);
2187
+ for (const c of children) h = mix(h, c);
2188
+ h = fnvMixInt32(h, params.length);
2189
+ for (const [key, e] of params) h = mix(fnvMixText(h, key), e);
2190
+ h = hashJson(h, data.value);
2191
+ return {
2192
+ kind: "Extension",
2193
+ name,
2194
+ output,
2195
+ children,
2196
+ params: Object.fromEntries(params),
2197
+ data: data.value,
2198
+ structuralHash: h,
2199
+ freeParams: depsOf(...children, ...params.map(([, e]) => e))
2200
+ };
2064
2201
  }
2065
2202
  //#endregion
2066
2203
  //#region src/csg/profiles.ts
@@ -3115,6 +3252,29 @@ function evalShell(node, ctx) {
3115
3252
  return require_healingFns.shell(solid.value, faces, thickness.value);
3116
3253
  }
3117
3254
  //#endregion
3255
+ //#region src/csg/evaluators/extension.ts
3256
+ function evalExtension(node, ctx) {
3257
+ const evaluator = ctx.extensions.get(node.name);
3258
+ if (evaluator === void 0) return require_errors.err(require_errors.validationError(require_errors.BrepErrorCode.CSG_EXTENSION_UNKNOWN, `Extension: no evaluator registered for '${node.name}'`, void 0, { name: node.name }, `Register one with new csg.Evaluator({ extensions: { '${node.name}': fn } })`));
3259
+ const params = [];
3260
+ for (const [key, expr] of Object.entries(node.params)) {
3261
+ const v = evalScalar(expr, ctx.env, `Extension(${node.name}).params.${key}`);
3262
+ if (!v.ok) return v;
3263
+ params.push([key, v.value]);
3264
+ }
3265
+ const children = [];
3266
+ for (const child of node.children) {
3267
+ const r = ctx.evalNode(child);
3268
+ if (!r.ok) return r;
3269
+ children.push(r.value);
3270
+ }
3271
+ return evaluator({
3272
+ params: Object.fromEntries(params),
3273
+ data: node.data,
3274
+ children
3275
+ });
3276
+ }
3277
+ //#endregion
3118
3278
  //#region src/csg/evaluate.ts
3119
3279
  function dispatch(node, ctx) {
3120
3280
  switch (node.kind) {
@@ -3139,6 +3299,7 @@ function dispatch(node, ctx) {
3139
3299
  case "Mirror": return evalMirror(node, ctx);
3140
3300
  case "Compound": return evalCompound(node, ctx);
3141
3301
  case "Instance": return evalInstance(node, ctx);
3302
+ case "Extension": return evalExtension(node, ctx);
3142
3303
  case "Extrude":
3143
3304
  case "Revolve":
3144
3305
  case "Loft":
@@ -3366,6 +3527,7 @@ var Evaluator = class {
3366
3527
  maxMeshCacheEntries;
3367
3528
  meshCache = /* @__PURE__ */ new Map();
3368
3529
  onStep;
3530
+ extensions;
3369
3531
  hits = 0;
3370
3532
  misses = 0;
3371
3533
  evictions = 0;
@@ -3379,6 +3541,9 @@ var Evaluator = class {
3379
3541
  const meshMax = options.maxMeshCacheEntries;
3380
3542
  if (meshMax !== void 0 && (!Number.isInteger(meshMax) || meshMax < 1)) throw new RangeError(`Evaluator: maxMeshCacheEntries must be a positive integer, got ${String(meshMax)}`);
3381
3543
  this.maxMeshCacheEntries = meshMax;
3544
+ const extensions = new Map(Object.entries(options.extensions ?? {}));
3545
+ for (const [name, fn] of extensions) if (typeof fn !== "function") throw new TypeError(`Evaluator: extensions['${name}'] must be a function`);
3546
+ this.extensions = extensions;
3382
3547
  }
3383
3548
  /**
3384
3549
  * Materialize a CSG IR tree against the given parameter environment.
@@ -3507,7 +3672,8 @@ var Evaluator = class {
3507
3672
  const result = dispatch(node, {
3508
3673
  env,
3509
3674
  tolerance: this.defaultTolerance,
3510
- evalNode: (child) => this.evaluateInner(child, env)
3675
+ evalNode: (child) => this.evaluateInner(child, env),
3676
+ extensions: this.extensions
3511
3677
  });
3512
3678
  if (!result.ok) return result;
3513
3679
  const shape = result.value;
@@ -3635,6 +3801,11 @@ function rebuildChildren(n, pred, repl, memo) {
3635
3801
  });
3636
3802
  case "Compound": return compound$1(n.children.map((c) => walk(c, pred, repl, memo)));
3637
3803
  case "Instance": return instance$1(walk(n.source, pred, repl, memo), n.placements, n.fuse);
3804
+ case "Extension": return extension(n.name, n.output, {
3805
+ children: n.children.map((c) => walk(c, pred, repl, memo)),
3806
+ params: n.params,
3807
+ data: n.data
3808
+ });
3638
3809
  case "Extrude":
3639
3810
  case "Revolve":
3640
3811
  case "Loft":
@@ -3699,7 +3870,8 @@ function childrenOf(n) {
3699
3870
  case "Fillet":
3700
3871
  case "Chamfer":
3701
3872
  case "Shell": return [n.target];
3702
- case "Compound": return n.children;
3873
+ case "Compound":
3874
+ case "Extension": return n.children;
3703
3875
  case "Instance": return [n.source];
3704
3876
  case "Extrude":
3705
3877
  case "Revolve": return [n.profile];
@@ -3723,11 +3895,11 @@ function toJSON(node) {
3723
3895
  };
3724
3896
  const root = emitNode(node, ctx);
3725
3897
  return ctx.defs.length > 0 ? {
3726
- csgVersion: 8,
3898
+ csgVersion: 9,
3727
3899
  defs: ctx.defs,
3728
3900
  root
3729
3901
  } : {
3730
- csgVersion: 8,
3902
+ csgVersion: 9,
3731
3903
  root
3732
3904
  };
3733
3905
  }
@@ -4000,7 +4172,18 @@ function featureToJson(n, ctx) {
4000
4172
  default: return;
4001
4173
  }
4002
4174
  }
4175
+ function extensionToJson(n, ctx) {
4176
+ return {
4177
+ kind: "Extension",
4178
+ name: n.name,
4179
+ output: n.output,
4180
+ children: n.children.map((c) => emitNode(c, ctx)),
4181
+ params: Object.fromEntries(Object.entries(n.params).map(([k, e]) => [k, exprToJson(e)])),
4182
+ data: n.data
4183
+ };
4184
+ }
4003
4185
  function nodeToJson(n, ctx) {
4186
+ if (n.kind === "Extension") return extensionToJson(n, ctx);
4004
4187
  if (n.kind === "Color") return {
4005
4188
  kind: "Color",
4006
4189
  target: emitNode(n.target, ctx),
@@ -4067,7 +4250,7 @@ function readVec2(v, where) {
4067
4250
  function fromJSON(envelope) {
4068
4251
  if (!isObj(envelope)) return bad("input is not an object");
4069
4252
  const v = envelope["csgVersion"];
4070
- if (typeof v !== "number" || !Number.isInteger(v) || v < MIN_CSG_VERSION || v > 8) return bad(`unsupported csgVersion ${String(v)} (expected ${MIN_CSG_VERSION}..8)`);
4253
+ if (typeof v !== "number" || !Number.isInteger(v) || v < MIN_CSG_VERSION || v > 9) return bad(`unsupported csgVersion ${String(v)} (expected ${MIN_CSG_VERSION}..9)`);
4071
4254
  const rawDefs = envelope["defs"];
4072
4255
  const defs = [];
4073
4256
  if (rawDefs !== void 0) {
@@ -4159,15 +4342,15 @@ function readOptTolerance(j) {
4159
4342
  if (t === void 0 || t === null) return require_errors.ok(void 0);
4160
4343
  return isNumber$1(t) ? require_errors.ok(t) : bad("tolerance: not a finite number");
4161
4344
  }
4345
+ function readRef(ref, defs) {
4346
+ if (!isNumber$1(ref) || !Number.isInteger(ref) || ref < 0) return bad("$ref: expected a non-negative integer def index");
4347
+ const node = defs[ref];
4348
+ if (node === void 0) return bad(`$ref: ${ref} is out of range (forward refs are rejected)`);
4349
+ return require_errors.ok(node);
4350
+ }
4162
4351
  function readNode(j, defs) {
4163
4352
  if (!isObj(j)) return bad("node: not an object");
4164
- const ref = j["$ref"];
4165
- if (ref !== void 0) {
4166
- if (!isNumber$1(ref) || !Number.isInteger(ref) || ref < 0) return bad("$ref: expected a non-negative integer def index");
4167
- const node = defs[ref];
4168
- if (node === void 0) return bad(`$ref: ${ref} is out of range (forward refs are rejected)`);
4169
- return require_errors.ok(node);
4170
- }
4353
+ if (j["$ref"] !== void 0) return readRef(j["$ref"], defs);
4171
4354
  const kind = j["kind"];
4172
4355
  switch (kind) {
4173
4356
  case "Box":
@@ -4201,6 +4384,7 @@ function readNode(j, defs) {
4201
4384
  case "Fillet": return readFillet(j, defs);
4202
4385
  case "Chamfer": return readChamfer(j, defs);
4203
4386
  case "Shell": return readShell(j, defs);
4387
+ case "Extension": return readExtension(j, defs);
4204
4388
  default: return bad(`unknown node kind: ${String(kind)}`);
4205
4389
  }
4206
4390
  }
@@ -4612,6 +4796,34 @@ function readCompound(j, defs) {
4612
4796
  function isMatrix4x4(v) {
4613
4797
  return Array.isArray(v) && v.length === 4 && v.every((row) => Array.isArray(row) && row.length === 4 && row.every(isNumber$1));
4614
4798
  }
4799
+ function readExtensionParams(j) {
4800
+ if (j === void 0) return require_errors.ok({});
4801
+ if (!isObj(j)) return bad("Extension.params: not an object");
4802
+ const entries = [];
4803
+ for (const [key, raw] of Object.entries(j)) {
4804
+ const e = readExpr(raw);
4805
+ if (!e.ok) return e;
4806
+ entries.push([key, e.value]);
4807
+ }
4808
+ return require_errors.ok(Object.fromEntries(entries));
4809
+ }
4810
+ function readExtension(j, defs) {
4811
+ const name = j["name"];
4812
+ if (!isString(name) || name.length === 0) return bad("Extension.name: expected a non-empty string");
4813
+ const output = j["output"];
4814
+ if (!isOutputKind(output)) return bad(`Extension.output: ${String(output)}`);
4815
+ const children = j["children"] === void 0 ? require_errors.ok([]) : readNodeArray(j["children"], "Extension.children", defs);
4816
+ if (!children.ok) return children;
4817
+ const params = readExtensionParams(j["params"]);
4818
+ if (!params.ok) return params;
4819
+ const data = canonicalJson(j["data"] ?? null, "Extension.data");
4820
+ if (!data.ok) return bad(data.error);
4821
+ return require_errors.ok(extension(name, output, {
4822
+ children: children.value,
4823
+ params: params.value,
4824
+ data: data.value
4825
+ }));
4826
+ }
4615
4827
  function readInstance(j, defs) {
4616
4828
  const source = readNode(j["source"], defs);
4617
4829
  if (!source.ok) return source;
@@ -4718,6 +4930,7 @@ function rewriteNode(n) {
4718
4930
  case "Mirror": return optimizeTransform(n);
4719
4931
  case "Compound": return compound$1(n.children.map(optimizeNode).filter((c) => c.kind !== "Empty"));
4720
4932
  case "Instance": return instance$1(optimizeNode(n.source), n.placements, n.fuse);
4933
+ case "Extension": return optimizeExtension(n);
4721
4934
  case "Extrude":
4722
4935
  case "Revolve":
4723
4936
  case "Loft":
@@ -4747,6 +4960,13 @@ function optimizeFeature(n) {
4747
4960
  case "Shell": return shell$1(optimizeNode(n.target), n.refs, foldExpr(n.thickness));
4748
4961
  }
4749
4962
  }
4963
+ function optimizeExtension(n) {
4964
+ return extension(n.name, n.output, {
4965
+ children: n.children.map(optimizeNode),
4966
+ params: Object.fromEntries(Object.entries(n.params).map(([k, e]) => [k, foldExpr(e)])),
4967
+ data: n.data
4968
+ });
4969
+ }
4750
4970
  function optimizeTransform(n) {
4751
4971
  switch (n.kind) {
4752
4972
  case "Rotate": return rotate$1(optimizeNode(n.target), foldExpr(n.angle), {
@@ -8770,7 +8990,7 @@ var patterns_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
8770
8990
  //#endregion
8771
8991
  //#region src/ns/csg.ts
8772
8992
  var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
8773
- CSG_VERSION: () => 8,
8993
+ CSG_VERSION: () => 9,
8774
8994
  Evaluator: () => Evaluator,
8775
8995
  add: () => add$1,
8776
8996
  arbitraryClosedProfile: () => arbitraryClosedProfile,
@@ -8802,6 +9022,7 @@ var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
8802
9022
  emptyFace: () => emptyFace,
8803
9023
  emptySolid: () => emptySolid,
8804
9024
  emptyWire: () => emptyWire,
9025
+ extension: () => extension,
8805
9026
  extrude: () => extrude$1,
8806
9027
  fillet: () => fillet$1,
8807
9028
  foldExpr: () => foldExpr,