brepjs 18.154.0 → 18.156.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.
package/dist/brepjs.js CHANGED
@@ -14,7 +14,7 @@ import { A as parseColor, D as colorShape, E as colorFaces, F as tagFaces, M as
14
14
  import { A as locate$1, C as threePointArc, D as wireLoop, E as wire, F as transformCopy$1, I as translate$2, M as resize, N as rotate$2, O as applyMatrix$1, P as scale$3, S as tangentArc, T as vertex, _ as polygon, a as circle, b as sphere$1, c as cylinder, d as ellipsoid, f as face, g as offsetFace, h as line, i as bsplineApprox, j as mirror$2, k as composeTransforms, l as ellipse, m as helix, n as bezier, o as compound, p as filledFace, r as box, s as cone, t as addHoles, u as ellipseArc, v as sewShells, w as torus$1, x as subFace, y as solid } from "./primitiveFns-B0iLpCEN.js";
15
15
  import { a as isSameShape, i as isEqualShape, n as getHashCode, o as simplify$1, r as isEmpty$2, s as toBREP$1, t as clone$1 } from "./shapeFns-D0hK1y4m.js";
16
16
  import { a as curveIsPeriodic, c as curvePointAt, d as flipOrientation, f as getCurveType, h as offsetWire2D, i as curveIsClosed, l as curveStartPoint, m as interpolateCurve, n as curveAxis, o as curveLength, p as getOrientation, r as curveEndPoint, s as curvePeriod, t as approximateCurve, u as curveTangentAt } from "./curveFns-U6vegvin.js";
17
- import { a as meshEdges$1, c as meshMultiLOD, d as createMeshCache, i as mesh$1, l as buildMeshCacheKey, n as exportSTEP, o as meshLODs, r as exportSTL, s as meshLODsProgressive, t as exportIGES, u as clearMeshCache } from "./meshFns-FSW58GBR.js";
17
+ import { a as meshEdges$1, c as meshMultiLOD, d as clearMeshCache, f as createMeshCache, i as mesh$1, l as scaleDefaultTolerance, n as exportSTEP, o as meshLODs, r as exportSTL, s as meshLODsProgressive, t as exportIGES, u as buildMeshCacheKey } from "./meshFns-BTE7WFpU.js";
18
18
  import { n as getAtOrThrow, r as lastOrThrow, t as firstOrThrow } from "./arrayAccess-DrUGPADn.js";
19
19
  import { _ as makeThreePointArc, d as makeCircle, h as makeLine, l as makeBSplineInterpolation, n as fill, p as makeEllipseArc, r as makeFace, s as assembleWire, u as makeBezierCurve } from "./surfaceBuilders-C_oBS19q.js";
20
20
  import { A as toBufferGeometryData, C as chamfer$2, D as shell$2, E as offset$1, M as toLODGeometryData, N as toLODGeometryLevels, O as thicken$1, P as toLineGeometryData, S as chamferDistAngle, T as fillet$2, _ as checkBoolean, a as healFace, b as getNurbsCurveData, c as isValid$1, d as chamferWithEvolution, f as cutWithEvolution, g as shellWithEvolution, h as intersectWithEvolution, i as heal$1, j as toGroupedBufferGeometryData, k as variableFillet, l as solidFromShell, m as fuseWithEvolution, n as fixSelfIntersection, o as healSolid, p as filletWithEvolution, r as fixShape, s as healWire, t as autoHeal, u as positionOnCurve, v as cutAllBisect, w as draft$1, x as getNurbsSurfaceData, y as fuseAllBisect } from "./healingFns-CN9ra8Kh.js";
@@ -3365,20 +3365,6 @@ function relocateFaceGroups(faceGroups, innerHashes, placedHashes) {
3365
3365
  faceId: remap.get(g.faceId) ?? g.faceId
3366
3366
  }));
3367
3367
  }
3368
- var SCALE_INVARIANT_DIAGONAL = 10;
3369
- function scaleDefaultTolerance(base, shape) {
3370
- let diagonal;
3371
- try {
3372
- const b = getBounds(shape);
3373
- const dx = b.xMax - b.xMin;
3374
- const dy = b.yMax - b.yMin;
3375
- const dz = b.zMax - b.zMin;
3376
- diagonal = Math.sqrt(dx * dx + dy * dy + dz * dz);
3377
- } catch {
3378
- return base;
3379
- }
3380
- return base * Math.max(1, diagonal / SCALE_INVARIANT_DIAGONAL);
3381
- }
3382
3368
  var Evaluator = class {
3383
3369
  cache = /* @__PURE__ */ new Map();
3384
3370
  refCounts = /* @__PURE__ */ new Map();
@@ -3616,13 +3602,165 @@ function withEvaluator(options, fn) {
3616
3602
  _usingCtx$4.d();
3617
3603
  }
3618
3604
  }
