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.cjs CHANGED
@@ -16,7 +16,7 @@ const require_solidBuilders = require("./solidBuilders-BKSCyfzL.cjs");
16
16
  const require_primitiveFns = require("./primitiveFns-BjwF7CHF.cjs");
17
17
  const require_shapeFns = require("./shapeFns-C3AFMiyv.cjs");
18
18
  const require_curveFns = require("./curveFns-DTbY4aYG.cjs");
19
- const require_meshFns = require("./meshFns-cMtop3IN.cjs");
19
+ const require_meshFns = require("./meshFns-BZNqLhDE.cjs");
20
20
  const require_arrayAccess = require("./arrayAccess-e4H9cBfh.cjs");
21
21
  const require_surfaceBuilders = require("./surfaceBuilders-aSFtcDr6.cjs");
22
22
  const require_healingFns = require("./healingFns-CnRyuMWf.cjs");
@@ -3355,20 +3355,6 @@ function relocateFaceGroups(faceGroups, innerHashes, placedHashes) {
3355
3355
  faceId: remap.get(g.faceId) ?? g.faceId
3356
3356
  }));
3357
3357
  }
3358
- var SCALE_INVARIANT_DIAGONAL = 10;
3359
- function scaleDefaultTolerance(base, shape) {
3360
- let diagonal;
3361
- try {
3362
- const b = require_topologyQueryFns.getBounds(shape);
3363
- const dx = b.xMax - b.xMin;
3364
- const dy = b.yMax - b.yMin;
3365
- const dz = b.zMax - b.zMin;
3366
- diagonal = Math.sqrt(dx * dx + dy * dy + dz * dz);
3367
- } catch {
3368
- return base;
3369
- }
3370
- return base * Math.max(1, diagonal / SCALE_INVARIANT_DIAGONAL);
3371
- }
3372
3358
  var Evaluator = class {
3373
3359
  cache = /* @__PURE__ */ new Map();
3374
3360
  refCounts = /* @__PURE__ */ new Map();
@@ -3499,7 +3485,7 @@ var Evaluator = class {
3499
3485
  const { inner } = peelRigid(node, env);
3500
3486
  const source = this.evaluate(inner, env);
3501
3487
  if (!source.ok) return base;
3502
- return scaleDefaultTolerance(base, source.value);
3488
+ return require_meshFns.scaleDefaultTolerance(base, source.value);
3503
3489
  }
3504
3490
  evaluateInner(node, env) {
3505
3491
  const key = cacheKey(node, env, this.kernelId, this.defaultTolerance);
@@ -3606,13 +3592,165 @@ function withEvaluator(options, fn) {
3606
3592
  _usingCtx$4.d();
3607
3593
  }
3608
3594
  }
3595
+ //#endregion
3596
+ //#region src/csg/edit.ts
3597
+ function replaceNode(root, pred, replacement) {
3598
+ return walk(root, pred, replacement, /* @__PURE__ */ new Map());
3599
+ }
3600
+ function walk(node, pred, repl, memo) {
3601
+ const cached = memo.get(node);
3602
+ if (cached !== void 0) return cached;
3603
+ const out = pred(node) ? repl : rebuildChildren(node, pred, repl, memo);
3604
+ memo.set(node, out);
3605
+ return out;
3606
+ }
3607
+ function rebuildChildren(n, pred, repl, memo) {
3608
+ switch (n.kind) {
3609
+ case "Box":
3610
+ case "Sphere":
3611
+ case "Cylinder":
3612
+ case "Cone":
3613
+ case "Torus":
3614
+ case "Polygon":
3615
+ case "Circle":
3616
+ case "Line":
3617
+ case "Vertex":
3618
+ case "Empty":
3619
+ case "Path":
3620
+ case "Profile": return n;
3621
+ case "Fuse": return fuse$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
3622
+ case "Cut": return cut$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
3623
+ case "Intersect": return intersect$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
3624
+ case "FuseAll": return fuseAll$1(n.shapes.map((c) => walk(c, pred, repl, memo)), n.tolerance);
3625
+ case "CutAll": return cutAll$1(walk(n.base, pred, repl, memo), n.tools.map((c) => walk(c, pred, repl, memo)), n.tolerance);
3626
+ case "Translate": return translate$1(walk(n.target, pred, repl, memo), n.vector);
3627
+ case "Rotate": return rotate$1(walk(n.target, pred, repl, memo), n.angle, {
3628
+ axis: n.axis,
3629
+ at: n.at
3630
+ });
3631
+ case "Scale": return scale$2(walk(n.target, pred, repl, memo), n.factor, { center: n.center });
3632
+ case "Mirror": return mirror$1(walk(n.target, pred, repl, memo), {
3633
+ normal: n.normal,
3634
+ at: n.at
3635
+ });
3636
+ case "Compound": return compound$1(n.children.map((c) => walk(c, pred, repl, memo)));
3637
+ case "Instance": return instance$1(walk(n.source, pred, repl, memo), n.placements, n.fuse);
3638
+ case "Extrude":
3639
+ case "Revolve":
3640
+ case "Loft":
3641
+ case "Sweep":
3642
+ case "Color":
3643
+ case "Fillet":
3644
+ case "Chamfer":
3645
+ case "Shell": return rebuildFeature(n, pred, repl, memo);
3646
+ }
3647
+ }
3648
+ function rebuildFeature(n, pred, repl, memo) {
3649
+ switch (n.kind) {
3650
+ case "Extrude": return extrude$1(walk(n.profile, pred, repl, memo), n.vector);
3651
+ case "Revolve": return revolve$1(walk(n.profile, pred, repl, memo), n.angle, {
3652
+ axis: n.axis,
3653
+ at: n.at
3654
+ });
3655
+ case "Loft": return loft$1(n.sections.map((s) => walk(s, pred, repl, memo)), { ruled: n.ruled });
3656
+ case "Sweep": return sweep$2(walk(n.profile, pred, repl, memo), walk(n.spine, pred, repl, memo), { frenet: n.frenet });
3657
+ case "Color": return color(walk(n.target, pred, repl, memo), [...n.color]);
3658
+ case "Fillet": return fillet$1(walk(n.target, pred, repl, memo), n.ref, n.radius);
3659
+ case "Chamfer": return chamfer$1(walk(n.target, pred, repl, memo), n.ref, n.distance);
3660
+ case "Shell": return shell$1(walk(n.target, pred, repl, memo), n.refs, n.thickness);
3661
+ }
3662
+ }
3663
+ /** Visits each DISTINCT node once (identity-deduplicated), so a shared
3664
+ * subtree is reported once and a self-referencing DAG walks in linear time. */
3665
+ function forEachNode(root, fn) {
3666
+ const seen = /* @__PURE__ */ new Set();
3667
+ const visit = (node) => {
3668
+ if (seen.has(node)) return;
3669
+ seen.add(node);
3670
+ fn(node);
3671
+ for (const child of childrenOf(node)) visit(child);
3672
+ };
3673
+ visit(root);
3674
+ }
3675
+ function childrenOf(n) {
3676
+ switch (n.kind) {
3677
+ case "Box":
3678
+ case "Sphere":
3679
+ case "Cylinder":
3680
+ case "Cone":
3681
+ case "Torus":
3682
+ case "Polygon":
3683
+ case "Circle":
3684
+ case "Line":
3685
+ case "Vertex":
3686
+ case "Empty":
3687
+ case "Path":
3688
+ case "Profile": return [];
3689
+ case "Fuse":
3690
+ case "Cut":
3691
+ case "Intersect": return [n.a, n.b];
3692
+ case "FuseAll": return n.shapes;
3693
+ case "CutAll": return [n.base, ...n.tools];
3694
+ case "Translate":
3695
+ case "Rotate":
3696
+ case "Scale":
3697
+ case "Mirror":
3698
+ case "Color":
3699
+ case "Fillet":
3700
+ case "Chamfer":
3701
+ case "Shell": return [n.target];
3702
+ case "Compound": return n.children;
3703
+ case "Instance": return [n.source];
3704
+ case "Extrude":
3705
+ case "Revolve": return [n.profile];
3706
+ case "Loft": return n.sections;
3707
+ case "Sweep": return [n.profile, n.spine];
3708
+ }
3709
+ }
3710
+ function nodeCount(root) {
3711
+ let n = 0;
3712
+ forEachNode(root, () => {
3713
+ n++;
3714
+ });
3715
+ return n;
3716
+ }
3609
3717
  var MIN_CSG_VERSION = 1;
3610
3718
  function toJSON(node) {
3611
- return {
3612
- csgVersion: 7,
3613
- root: nodeToJson(node)
3719
+ const ctx = {
3720
+ counts: countRefs(node),
3721
+ ids: /* @__PURE__ */ new Map(),
3722
+ defs: []
3723
+ };
3724
+ const root = emitNode(node, ctx);
3725
+ return ctx.defs.length > 0 ? {
3726
+ csgVersion: 8,
3727
+ defs: ctx.defs,
3728
+ root
3729
+ } : {
3730
+ csgVersion: 8,
3731
+ root
3614
3732
  };
3615
3733
  }
3734
+ function countRefs(root) {
3735
+ const counts = /* @__PURE__ */ new Map();
3736
+ const visit = (n) => {
3737
+ const seen = counts.get(n) ?? 0;
3738
+ counts.set(n, seen + 1);
3739
+ if (seen === 0) for (const child of childrenOf(n)) visit(child);
3740
+ };
3741
+ visit(root);
3742
+ return counts;
3743
+ }
3744
+ function emitNode(n, ctx) {
3745
+ if ((ctx.counts.get(n) ?? 0) < 2) return nodeToJson(n, ctx);
3746
+ const existing = ctx.ids.get(n);
3747
+ if (existing !== void 0) return { $ref: existing };
3748
+ const body = nodeToJson(n, ctx);
3749
+ const id = ctx.defs.length;
3750
+ ctx.defs.push(body);
3751
+ ctx.ids.set(n, id);
3752
+ return { $ref: id };
3753
+ }
3616
3754
  function exprToJson(e) {
3617
3755
  switch (e.kind) {
3618
3756
  case "NumLit": return {
@@ -3710,25 +3848,25 @@ function primitiveToJson(n) {
3710
3848
  default: return;
3711
3849
  }
3712
3850
  }
3713
- function booleanToJson(n) {
3851
+ function booleanToJson(n, ctx) {
3714
3852
  switch (n.kind) {
3715
3853
  case "Fuse":
3716
3854
  case "Cut":
3717
3855
  case "Intersect": return {
3718
3856
  kind: n.kind,
3719
- a: nodeToJson(n.a),
3720
- b: nodeToJson(n.b),
3857
+ a: emitNode(n.a, ctx),
3858
+ b: emitNode(n.b, ctx),
3721
3859
  tolerance: n.tolerance
3722
3860
  };
3723
3861
  case "FuseAll": return {
3724
3862
  kind: "FuseAll",
3725
- shapes: n.shapes.map(nodeToJson),
3863
+ shapes: n.shapes.map((s) => emitNode(s, ctx)),
3726
3864
  tolerance: n.tolerance
3727
3865
  };
3728
3866
  case "CutAll": return {
3729
3867
  kind: "CutAll",
3730
- base: nodeToJson(n.base),
3731
- tools: n.tools.map(nodeToJson),
3868
+ base: emitNode(n.base, ctx),
3869
+ tools: n.tools.map((t) => emitNode(t, ctx)),
3732
3870
  tolerance: n.tolerance
3733
3871
  };
3734
3872
  default: return;
@@ -3795,103 +3933,108 @@ function contourToJson(c) {
3795
3933
  segments: c.segments.map(segmentToJson)
3796
3934
  };
3797
3935
  }
3798
- function transformToJson(n) {
3936
+ function transformToJson(n, ctx) {
3799
3937
  switch (n.kind) {
3800
3938
  case "Translate": return {
3801
3939
  kind: "Translate",
3802
- target: nodeToJson(n.target),
3940
+ target: emitNode(n.target, ctx),
3803
3941
  vector: exprToJson(n.vector)
3804
3942
  };
3805
3943
  case "Rotate": return {
3806
3944
  kind: "Rotate",
3807
- target: nodeToJson(n.target),
3945
+ target: emitNode(n.target, ctx),
3808
3946
  angle: exprToJson(n.angle),
3809
3947
  axis: optExprToJson(n.axis),
3810
3948
  at: optExprToJson(n.at)
3811
3949
  };
3812
3950
  case "Scale": return {
3813
3951
  kind: "Scale",
3814
- target: nodeToJson(n.target),
3952
+ target: emitNode(n.target, ctx),
3815
3953
  factor: exprToJson(n.factor),
3816
3954
  center: optExprToJson(n.center)
3817
3955
  };
3818
3956
  case "Mirror": return {
3819
3957
  kind: "Mirror",
3820
- target: nodeToJson(n.target),
3958
+ target: emitNode(n.target, ctx),
3821
3959
  normal: optExprToJson(n.normal),
3822
3960
  at: optExprToJson(n.at)
3823
3961
  };
3824
3962
  default: return;
3825
3963
  }
3826
3964
  }
3827
- function nodeToJson(n) {
3828
- if (n.kind === "Extrude") return {
3829
- kind: "Extrude",
3830
- profile: nodeToJson(n.profile),
3831
- vector: exprToJson(n.vector)
3832
- };
3833
- if (n.kind === "Revolve") return {
3834
- kind: "Revolve",
3835
- profile: nodeToJson(n.profile),
3836
- angle: exprToJson(n.angle),
3837
- axis: optExprToJson(n.axis),
3838
- at: optExprToJson(n.at)
3839
- };
3840
- if (n.kind === "Loft") return {
3841
- kind: "Loft",
3842
- sections: n.sections.map(nodeToJson),
3843
- ruled: n.ruled
3844
- };
3845
- if (n.kind === "Path") return {
3846
- kind: "Path",
3847
- start: exprToJson(n.start),
3848
- segments: n.segments.map(segmentToJson)
3849
- };
3850
- if (n.kind === "Sweep") return {
3851
- kind: "Sweep",
3852
- profile: nodeToJson(n.profile),
3853
- spine: nodeToJson(n.spine),
3854
- frenet: n.frenet
3855
- };
3856
- if (n.kind === "Profile") return {
3857
- kind: "Profile",
3858
- outline: contourToJson(n.outline),
3859
- holes: n.holes.map(contourToJson)
3860
- };
3965
+ function featureToJson(n, ctx) {
3966
+ switch (n.kind) {
3967
+ case "Extrude": return {
3968
+ kind: "Extrude",
3969
+ profile: emitNode(n.profile, ctx),
3970
+ vector: exprToJson(n.vector)
3971
+ };
3972
+ case "Revolve": return {
3973
+ kind: "Revolve",
3974
+ profile: emitNode(n.profile, ctx),
3975
+ angle: exprToJson(n.angle),
3976
+ axis: optExprToJson(n.axis),
3977
+ at: optExprToJson(n.at)
3978
+ };
3979
+ case "Loft": return {
3980
+ kind: "Loft",
3981
+ sections: n.sections.map((s) => emitNode(s, ctx)),
3982
+ ruled: n.ruled
3983
+ };
3984
+ case "Path": return {
3985
+ kind: "Path",
3986
+ start: exprToJson(n.start),
3987
+ segments: n.segments.map(segmentToJson)
3988
+ };
3989
+ case "Sweep": return {
3990
+ kind: "Sweep",
3991
+ profile: emitNode(n.profile, ctx),
3992
+ spine: emitNode(n.spine, ctx),
3993
+ frenet: n.frenet
3994
+ };
3995
+ case "Profile": return {
3996
+ kind: "Profile",
3997
+ outline: contourToJson(n.outline),
3998
+ holes: n.holes.map(contourToJson)
3999
+ };
4000
+ default: return;
4001
+ }
4002
+ }
4003
+ function nodeToJson(n, ctx) {
3861
4004
  if (n.kind === "Color") return {
3862
4005
  kind: "Color",
3863
- target: nodeToJson(n.target),
4006
+ target: emitNode(n.target, ctx),
3864
4007
  color: [...n.color]
3865
4008
  };
3866
4009
  if (n.kind === "Fillet") return {
3867
4010
  kind: "Fillet",
3868
- target: nodeToJson(n.target),
4011
+ target: emitNode(n.target, ctx),
3869
4012
  ref: edgeRefToJson(n.ref),
3870
4013
  radius: exprToJson(n.radius)
3871
4014
  };
3872
4015
  if (n.kind === "Chamfer") return {
3873
4016
  kind: "Chamfer",
3874
- target: nodeToJson(n.target),
4017
+ target: emitNode(n.target, ctx),
3875
4018
  ref: edgeRefToJson(n.ref),
3876
4019
  distance: exprToJson(n.distance)
3877
4020
  };
3878
4021
  if (n.kind === "Shell") return {
3879
4022
  kind: "Shell",
3880
- target: nodeToJson(n.target),
4023
+ target: emitNode(n.target, ctx),
3881
4024
  refs: n.refs.map(shapeRefToJson),
3882
4025
  thickness: exprToJson(n.thickness)
3883
4026
  };
3884
4027
  if (n.kind === "Compound") return {
3885
4028
  kind: "Compound",
3886
- children: n.children.map(nodeToJson)
4029
+ children: n.children.map((c) => emitNode(c, ctx))
3887
4030
  };
3888
4031
  if (n.kind === "Instance") return {
3889
4032
  kind: "Instance",
3890
- source: nodeToJson(n.source),
4033
+ source: emitNode(n.source, ctx),
3891
4034
  placements: n.placements,
3892
4035
  fuse: n.fuse
3893
4036
  };
3894
- return primitiveToJson(n) ?? booleanToJson(n) ?? transformToJson(n);
4037
+ return featureToJson(n, ctx) ?? primitiveToJson(n) ?? booleanToJson(n, ctx) ?? transformToJson(n, ctx);
3895
4038
  }
3896
4039
  function bad(msg) {
3897
4040
  return require_errors.err(require_errors.validationError(require_errors.BrepErrorCode.NULL_SHAPE_INPUT, `csg.fromJSON: ${msg}`));
@@ -3924,9 +4067,18 @@ function readVec2(v, where) {
3924
4067
  function fromJSON(envelope) {
3925
4068
  if (!isObj(envelope)) return bad("input is not an object");
3926
4069
  const v = envelope["csgVersion"];
3927
- if (typeof v !== "number" || !Number.isInteger(v) || v < MIN_CSG_VERSION || v > 7) return bad(`unsupported csgVersion ${String(v)} (expected ${MIN_CSG_VERSION}..7)`);
3928
- const root = envelope["root"];
3929
- return readNode(root);
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)`);
4071
+ const rawDefs = envelope["defs"];
4072
+ const defs = [];
4073
+ if (rawDefs !== void 0) {
4074
+ if (!Array.isArray(rawDefs)) return bad("defs: not an array");
4075
+ for (const raw of rawDefs) {
4076
+ const r = readNode(raw, defs);
4077
+ if (!r.ok) return r;
4078
+ defs.push(r.value);
4079
+ }
4080
+ }
4081
+ return readNode(envelope["root"], defs);
3930
4082
  }
3931
4083
  function readExpr(j) {
3932
4084
  if (!isObj(j)) return bad("expression: not an object");
@@ -3992,11 +4144,11 @@ function readBuildVec(j) {
3992
4144
  }
3993
4145
  return require_errors.ok(buildVec(dim, out));
3994
4146
  }
3995
- function readNodeArray(j, where) {
4147
+ function readNodeArray(j, where, defs) {
3996
4148
  if (!Array.isArray(j)) return bad(`${where}: not array`);
3997
4149
  const out = [];
3998
4150
  for (const c of j) {
3999
- const r = readNode(c);
4151
+ const r = readNode(c, defs);
4000
4152
  if (!r.ok) return r;
4001
4153
  out.push(r.value);
4002
4154
  }
@@ -4007,8 +4159,15 @@ function readOptTolerance(j) {
4007
4159
  if (t === void 0 || t === null) return require_errors.ok(void 0);
4008
4160
  return isNumber$1(t) ? require_errors.ok(t) : bad("tolerance: not a finite number");
4009
4161
  }
4010
- function readNode(j) {
4162
+ function readNode(j, defs) {
4011
4163
  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
+ }
4012
4171
  const kind = j["kind"];
4013
4172
  switch (kind) {
4014
4173
  case "Box":
@@ -4023,25 +4182,25 @@ function readNode(j) {
4023
4182
  case "Empty": return readPrimitive(kind, j);
4024
4183
  case "Fuse":
4025
4184
  case "Cut":
4026
- case "Intersect": return readBinaryBool(kind, j);
4185
+ case "Intersect": return readBinaryBool(kind, j, defs);
4027
4186
  case "FuseAll":
4028
- case "CutAll": return readNaryBool(kind, j);
4187
+ case "CutAll": return readNaryBool(kind, j, defs);
4029
4188
  case "Translate":
4030
4189
  case "Rotate":
4031
4190
  case "Scale":
4032
- case "Mirror": return readTransform(kind, j);
4033
- case "Compound": return readCompound(j);
4034
- case "Instance": return readInstance(j);
4035
- case "Extrude": return readExtrude(j);
4036
- case "Revolve": return readRevolve(j);
4037
- case "Loft": return readLoft(j);
4191
+ case "Mirror": return readTransform(kind, j, defs);
4192
+ case "Compound": return readCompound(j, defs);
4193
+ case "Instance": return readInstance(j, defs);
4194
+ case "Extrude": return readExtrude(j, defs);
4195
+ case "Revolve": return readRevolve(j, defs);
4196
+ case "Loft": return readLoft(j, defs);
4038
4197
  case "Path": return readPath(j);
4039
- case "Sweep": return readSweep(j);
4198
+ case "Sweep": return readSweep(j, defs);
4040
4199
  case "Profile": return readProfile(j);
4041
- case "Color": return readColor(j);
4042
- case "Fillet": return readFillet(j);
4043
- case "Chamfer": return readChamfer(j);
4044
- case "Shell": return readShell(j);
4200
+ case "Color": return readColor(j, defs);
4201
+ case "Fillet": return readFillet(j, defs);
4202
+ case "Chamfer": return readChamfer(j, defs);
4203
+ case "Shell": return readShell(j, defs);
4045
4204
  default: return bad(`unknown node kind: ${String(kind)}`);
4046
4205
  }
4047
4206
  }
@@ -4123,10 +4282,10 @@ function readEmpty(j) {
4123
4282
  default: return bad(`Empty.output: ${String(out)}`);
4124
4283
  }
4125
4284
  }
4126
- function readBinaryBool(kind, j) {
4127
- const a = readNode(j["a"]);
4285
+ function readBinaryBool(kind, j, defs) {
4286
+ const a = readNode(j["a"], defs);
4128
4287
  if (!a.ok) return a;
4129
- const b = readNode(j["b"]);
4288
+ const b = readNode(j["b"], defs);
4130
4289
  if (!b.ok) return b;
4131
4290
  const t = readOptTolerance(j);
4132
4291
  if (!t.ok) return t;
@@ -4136,20 +4295,20 @@ function readBinaryBool(kind, j) {
4136
4295
  case "Intersect": return require_errors.ok(intersect$1(a.value, b.value, t.value));
4137
4296
  }
4138
4297
  }
4139
- function readNaryBool(kind, j) {
4298
+ function readNaryBool(kind, j, defs) {
4140
4299
  const t = readOptTolerance(j);
4141
4300
  if (!t.ok) return t;
4142
4301
  if (kind === "FuseAll") {
4143
- const shapes = readNodeArray(j["shapes"], "FuseAll.shapes");
4302
+ const shapes = readNodeArray(j["shapes"], "FuseAll.shapes", defs);
4144
4303
  return shapes.ok ? require_errors.ok(fuseAll$1(shapes.value, t.value)) : shapes;
4145
4304
  }
4146
- const base = readNode(j["base"]);
4305
+ const base = readNode(j["base"], defs);
4147
4306
  if (!base.ok) return base;
4148
- const tools = readNodeArray(j["tools"], "CutAll.tools");
4307
+ const tools = readNodeArray(j["tools"], "CutAll.tools", defs);
4149
4308
  return tools.ok ? require_errors.ok(cutAll$1(base.value, tools.value, t.value)) : tools;
4150
4309
  }
4151
- function readTransform(kind, j) {
4152
- const tgt = readNode(j["target"]);
4310
+ function readTransform(kind, j, defs) {
4311
+ const tgt = readNode(j["target"], defs);
4153
4312
  if (!tgt.ok) return tgt;
4154
4313
  switch (kind) {
4155
4314
  case "Translate": return readTranslate(j, tgt.value);
@@ -4196,15 +4355,15 @@ function readMirror(j, target) {
4196
4355
  at: at.value
4197
4356
  }));
4198
4357
  }
4199
- function readExtrude(j) {
4200
- const profile = readNode(j["profile"]);
4358
+ function readExtrude(j, defs) {
4359
+ const profile = readNode(j["profile"], defs);
4201
4360
  if (!profile.ok) return profile;
4202
4361
  const vector = readExpr(j["vector"]);
4203
4362
  if (!vector.ok) return vector;
4204
4363
  return require_errors.ok(extrude$1(profile.value, vector.value));
4205
4364
  }
4206
- function readRevolve(j) {
4207
- const profile = readNode(j["profile"]);
4365
+ function readRevolve(j, defs) {
4366
+ const profile = readNode(j["profile"], defs);
4208
4367
  if (!profile.ok) return profile;
4209
4368
  const angle = readExpr(j["angle"]);
4210
4369
  if (!angle.ok) return angle;
@@ -4217,8 +4376,8 @@ function readRevolve(j) {
4217
4376
  at: at.value
4218
4377
  }));
4219
4378
  }
4220
- function readLoft(j) {
4221
- const sections = readNodeArray(j["sections"], "Loft.sections");
4379
+ function readLoft(j, defs) {
4380
+ const sections = readNodeArray(j["sections"], "Loft.sections", defs);
4222
4381
  if (!sections.ok) return sections;
4223
4382
  const ruled = j["ruled"];
4224
4383
  if (ruled !== void 0 && typeof ruled !== "boolean") return bad("Loft.ruled: not a boolean");
@@ -4366,8 +4525,8 @@ function readShapeRef(j, where) {
4366
4525
  }
4367
4526
  });
4368
4527
  }
4369
- function readShell(j) {
4370
- const target = readNode(j["target"]);
4528
+ function readShell(j, defs) {
4529
+ const target = readNode(j["target"], defs);
4371
4530
  if (!target.ok) return target;
4372
4531
  const thickness = readExpr(j["thickness"]);
4373
4532
  if (!thickness.ok) return thickness;
@@ -4381,8 +4540,8 @@ function readShell(j) {
4381
4540
  }
4382
4541
  return require_errors.ok(shell$1(target.value, refs, thickness.value));
4383
4542
  }
4384
- function readFillet(j) {
4385
- const target = readNode(j["target"]);
4543
+ function readFillet(j, defs) {
4544
+ const target = readNode(j["target"], defs);
4386
4545
  if (!target.ok) return target;
4387
4546
  const radius = readExpr(j["radius"]);
4388
4547
  if (!radius.ok) return radius;
@@ -4390,8 +4549,8 @@ function readFillet(j) {
4390
4549
  if (!ref.ok) return ref;
4391
4550
  return require_errors.ok(fillet$1(target.value, ref.value, radius.value));
4392
4551
  }
4393
- function readChamfer(j) {
4394
- const target = readNode(j["target"]);
4552
+ function readChamfer(j, defs) {
4553
+ const target = readNode(j["target"], defs);
4395
4554
  if (!target.ok) return target;
4396
4555
  const distance = readExpr(j["distance"]);
4397
4556
  if (!distance.ok) return distance;
@@ -4399,8 +4558,8 @@ function readChamfer(j) {
4399
4558
  if (!ref.ok) return ref;
4400
4559
  return require_errors.ok(chamfer$1(target.value, ref.value, distance.value));
4401
4560
  }
4402
- function readColor(j) {
4403
- const target = readNode(j["target"]);
4561
+ function readColor(j, defs) {
4562
+ const target = readNode(j["target"], defs);
4404
4563
  if (!target.ok) return target;
4405
4564
  const c = j["color"];
4406
4565
  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]");
@@ -4424,10 +4583,10 @@ function readProfile(j) {
4424
4583
  }
4425
4584
  return require_errors.ok(profile(outline.value, holes));
4426
4585
  }
4427
- function readSweep(j) {
4428
- const profile = readNode(j["profile"]);
4586
+ function readSweep(j, defs) {
4587
+ const profile = readNode(j["profile"], defs);
4429
4588
  if (!profile.ok) return profile;
4430
- const spine = readNode(j["spine"]);
4589
+ const spine = readNode(j["spine"], defs);
4431
4590
  if (!spine.ok) return spine;
4432
4591
  const frenet = readFlag(j, "frenet");
4433
4592
  if (!frenet.ok) return frenet;
@@ -4446,15 +4605,15 @@ function readPath(j) {
4446
4605
  }
4447
4606
  return require_errors.ok(path(start.value, segments));
4448
4607
  }
4449
- function readCompound(j) {
4450
- const children = readNodeArray(j["children"], "Compound.children");
4608
+ function readCompound(j, defs) {
4609
+ const children = readNodeArray(j["children"], "Compound.children", defs);
4451
4610
  return children.ok ? require_errors.ok(compound$1(children.value)) : children;
4452
4611
  }
4453
4612
  function isMatrix4x4(v) {
4454
4613
  return Array.isArray(v) && v.length === 4 && v.every((row) => Array.isArray(row) && row.length === 4 && row.every(isNumber$1));
4455
4614
  }
4456
- function readInstance(j) {
4457
- const source = readNode(j["source"]);
4615
+ function readInstance(j, defs) {
4616
+ const source = readNode(j["source"], defs);
4458
4617
  if (!source.ok) return source;
4459
4618
  const placements = j["placements"];
4460
4619
  if (!Array.isArray(placements) || !placements.every(isMatrix4x4)) return bad("Instance.placements must be an array of 4x4 number matrices");
@@ -4652,128 +4811,6 @@ function optimizeTranslate(target, vector) {
4652
4811
  return translate$1(ot, ov);
4653
4812
  }
4654
4813
  //#endregion
4655
- //#region src/csg/edit.ts
4656
- function replaceNode(root, pred, replacement) {
4657
- return walk(root, pred, replacement, /* @__PURE__ */ new Map());
4658
- }
4659
- function walk(node, pred, repl, memo) {
4660
- const cached = memo.get(node);
4661
- if (cached !== void 0) return cached;
4662
- const out = pred(node) ? repl : rebuildChildren(node, pred, repl, memo);
4663
- memo.set(node, out);
4664
- return out;
4665
- }
4666
- function rebuildChildren(n, pred, repl, memo) {
4667
- switch (n.kind) {
4668
- case "Box":
4669
- case "Sphere":
4670
- case "Cylinder":
4671
- case "Cone":
4672
- case "Torus":
4673
- case "Polygon":
4674
- case "Circle":
4675
- case "Line":
4676
- case "Vertex":
4677
- case "Empty":
4678
- case "Path":
4679
- case "Profile": return n;
4680
- case "Fuse": return fuse$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
4681
- case "Cut": return cut$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
4682
- case "Intersect": return intersect$1(walk(n.a, pred, repl, memo), walk(n.b, pred, repl, memo), n.tolerance);
4683
- case "FuseAll": return fuseAll$1(n.shapes.map((c) => walk(c, pred, repl, memo)), n.tolerance);
4684
- case "CutAll": return cutAll$1(walk(n.base, pred, repl, memo), n.tools.map((c) => walk(c, pred, repl, memo)), n.tolerance);
4685
- case "Translate": return translate$1(walk(n.target, pred, repl, memo), n.vector);
4686
- case "Rotate": return rotate$1(walk(n.target, pred, repl, memo), n.angle, {
4687
- axis: n.axis,
4688
- at: n.at
4689
- });
4690
- case "Scale": return scale$2(walk(n.target, pred, repl, memo), n.factor, { center: n.center });
4691
- case "Mirror": return mirror$1(walk(n.target, pred, repl, memo), {
4692
- normal: n.normal,
4693
- at: n.at
4694
- });
4695
- case "Compound": return compound$1(n.children.map((c) => walk(c, pred, repl, memo)));
4696
- case "Instance": return instance$1(walk(n.source, pred, repl, memo), n.placements, n.fuse);
4697
- case "Extrude":
4698
- case "Revolve":
4699
- case "Loft":
4700
- case "Sweep":
4701
- case "Color":
4702
- case "Fillet":
4703
- case "Chamfer":
4704
- case "Shell": return rebuildFeature(n, pred, repl, memo);
4705
- }
4706
- }
4707
- function rebuildFeature(n, pred, repl, memo) {
4708
- switch (n.kind) {
4709
- case "Extrude": return extrude$1(walk(n.profile, pred, repl, memo), n.vector);
4710
- case "Revolve": return revolve$1(walk(n.profile, pred, repl, memo), n.angle, {
4711
- axis: n.axis,
4712
- at: n.at
4713
- });
4714
- case "Loft": return loft$1(n.sections.map((s) => walk(s, pred, repl, memo)), { ruled: n.ruled });
4715
- case "Sweep": return sweep$2(walk(n.profile, pred, repl, memo), walk(n.spine, pred, repl, memo), { frenet: n.frenet });
4716
- case "Color": return color(walk(n.target, pred, repl, memo), [...n.color]);
4717
- case "Fillet": return fillet$1(walk(n.target, pred, repl, memo), n.ref, n.radius);
4718
- case "Chamfer": return chamfer$1(walk(n.target, pred, repl, memo), n.ref, n.distance);
4719
- case "Shell": return shell$1(walk(n.target, pred, repl, memo), n.refs, n.thickness);
4720
- }
4721
- }
4722
- /** Visits each DISTINCT node once (identity-deduplicated), so a shared
4723
- * subtree is reported once and a self-referencing DAG walks in linear time. */
4724
- function forEachNode(root, fn) {
4725
- const seen = /* @__PURE__ */ new Set();
4726
- const visit = (node) => {
4727
- if (seen.has(node)) return;
4728
- seen.add(node);
4729
- fn(node);
4730
- for (const child of childrenOf(node)) visit(child);
4731
- };
4732
- visit(root);
4733
- }
4734
- function childrenOf(n) {
4735
- switch (n.kind) {
4736
- case "Box":
4737
- case "Sphere":
4738
- case "Cylinder":
4739
- case "Cone":
4740
- case "Torus":
4741
- case "Polygon":
4742
- case "Circle":
4743
- case "Line":
4744
- case "Vertex":
4745
- case "Empty":
4746
- case "Path":
4747
- case "Profile": return [];
4748
- case "Fuse":
4749
- case "Cut":
4750
- case "Intersect": return [n.a, n.b];
4751
- case "FuseAll": return n.shapes;
4752
- case "CutAll": return [n.base, ...n.tools];
4753
- case "Translate":
4754
- case "Rotate":
4755
- case "Scale":
4756
- case "Mirror":
4757
- case "Color":
4758
- case "Fillet":
4759
- case "Chamfer":
4760
- case "Shell": return [n.target];
4761
- case "Compound": return n.children;
4762
- case "Instance": return [n.source];
4763
- case "Extrude":
4764
- case "Revolve": return [n.profile];
4765
- case "Loft": return n.sections;
4766
- case "Sweep": return [n.profile, n.spine];
4767
- }
4768
- }
4769
- function nodeCount(root) {
4770
- let n = 0;
4771
- forEachNode(root, () => {
4772
- n++;
4773
- });
4774
- return n;
4775
- }
4776
- //#endregion
4777
4814
  //#region src/sketching/blueprintContourFns.ts
4778
4815
  var EPS$1 = 1e-6;
4779
4816
  function p2(p) {
@@ -8732,7 +8769,7 @@ var patterns_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
8732
8769
  //#endregion
8733
8770
  //#region src/ns/csg.ts
8734
8771
  var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
8735
- CSG_VERSION: () => 7,
8772
+ CSG_VERSION: () => 8,
8736
8773
  Evaluator: () => Evaluator,
8737
8774
  add: () => add$1,
8738
8775
  arbitraryClosedProfile: () => arbitraryClosedProfile,