3605
+ //#endregion
3606
+ //#region src/csg/edit.ts
3607
+ function replaceNode(root, pred, replacement) {
3608
+ return walk(root, pred, replacement, /* @__PURE__ */ new Map());
3609
+ }
3610
+ function walk(node, pred, repl, memo) {
3611
+ const cached = memo.get(node);
3612
+ if (cached !== void 0) return cached;
3613
+ const out = pred(node) ? repl : rebuildChildren(node, pred, repl, memo);
3614
+ memo.set(node, out);
3615
+ return out;
3616
+ }
3617
+ function rebuildChildren(n, pred, repl, memo) {
3618
+ switch (n.kind) {
3619
+ case "Box":
3620
+ case "Sphere":
3621
+ case "Cylinder":
3622
+ case "Cone":
3623
+ case "Torus":
3624
+ case "Polygon":
3625
+ case "Circle":
3626
+ case "Line":
3627
+ case "Vertex":
3628
+ case "Empty":
3629
+ case "Path":
3630
+ case "Profile": return n;
3631
+ case "Fuse": return fuse$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
3632
+ case "Cut": return cut$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
3633
+ case "Intersect": return intersect$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
3634
+ case "FuseAll": return fuseAll$1(n.shapes.map((c) => walk(c, pred, repl, memo)), n.tolerance);
3635
+ case "CutAll": return cutAll$1(walk(n.base, pred, repl, memo), n.tools.map((c) => walk(c, pred, repl, memo)), n.tolerance);
3636
+ case "Translate": return translate$1(walk(n.target, pred, repl, memo), n.vector);
3637
+ case "Rotate": return rotate$1(walk(n.target, pred, repl, memo), n.angle, {
3638
+ axis: n.axis,
3639
+ at: n.at
3640
+ });
3641
+ case "Scale": return scale$2(walk(n.target, pred, repl, memo), n.factor, { center: n.center });
3642
+ case "Mirror": return mirror$1(walk(n.target, pred, repl, memo), {
3643
+ normal: n.normal,
3644
+ at: n.at
3645
+ });
3646
+ case "Compound": return compound$1(n.children.map((c) => walk(c, pred, repl, memo)));
3647
+ case "Instance": return instance$1(walk(n.source, pred, repl, memo), n.placements, n.fuse);
3648
+ case "Extrude":
3649
+ case "Revolve":
3650
+ case "Loft":
3651
+ case "Sweep":
3652
+ case "Color":
3653
+ case "Fillet":
3654
+ case "Chamfer":
3655
+ case "Shell": return rebuildFeature(n, pred, repl, memo);
3656
+ }
3657
+ }
3658
+ function rebuildFeature(n, pred, repl, memo) {
3659
+ switch (n.kind) {
3660
+ case "Extrude": return extrude$1(walk(n.profile, pred, repl, memo), n.vector);
3661
+ case "Revolve": return revolve$1(walk(n.profile, pred, repl, memo), n.angle, {
3662
+ axis: n.axis,
3663
+ at: n.at
3664
+ });
3665
+ case "Loft": return loft$1(n.sections.map((s) => walk(s, pred, repl, memo)), { ruled: n.ruled });
3666
+ case "Sweep": return sweep$2(walk(n.profile, pred, repl, memo), walk(n.spine, pred, repl, memo), { frenet: n.frenet });
3667
+ case "Color": return color(walk(n.target, pred, repl, memo), [...n.color]);
3668
+ case "Fillet": return fillet$1(walk(n.target, pred, repl, memo), n.ref, n.radius);
3669
+ case "Chamfer": return chamfer$1(walk(n.target, pred, repl, memo), n.ref, n.distance);
3670
+ case "Shell": return shell$1(walk(n.target, pred, repl, memo), n.refs, n.thickness);
3671
+ }
3672
+ }
3673
+ /** Visits each DISTINCT node once (identity-deduplicated), so a shared
3674
+ * subtree is reported once and a self-referencing DAG walks in linear time. */
3675
+ function forEachNode(root, fn) {
3676
+ const seen = /* @__PURE__ */ new Set();
3677
+ const visit = (node) => {
3678
+ if (seen.has(node)) return;
3679
+ seen.add(node);
3680
+ fn(node);
3681
+ for (const child of childrenOf(node)) visit(child);
3682
+ };
3683
+ visit(root);
3684
+ }
3685
+ function childrenOf(n) {
3686
+ switch (n.kind) {
3687
+ case "Box":
3688
+ case "Sphere":
3689
+ case "Cylinder":
3690
+ case "Cone":
3691
+ case "Torus":
3692
+ case "Polygon":
3693
+ case "Circle":
3694
+ case "Line":
3695
+ case "Vertex":
3696
+ case "Empty":
3697
+ case "Path":
3698
+ case "Profile": return [];
3699
+ case "Fuse":
3700
+ case "Cut":
3701
+ case "Intersect": return [n.a, n.b];
3702
+ case "FuseAll": return n.shapes;
3703
+ case "CutAll": return [n.base, ...n.tools];
3704
+ case "Translate":
3705
+ case "Rotate":
3706
+ case "Scale":
3707
+ case "Mirror":
3708
+ case "Color":
3709
+ case "Fillet":
3710
+ case "Chamfer":
3711
+ case "Shell": return [n.target];
3712
+ case "Compound": return n.children;
3713
+ case "Instance": return [n.source];
3714
+ case "Extrude":
3715
+ case "Revolve": return [n.profile];
3716
+ case "Loft": return n.sections;
3717
+ case "Sweep": return [n.profile, n.spine];
3718
+ }
3719
+ }
3720
+ function nodeCount(root) {
3721
+ let n = 0;
3722
+ forEachNode(root, () => {
3723
+ n++;
3724
+ });
3725
+ return n;
3726
+ }
3619
3727
  var MIN_CSG_VERSION = 1;
3620
3728
  function toJSON(node) {
3621
- return {
3622
- csgVersion: 7,
3623
- root: nodeToJson(node)
3729
+ const ctx = {
3730
+ counts: countRefs(node),
3731
+ ids: /* @__PURE__ */ new Map(),
3732
+ defs: []
3733
+ };
3734
+ const root = emitNode(node, ctx);
3735
+ return ctx.defs.length > 0 ? {
3736
+ csgVersion: 8,
3737
+ defs: ctx.defs,
3738
+ root
3739
+ } : {
3740
+ csgVersion: 8,
3741
+ root
3624
3742
  };
3625
3743
  }
3744
+ function countRefs(root) {
3745
+ const counts = /* @__PURE__ */ new Map();
3746
+ const visit = (n) => {
3747
+ const seen = counts.get(n) ?? 0;
3748
+ counts.set(n, seen + 1);
3749
+ if (seen === 0) for (const child of childrenOf(n)) visit(child);
3750
+ };
3751
+ visit(root);
3752
+ return counts;
3753
+ }
3754
+ function emitNode(n, ctx) {
3755
+ if ((ctx.counts.get(n) ?? 0) < 2) return nodeToJson(n, ctx);
3756
+ const existing = ctx.ids.get(n);
3757
+ if (existing !== void 0) return { $ref: existing };
3758
+ const body = nodeToJson(n, ctx);
3759
+ const id = ctx.defs.length;
3760
+ ctx.defs.push(body);
3761
+ ctx.ids.set(n, id);
3762
+ return { $ref: id };
3763
+ }
3626
3764
  function exprToJson(e) {
3627
3765
  switch (e.kind) {
3628
3766
  case "NumLit": return {
@@ -3720,25 +3858,25 @@ function primitiveToJson(n) {
3720
3858
  default: return;
3721
3859
  }
3722
3860
  }
3723
- function booleanToJson(n) {
3861
+ function booleanToJson(n, ctx) {
3724
3862
  switch (n.kind) {
3725
3863
  case "Fuse":
3726
3864
  case "Cut":
3727
3865
  case "Intersect": return {
3728
3866
  kind: n.kind,
3729
- a: nodeToJson(n.a),
3730
- b: nodeToJson(n.b),
3867
+ a: emitNode(n.a, ctx),
3868
+ b: emitNode(n.b, ctx),
3731
3869
  tolerance: n.tolerance
3732
3870
  };
3733
3871
  case "FuseAll": return {
3734
3872
  kind: "FuseAll",
3735
- shapes: n.shapes.map(nodeToJson),
3873
+ shapes: n.shapes.map((s) => emitNode(s, ctx)),
3736
3874
  tolerance: n.tolerance
3737
3875
  };
3738
3876
  case "CutAll": return {
3739
3877
  kind: "CutAll",
3740
- base: nodeToJson(n.base),
3741
- tools: n.tools.map(nodeToJson),
3878
+ base: emitNode(n.base, ctx),
3879
+ tools: n.tools.map((t) => emitNode(t, ctx)),
3742
3880
  tolerance: n.tolerance
3743
3881
  };
3744
3882
  default: return;
@@ -3805,103 +3943,108 @@ function contourToJson(c) {
3805
3943
  segments: c.segments.map(segmentToJson)
3806
3944
  };
3807
3945
  }
3808
- function transformToJson(n) {
3946
+ function transformToJson(n, ctx) {
3809
3947
  switch (n.kind) {
3810
3948
  case "Translate": return {
3811
3949
  kind: "Translate",
3812
- target: nodeToJson(n.target),
3950
+ target: emitNode(n.target, ctx),
3813
3951
  vector: exprToJson(n.vector)
3814
3952
  };
3815
3953
  case "Rotate": return {
3816
3954
  kind: "Rotate",
3817
- target: nodeToJson(n.target),
3955
+ target: emitNode(n.target, ctx),
3818
3956
  angle: exprToJson(n.angle),
3819
3957
  axis: optExprToJson(n.axis),
3820
3958
  at: optExprToJson(n.at)
3821
3959
  };
3822
3960
  case "Scale": return {
3823
3961
  kind: "Scale",
3824
- target: nodeToJson(n.target),
3962
+ target: emitNode(n.target, ctx),
3825
3963
  factor: exprToJson(n.factor),
3826
3964
  center: optExprToJson(n.center)
3827
3965
  };
3828
3966
  case "Mirror": return {
3829
3967
  kind: "Mirror",
3830
- target: nodeToJson(n.target),
3968
+ target: emitNode(n.target, ctx),
3831
3969
  normal: optExprToJson(n.normal),
3832
3970
  at: optExprToJson(n.at)
3833
3971
  };
3834
3972
  default: return;
3835
3973
  }
3836
3974
  }
3837
- function nodeToJson(n) {
3838
- if (n.kind === "Extrude") return {
3839
- kind: "Extrude",
3840
- profile: nodeToJson(n.profile),
3841
- vector: exprToJson(n.vector)
3842
- };
3843
- if (n.kind === "Revolve") return {
3844
- kind: "Revolve",
3845
- profile: nodeToJson(n.profile),
3846
- angle: exprToJson(n.angle),
3847
- axis: optExprToJson(n.axis),
3848
- at: optExprToJson(n.at)
3849
- };
3850
- if (n.kind === "Loft") return {
3851
- kind: "Loft",
3852
- sections: n.sections.map(nodeToJson),
3853
- ruled: n.ruled
3854
- };
3855
- if (n.kind === "Path") return {
3856
- kind: "Path",
3857
- start: exprToJson(n.start),
3858
- segments: n.segments.map(segmentToJson)
3859
- };
3860
- if (n.kind === "Sweep") return {
3861
- kind: "Sweep",
3862
- profile: nodeToJson(n.profile),
3863
- spine: nodeToJson(n.spine),
3864
- frenet: n.frenet
3865
- };
3866
- if (n.kind === "Profile") return {
3867
- kind: "Profile",
3868
- outline: contourToJson(n.outline),
3869
- holes: n.holes.map(contourToJson)
3870
- };
3975
+ function featureToJson(n, ctx) {
3976
+ switch (n.kind) {
3977
+ case "Extrude": return {
3978
+ kind: "Extrude",
3979
+ profile: emitNode(n.profile, ctx),
3980
+ vector: exprToJson(n.vector)
3981
+ };
3982
+ case "Revolve": return {
3983
+ kind: "Revolve",
3984
+ profile: emitNode(n.profile, ctx),
3985
+ angle: exprToJson(n.angle),
3986
+ axis: optExprToJson(n.axis),
3987
+ at: optExprToJson(n.at)
3988
+ };
3989
+ case "Loft": return {
3990
+ kind: "Loft",
3991
+ sections: n.sections.map((s) => emitNode(s, ctx)),
3992
+ ruled: n.ruled
3993
+ };
3994
+ case "Path": return {
3995
+ kind: "Path",
3996
+ start: exprToJson(n.start),
3997
+ segments: n.segments.map(segmentToJson)
3998
+ };
3999
+ case "Sweep": return {
4000
+ kind: "Sweep",
4001
+ profile: emitNode(n.profile, ctx),
4002
+ spine: emitNode(n.spine, ctx),
4003
+ frenet: n.frenet
4004
+ };
4005
+ case "Profile": return {
4006
+ kind: "Profile",
4007
+ outline: contourToJson(n.outline),
4008
+ holes: n.holes.map(contourToJson)
4009
+ };
4010
+ default: return;
4011
+ }
4012
+ }
4013
+ function nodeToJson(n, ctx) {
3871
4014
  if (n.kind === "Color") return {
3872
4015
  kind: "Color",
3873
- target: nodeToJson(n.target),
4016
+ target: emitNode(n.target, ctx),
3874
4017
  color: [...n.color]
3875
4018
  };
3876
4019
  if (n.kind === "Fillet") return {
3877
4020
  kind: "Fillet",
3878
- target: nodeToJson(n.target),
4021
+ target: emitNode(n.target, ctx),
3879
4022
  ref: edgeRefToJson(n.ref),
3880
4023
  radius: exprToJson(n.radius)
3881
4024
  };
3882
4025
  if (n.kind === "Chamfer") return {
3883
4026
  kind: "Chamfer",
3884
- target: nodeToJson(n.target),
4027
+ target: emitNode(n.target, ctx),
3885
4028
  ref: edgeRefToJson(n.ref),
3886
4029
  distance: exprToJson(n.distance)
3887
4030
  };
3888
4031
  if (n.kind === "Shell") return {
3889
4032
  kind: "Shell",
3890
- target: nodeToJson(n.target),
4033
+ target: emitNode(n.target, ctx),
3891
4034
  refs: n.refs.map(shapeRefToJson),
3892
4035
  thickness: exprToJson(n.thickness)
3893
4036
  };
3894
4037
  if (n.kind === "Compound") return {
3895
4038
  kind: "Compound",
3896
- children: n.children.map(nodeToJson)
4039
+ children: n.children.map((c) => emitNode(c, ctx))
3897
4040
  };
3898
4041
  if (n.kind === "Instance") return {
3899
4042
  kind: "Instance",
3900
- source: nodeToJson(n.source),
4043
+ source: emitNode(n.source, ctx),
3901
4044
  placements: n.placements,
3902
4045
  fuse: n.fuse
3903
4046
  };
3904
- return primitiveToJson(n) ?? booleanToJson(n) ?? transformToJson(n);
4047
+ return featureToJson(n, ctx) ?? primitiveToJson(n) ?? booleanToJson(n, ctx) ?? transformToJson(n, ctx);
3905
4048
  }
3906
4049
  function bad(msg) {
3907
4050
  return err(validationError(BrepErrorCode.NULL_SHAPE_INPUT, `csg.fromJSON: ${msg}`));
@@ -3934,9 +4077,18 @@ function readVec2(v, where) {
3934
4077
  function fromJSON(envelope) {
3935
4078
  if (!isObj(envelope)) return bad("input is not an object");
3936
4079
  const v = envelope["csgVersion"];
3937
- if (typeof v !== "number" || !Number.isInteger(v) || v < MIN_CSG_VERSION || v > 7) return bad(`unsupported csgVersion ${String(v)} (expected ${MIN_CSG_VERSION}..7)`);
3938
- const root = envelope["root"];
3939
- return readNode(root);
4080
+ if (typeof v !== "number" || !Number.isInteger(v) || v < MIN_CSG_VERSION || v > 8) return bad(`unsupported csgVersion ${String(v)} (expected ${MIN_CSG_VERSION}..8)`);
4081
+ const rawDefs = envelope["defs"];
4082
+ const defs = [];
4083
+ if (rawDefs !== void 0) {
4084
+ if (!Array.isArray(rawDefs)) return bad("defs: not an array");
4085
+ for (const raw of rawDefs) {
4086
+ const r = readNode(raw, defs);
4087
+ if (!r.ok) return r;
4088
+ defs.push(r.value);
4089
+ }
4090
+ }
4091
+ return readNode(envelope["root"], defs);
3940
4092
  }
3941
4093
  function readExpr(j) {
3942
4094
  if (!isObj(j)) return bad("expression: not an object");
@@ -4002,11 +4154,11 @@ function readBuildVec(j) {
4002
4154
  }
4003
4155
  return ok(buildVec(dim, out));
4004
4156
  }
4005
- function readNodeArray(j, where) {
4157
+ function readNodeArray(j, where, defs) {
4006
4158
  if (!Array.isArray(j)) return bad(`${where}: not array`);
4007
4159
  const out = [];
4008
4160
  for (const c of j) {
4009
- const r = readNode(c);
4161
+ const r = readNode(c, defs);
4010
4162
  if (!r.ok) return r;
4011
4163
  out.push(r.value);
4012
4164
  }
@@ -4017,8 +4169,15 @@ function readOptTolerance(j) {
4017
4169
  if (t === void 0 || t === null) return ok(void 0);
4018
4170
  return isNumber$1(t) ? ok(t) : bad("tolerance: not a finite number");
4019
4171
  }
4020
- function readNode(j) {
4172
+ function readNode(j, defs) {
4021
4173
  if (!isObj(j)) return bad("node: not an object");
4174
+ const ref = j["$ref"];
4175
+ if (ref !== void 0) {
4176
+ if (!isNumber$1(ref) || !Number.isInteger(ref) || ref < 0) return bad("$ref: expected a non-negative integer def index");
4177
+ const node = defs[ref];
4178
+ if (node === void 0) return bad(`$ref: ${ref} is out of range (forward refs are rejected)`);
4179
+ return ok(node);
4180
+ }
4022
4181
  const kind = j["kind"];
4023
4182
  switch (kind) {
4024
4183
  case "Box":
@@ -4033,25 +4192,25 @@ function readNode(j) {
4033
4192
  case "Empty": return readPrimitive(kind, j);
4034
4193
  case "Fuse":
4035
4194
  case "Cut":
4036
- case "Intersect": return readBinaryBool(kind, j);
4195
+ case "Intersect": return readBinaryBool(kind, j, defs);
4037
4196
  case "FuseAll":
4038
- case "CutAll": return readNaryBool(kind, j);
4197
+ case "CutAll": return readNaryBool(kind, j, defs);
4039
4198
  case "Translate":
4040
4199
  case "Rotate":
4041
4200
  case "Scale":
4042
- case "Mirror": return readTransform(kind, j);
4043
- case "Compound": return readCompound(j);
4044
- case "Instance": return readInstance(j);
4045
- case "Extrude": return readExtrude(j);
4046
- case "Revolve": return readRevolve(j);
4047
- case "Loft": return readLoft(j);
4201
+ case "Mirror": return readTransform(kind, j, defs);
4202
+ case "Compound": return readCompound(j, defs);
4203
+ case "Instance": return readInstance(j, defs);
4204
+ case "Extrude": return readExtrude(j, defs);
4205
+ case "Revolve": return readRevolve(j, defs);
4206
+ case "Loft": return readLoft(j, defs);
4048
4207
  case "Path": return readPath(j);
4049
- case "Sweep": return readSweep(j);
4208
+ case "Sweep": return readSweep(j, defs);
4050
4209
  case "Profile": return readProfile(j);
4051
- case "Color": return readColor(j);
4052
- case "Fillet": return readFillet(j);
4053
- case "Chamfer": return readChamfer(j);
4054
- case "Shell": return readShell(j);
4210
+ case "Color": return readColor(j, defs);
4211
+ case "Fillet": return readFillet(j, defs);
4212
+ case "Chamfer": return readChamfer(j, defs);
4213
+ case "Shell": return readShell(j, defs);
4055
4214
  default: return bad(`unknown node kind: ${String(kind)}`);
4056
4215
  }
4057
4216
  }
@@ -4133,10 +4292,10 @@ function readEmpty(j) {
4133
4292
  default: return bad(`Empty.output: ${String(out)}`);
4134
4293
  }
4135
4294
  }
4136
- function readBinaryBool(kind, j) {
4137
- const a = readNode(j["a"]);
4295
+ function readBinaryBool(kind, j, defs) {
4296
+ const a = readNode(j["a"], defs);
4138
4297
  if (!a.ok) return a;
4139
- const b = readNode(j["b"]);
4298
+ const b = readNode(j["b"], defs);
4140
4299
  if (!b.ok) return b;
4141
4300
  const t = readOptTolerance(j);
4142
4301
  if (!t.ok) return t;
@@ -4146,20 +4305,20 @@ function readBinaryBool(kind, j) {
4146
4305
  case "Intersect": return ok(intersect$1(a.value, b.value, t.value));
4147
4306
  }
4148
4307
  }
4149
- function readNaryBool(kind, j) {
4308
+ function readNaryBool(kind, j, defs) {
4150
4309
  const t = readOptTolerance(j);
4151
4310
  if (!t.ok) return t;
4152
4311
  if (kind === "FuseAll") {
4153
- const shapes = readNodeArray(j["shapes"], "FuseAll.shapes");
4312
+ const shapes = readNodeArray(j["shapes"], "FuseAll.shapes", defs);
4154
4313
  return shapes.ok ? ok(fuseAll$1(shapes.value, t.value)) : shapes;
4155
4314
  }
4156
- const base = readNode(j["base"]);
4315
+ const base = readNode(j["base"], defs);
4157
4316
  if (!base.ok) return base;
4158
- const tools = readNodeArray(j["tools"], "CutAll.tools");
4317
+ const tools = readNodeArray(j["tools"], "CutAll.tools", defs);
4159
4318
  return tools.ok ? ok(cutAll$1(base.value, tools.value, t.value)) : tools;
4160
4319
  }
4161
- function readTransform(kind, j) {
4162
- const tgt = readNode(j["target"]);
4320
+ function readTransform(kind, j, defs) {
4321
+ const tgt = readNode(j["target"], defs);
4163
4322
  if (!tgt.ok) return tgt;
4164
4323
  switch (kind) {
4165
4324
  case "Translate": return readTranslate(j, tgt.value);
@@ -4206,15 +4365,15 @@ function readMirror(j, target) {
4206
4365
  at: at.value
4207
4366
  }));
4208
4367
  }
4209
- function readExtrude(j) {
4210
- const profile = readNode(j["profile"]);
4368
+ function readExtrude(j, defs) {
4369
+ const profile = readNode(j["profile"], defs);
4211
4370
  if (!profile.ok) return profile;
4212
4371
  const vector = readExpr(j["vector"]);
4213
4372
  if (!vector.ok) return vector;
4214
4373
  return ok(extrude$1(profile.value, vector.value));
4215
4374
  }
4216
- function readRevolve(j) {
4217
- const profile = readNode(j["profile"]);
4375
+ function readRevolve(j, defs) {
4376
+ const profile = readNode(j["profile"], defs);
4218
4377
  if (!profile.ok) return profile;
4219
4378
  const angle = readExpr(j["angle"]);
4220
4379
  if (!angle.ok) return angle;
@@ -4227,8 +4386,8 @@ function readRevolve(j) {
4227
4386
  at: at.value
4228
4387
  }));
4229
4388
  }
4230
- function readLoft(j) {
4231
- const sections = readNodeArray(j["sections"], "Loft.sections");
4389
+ function readLoft(j, defs) {
4390
+ const sections = readNodeArray(j["sections"], "Loft.sections", defs);
4232
4391
  if (!sections.ok) return sections;
4233
4392
  const ruled = j["ruled"];
4234
4393
  if (ruled !== void 0 && typeof ruled !== "boolean") return bad("Loft.ruled: not a boolean");
@@ -4376,8 +4535,8 @@ function readShapeRef(j, where) {
4376
4535
  }
4377
4536
  });
4378
4537
  }
4379
- function readShell(j) {
4380
- const target = readNode(j["target"]);
4538
+ function readShell(j, defs) {
4539
+ const target = readNode(j["target"], defs);
4381
4540
  if (!target.ok) return target;
4382
4541
  const thickness = readExpr(j["thickness"]);
4383
4542
  if (!thickness.ok) return thickness;
@@ -4391,8 +4550,8 @@ function readShell(j) {
4391
4550
  }
4392
4551
  return ok(shell$1(target.value, refs, thickness.value));
4393
4552
  }
4394
- function readFillet(j) {
4395
- const target = readNode(j["target"]);
4553
+ function readFillet(j, defs) {
4554
+ const target = readNode(j["target"], defs);
4396
4555
  if (!target.ok) return target;
4397
4556
  const radius = readExpr(j["radius"]);
4398
4557
  if (!radius.ok) return radius;
@@ -4400,8 +4559,8 @@ function readFillet(j) {
4400
4559
  if (!ref.ok) return ref;
4401
4560
  return ok(fillet$1(target.value, ref.value, radius.value));
4402
4561
  }
4403
- function readChamfer(j) {
4404
- const target = readNode(j["target"]);
4562
+ function readChamfer(j, defs) {
4563
+ const target = readNode(j["target"], defs);
4405
4564
  if (!target.ok) return target;
4406
4565
  const distance = readExpr(j["distance"]);
4407
4566
  if (!distance.ok) return distance;
@@ -4409,8 +4568,8 @@ function readChamfer(j) {
4409
4568
  if (!ref.ok) return ref;
4410
4569
  return ok(chamfer$1(target.value, ref.value, distance.value));
4411
4570
  }
4412
- function readColor(j) {
4413
- const target = readNode(j["target"]);
4571
+ function readColor(j, defs) {
4572
+ const target = readNode(j["target"], defs);
4414
4573
  if (!target.ok) return target;
4415
4574
  const c = j["color"];
4416
4575
  if (!Array.isArray(c) || c.length !== 4 || !c.every((v) => isNumber$1(v) && v >= 0 && v <= 1)) return bad("Color.color: expected an RGBA array of 4 numbers in [0, 1]");
@@ -4434,10 +4593,10 @@ function readProfile(j) {
4434
4593
  }
4435
4594
  return ok(profile(outline.value, holes));
4436
4595
  }
4437
- function readSweep(j) {
4438
- const profile = readNode(j["profile"]);
4596
+ function readSweep(j, defs) {
4597
+ const profile = readNode(j["profile"], defs);
4439
4598
  if (!profile.ok) return profile;
4440
- const spine = readNode(j["spine"]);
4599
+ const spine = readNode(j["spine"], defs);
4441
4600
  if (!spine.ok) return spine;
4442
4601
  const frenet = readFlag(j, "frenet");
4443
4602
  if (!frenet.ok) return frenet;
@@ -4456,15 +4615,15 @@ function readPath(j) {
4456
4615
  }
4457
4616
  return ok(path(start.value, segments));
4458
4617
  }
4459
- function readCompound(j) {
4460
- const children = readNodeArray(j["children"], "Compound.children");
4618
+ function readCompound(j, defs) {
4619
+ const children = readNodeArray(j["children"], "Compound.children", defs);
4461
4620
  return children.ok ? ok(compound$1(children.value)) : children;
4462
4621
  }
4463
4622
  function isMatrix4x4(v) {
4464
4623
  return Array.isArray(v) && v.length === 4 && v.every((row) => Array.isArray(row) && row.length === 4 && row.every(isNumber$1));
4465
4624
  }
4466
- function readInstance(j) {
4467
- const source = readNode(j["source"]);
4625
+ function readInstance(j, defs) {
4626
+ const source = readNode(j["source"], defs);
4468
4627
  if (!source.ok) return source;
4469
4628
  const placements = j["placements"];
4470
4629
  if (!Array.isArray(placements) || !placements.every(isMatrix4x4)) return bad("Instance.placements must be an array of 4x4 number matrices");
@@ -4662,128 +4821,6 @@ function optimizeTranslate(target, vector) {
4662
4821
  return translate$1(ot, ov);
4663
4822
  }
4664
4823
  //#endregion
4665
- //#region src/csg/edit.ts
4666
- function replaceNode(root, pred, replacement) {
4667
- return walk(root, pred, replacement, /* @__PURE__ */ new Map());
4668
- }
4669
- function walk(node, pred, repl, memo) {
4670
- const cached = memo.get(node);
4671
- if (cached !== void 0) return cached;
4672
- const out = pred(node) ? repl : rebuildChildren(node, pred, repl, memo);
4673
- memo.set(node, out);
4674
- return out;
4675
- }
4676
- function rebuildChildren(n, pred, repl, memo) {
4677
- switch (n.kind) {
4678
- case "Box":
4679
- case "Sphere":
4680
- case "Cylinder":
4681
- case "Cone":
4682
- case "Torus":
4683
- case "Polygon":
4684
- case "Circle":
4685
- case "Line":
4686
- case "Vertex":
4687
- case "Empty":
4688
- case "Path":
4689
- case "Profile": return n;
4690
- case "Fuse": return fuse$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
4691
- case "Cut": return cut$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
4692
- case "Intersect": return intersect$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
4693
- case "FuseAll": return fuseAll$1(n.shapes.map((c) => walk(c, pred, repl, memo)), n.tolerance);
4694
- case "CutAll": return cutAll$1(walk(n.base, pred, repl, memo), n.tools.map((c) => walk(c, pred, repl, memo)), n.tolerance);
4695
- case "Translate": return translate$1(walk(n.target, pred, repl, memo), n.vector);
4696
- case "Rotate": return rotate$1(walk(n.target, pred, repl, memo), n.angle, {
4697
- axis: n.axis,
4698
- at: n.at
4699
- });
4700
- case "Scale": return scale$2(walk(n.target, pred, repl, memo), n.factor, { center: n.center });
4701
- case "Mirror": return mirror$1(walk(n.target, pred, repl, memo), {
4702
- normal: n.normal,
4703
- at: n.at
4704
- });
4705
- case "Compound": return compound$1(n.children.map((c) => walk(c, pred, repl, memo)));
4706
- case "Instance": return instance$1(walk(n.source, pred, repl, memo), n.placements, n.fuse);
4707
- case "Extrude":
4708
- case "Revolve":
4709
- case "Loft":
4710
- case "Sweep":
4711
- case "Color":
4712
- case "Fillet":
4713
- case "Chamfer":
4714
- case "Shell": return rebuildFeature(n, pred, repl, memo);
4715
- }
4716
- }
4717
- function rebuildFeature(n, pred, repl, memo) {
4718
- switch (n.kind) {
4719
- case "Extrude": return extrude$1(walk(n.profile, pred, repl, memo), n.vector);
4720
- case "Revolve": return revolve$1(walk(n.profile, pred, repl, memo), n.angle, {
4721
- axis: n.axis,
4722
- at: n.at
4723
- });
4724
- case "Loft": return loft$1(n.sections.map((s) => walk(s, pred, repl, memo)), { ruled: n.ruled });
4725
- case "Sweep": return sweep$2(walk(n.profile, pred, repl, memo), walk(n.spine, pred, repl, memo), { frenet: n.frenet });
4726
- case "Color": return color(walk(n.target, pred, repl, memo), [...n.color]);
4727
- case "Fillet": return fillet$1(walk(n.target, pred, repl, memo), n.ref, n.radius);
4728
- case "Chamfer": return chamfer$1(walk(n.target, pred, repl, memo), n.ref, n.distance);
4729
- case "Shell": return shell$1(walk(n.target, pred, repl, memo), n.refs, n.thickness);
4730
- }
4731
- }
4732
- /** Visits each DISTINCT node once (identity-deduplicated), so a shared
4733
- * subtree is reported once and a self-referencing DAG walks in linear time. */
4734
- function forEachNode(root, fn) {
4735
- const seen = /* @__PURE__ */ new Set();
4736
- const visit = (node) => {
4737
- if (seen.has(node)) return;
4738
- seen.add(node);
4739
- fn(node);
4740
- for (const child of childrenOf(node)) visit(child);
4741
- };
4742
- visit(root);
4743
- }
4744
- function childrenOf(n) {
4745
- switch (n.kind) {
4746
- case "Box":
4747
- case "Sphere":
4748
- case "Cylinder":
4749
- case "Cone":
4750
- case "Torus":
4751
- case "Polygon":
4752
- case "Circle":
4753
- case "Line":
4754
- case "Vertex":
4755
- case "Empty":
4756
- case "Path":
4757
- case "Profile": return [];
4758
- case "Fuse":
4759
- case "Cut":
4760
- case "Intersect": return [n.a, n.b];
4761
- case "FuseAll": return n.shapes;
4762
- case "CutAll": return [n.base, ...n.tools];
4763
- case "Translate":
4764
- case "Rotate":
4765
- case "Scale":
4766
- case "Mirror":
4767
- case "Color":
4768
- case "Fillet":
4769
- case "Chamfer":
4770
- case "Shell": return [n.target];
4771
- case "Compound": return n.children;
4772
- case "Instance": return [n.source];
4773
- case "Extrude":
4774
- case "Revolve": return [n.profile];
4775
- case "Loft": return n.sections;
4776
- case "Sweep": return [n.profile, n.spine];
4777
- }
4778
- }
4779
- function nodeCount(root) {
4780
- let n = 0;
4781
- forEachNode(root, () => {
4782
- n++;
4783
- });
4784
- return n;
4785
- }
4786
- //#endregion
4787
4824
  //#region src/sketching/blueprintContourFns.ts
4788
4825
  var EPS$1 = 1e-6;
4789
4826
  function p2(p) {
@@ -8741,7 +8778,7 @@ var patterns_exports = /* @__PURE__ */ __exportAll({
8741
8778
  //#endregion
8742
8779
  //#region src/ns/csg.ts
8743
8780
  var csg_exports = /* @__PURE__ */ __exportAll({
8744
- CSG_VERSION: () => 7,
8781
+ CSG_VERSION: () => 8,
8745
8782
  Evaluator: () => Evaluator,
8746
8783
  add: () => add$1,
8747
8784
  arbitraryClosedProfile: () => arbitraryClosedProfile,