brepjs 18.67.0 → 18.68.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
@@ -182,7 +182,7 @@ function validateInputs(mesh, queries) {
182
182
  return null;
183
183
  }
184
184
  /** Resolve a registered voxel engine, mapping an unregistered id to an error. */
185
- function resolveEngine(id) {
185
+ function resolveEngine$1(id) {
186
186
  try {
187
187
  return require_errors.ok(getVoxel(id));
188
188
  } catch (cause) {
@@ -199,7 +199,7 @@ function resolveEngine(id) {
199
199
  function windingNumbers(mesh, queries, id) {
200
200
  const invalid = validateInputs(mesh, queries);
201
201
  if (invalid) return require_errors.err(invalid);
202
- const engine = resolveEngine(id);
202
+ const engine = resolveEngine$1(id);
203
203
  if (require_errors.isErr(engine)) return engine;
204
204
  return require_errors.ok(engine.value.winding_numbers(mesh.vertices, mesh.triangles, queries));
205
205
  }
@@ -211,15 +211,15 @@ function windingNumbers(mesh, queries, id) {
211
211
  function pointsInside(mesh, queries, id) {
212
212
  const invalid = validateInputs(mesh, queries);
213
213
  if (invalid) return require_errors.err(invalid);
214
- const engine = resolveEngine(id);
214
+ const engine = resolveEngine$1(id);
215
215
  if (require_errors.isErr(engine)) return engine;
216
216
  const flags = engine.value.points_inside(mesh.vertices, mesh.triangles, queries);
217
217
  return require_errors.ok(Array.from(flags, (flag) => flag === 1));
218
218
  }
219
219
  //#endregion
220
220
  //#region src/voxel/repairFns.ts
221
- var DEFAULT_RESOLUTION$3 = 48;
222
- var DEFAULT_PADDING$3 = 2;
221
+ var DEFAULT_RESOLUTION$4 = 48;
222
+ var DEFAULT_PADDING$4 = 2;
223
223
  /**
224
224
  * Repair a (possibly non-watertight) triangle-soup mesh into a closed surface:
225
225
  * voxelize an FWN-signed SDF, then Surface-Nets contour it back to triangles.
@@ -232,11 +232,11 @@ function repairMesh(mesh, opts, id) {
232
232
  const invalid = validateMesh(mesh);
233
233
  if (invalid) return require_errors.err(invalid);
234
234
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "repairMesh requires a non-empty triangle mesh."));
235
- const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$3;
236
- const padding = opts?.padding ?? DEFAULT_PADDING$3;
235
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$4;
236
+ const padding = opts?.padding ?? DEFAULT_PADDING$4;
237
237
  if (!Number.isInteger(resolution) || resolution < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
238
238
  if (!Number.isInteger(padding) || padding < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
239
- const engine = resolveEngine(id);
239
+ const engine = resolveEngine$1(id);
240
240
  if (require_errors.isErr(engine)) return engine;
241
241
  try {
242
242
  try {
@@ -286,16 +286,16 @@ function shapeToMeshInput(shape, deflection = DEFAULT_DEFLECTION) {
286
286
  }
287
287
  //#endregion
288
288
  //#region src/voxel/meshOpsFns.ts
289
- var DEFAULT_RESOLUTION$2 = 48;
290
- var DEFAULT_PADDING$2 = 2;
289
+ var DEFAULT_RESOLUTION$3 = 48;
290
+ var DEFAULT_PADDING$3 = 2;
291
291
  var BOOLEAN_OP_CODES$1 = {
292
292
  union: 0,
293
293
  intersection: 1,
294
294
  difference: 2
295
295
  };
296
- function resolveGridParams$1(opts) {
297
- const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$2;
298
- const padding = opts?.padding ?? DEFAULT_PADDING$2;
296
+ function resolveGridParams$2(opts) {
297
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$3;
298
+ const padding = opts?.padding ?? DEFAULT_PADDING$3;
299
299
  if (!Number.isInteger(resolution) || resolution < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
300
300
  if (!Number.isInteger(padding) || padding < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
301
301
  return require_errors.ok({
@@ -330,9 +330,9 @@ function offsetMesh(mesh, distance, opts, id) {
330
330
  if (invalid) return require_errors.err(invalid);
331
331
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "offsetMesh requires a non-empty triangle mesh."));
332
332
  if (!Number.isFinite(distance)) return require_errors.err(require_errors.validationError("VOXEL_INVALID_DISTANCE", "distance must be a finite number."));
333
- const params = resolveGridParams$1(opts);
333
+ const params = resolveGridParams$2(opts);
334
334
  if (require_errors.isErr(params)) return params;
335
- const engine = resolveEngine(id);
335
+ const engine = resolveEngine$1(id);
336
336
  if (require_errors.isErr(engine)) return engine;
337
337
  try {
338
338
  try {
@@ -359,9 +359,9 @@ function shellMesh(mesh, thickness, opts, id) {
359
359
  if (invalid) return require_errors.err(invalid);
360
360
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "shellMesh requires a non-empty triangle mesh."));
361
361
  if (!Number.isFinite(thickness) || thickness <= 0) return require_errors.err(require_errors.validationError("VOXEL_INVALID_THICKNESS", "thickness must be a finite number > 0."));
362
- const params = resolveGridParams$1(opts);
362
+ const params = resolveGridParams$2(opts);
363
363
  if (require_errors.isErr(params)) return params;
364
- const engine = resolveEngine(id);
364
+ const engine = resolveEngine$1(id);
365
365
  if (require_errors.isErr(engine)) return engine;
366
366
  try {
367
367
  try {
@@ -391,9 +391,9 @@ function voxelBoolean(a, b, op, opts, id) {
391
391
  if (a.vertices.length === 0 || a.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "voxelBoolean requires a non-empty mesh for operand A."));
392
392
  if (b.vertices.length === 0 || b.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "voxelBoolean requires a non-empty mesh for operand B."));
393
393
  const opCode = BOOLEAN_OP_CODES$1[op];
394
- const params = resolveGridParams$1(opts);
394
+ const params = resolveGridParams$2(opts);
395
395
  if (require_errors.isErr(params)) return params;
396
- const engine = resolveEngine(id);
396
+ const engine = resolveEngine$1(id);
397
397
  if (require_errors.isErr(engine)) return engine;
398
398
  try {
399
399
  try {
@@ -442,16 +442,16 @@ function voxelBooleanShapes(a, b, op, opts, id) {
442
442
  }
443
443
  //#endregion
444
444
  //#region src/voxel/fieldFns.ts
445
- var DEFAULT_RESOLUTION$1 = 48;
446
- var DEFAULT_PADDING$1 = 2;
445
+ var DEFAULT_RESOLUTION$2 = 48;
446
+ var DEFAULT_PADDING$2 = 2;
447
447
  var BOOLEAN_OP_CODES = {
448
448
  union: 0,
449
449
  intersection: 1,
450
450
  difference: 2
451
451
  };
452
- function resolveGridParams(opts) {
453
- const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
454
- const padding = opts?.padding ?? DEFAULT_PADDING$1;
452
+ function resolveGridParams$1(opts) {
453
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$2;
454
+ const padding = opts?.padding ?? DEFAULT_PADDING$2;
455
455
  if (!Number.isInteger(resolution) || resolution < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
456
456
  if (!Number.isInteger(padding) || padding < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
457
457
  return require_errors.ok({
@@ -501,6 +501,9 @@ function fieldDeletable(raw) {
501
501
  }
502
502
  };
503
503
  }
504
+ function makeFieldHandle(raw) {
505
+ return makeHandle(raw);
506
+ }
504
507
  function makeHandle(raw) {
505
508
  const inner = require_shapeTypes.createKernelHandle(fieldDeletable(raw));
506
509
  const handle = {
@@ -557,9 +560,9 @@ function voxelField(mesh, opts, id) {
557
560
  const invalid = validateMesh(mesh);
558
561
  if (invalid) return require_errors.err(invalid);
559
562
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "voxelField requires a non-empty triangle mesh."));
560
- const params = resolveGridParams(opts);
563
+ const params = resolveGridParams$1(opts);
561
564
  if (require_errors.isErr(params)) return params;
562
- const engine = resolveEngine(id);
565
+ const engine = resolveEngine$1(id);
563
566
  if (require_errors.isErr(engine)) return engine;
564
567
  try {
565
568
  return require_errors.ok(makeHandle(new engine.value.VoxelField(mesh.vertices, mesh.triangles, params.value.resolution, params.value.padding)));
@@ -586,9 +589,9 @@ function voxelBooleanField(a, b, op, opts, id) {
586
589
  if (invalidB) return require_errors.err(invalidB);
587
590
  if (a.vertices.length === 0 || a.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "voxelBooleanField requires a non-empty mesh for operand A."));
588
591
  if (b.vertices.length === 0 || b.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "voxelBooleanField requires a non-empty mesh for operand B."));
589
- const params = resolveGridParams(opts);
592
+ const params = resolveGridParams$1(opts);
590
593
  if (require_errors.isErr(params)) return params;
591
- const engine = resolveEngine(id);
594
+ const engine = resolveEngine$1(id);
592
595
  if (require_errors.isErr(engine)) return engine;
593
596
  try {
594
597
  return require_errors.ok(makeHandle(engine.value.VoxelField.boolean_of(a.vertices, a.triangles, b.vertices, b.triangles, BOOLEAN_OP_CODES[op], params.value.resolution, params.value.padding)));
@@ -698,6 +701,163 @@ function voxelBooleanFieldShapes(a, b, op, opts, id) {
698
701
  return voxelBooleanField(meshA.value, meshB.value, op, opts, id);
699
702
  }
700
703
  //#endregion
704
+ //#region src/implicit/sdfFns.ts
705
+ var DEFAULT_RESOLUTION$1 = 48;
706
+ var DEFAULT_PADDING$1 = 2;
707
+ function resolveEngine(id) {
708
+ try {
709
+ return require_errors.ok(getVoxel(id));
710
+ } catch (cause) {
711
+ return require_errors.err(require_errors.moduleInitError("VOXEL_NOT_INITIALIZED", cause instanceof Error ? cause.message : "voxel engine not initialized", cause));
712
+ }
713
+ }
714
+ function resolveGridParams(opts) {
715
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
716
+ const padding = opts?.padding ?? DEFAULT_PADDING$1;
717
+ if (!Number.isInteger(resolution) || resolution < 1) return require_errors.err(require_errors.validationError("SDF_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
718
+ if (!Number.isInteger(padding) || padding < 1) return require_errors.err(require_errors.validationError("SDF_INVALID_PADDING", "padding must be an integer >= 1."));
719
+ return require_errors.ok({
720
+ resolution,
721
+ padding
722
+ });
723
+ }
724
+ function sdfDeletable(raw) {
725
+ return {
726
+ raw,
727
+ delete() {
728
+ raw.free();
729
+ }
730
+ };
731
+ }
732
+ function makeSdfHandle(raw) {
733
+ const inner = require_shapeTypes.createKernelHandle(sdfDeletable(raw));
734
+ return {
735
+ get value() {
736
+ return inner.value.raw;
737
+ },
738
+ get disposed() {
739
+ return inner.disposed;
740
+ },
741
+ [Symbol.dispose]() {
742
+ inner[Symbol.dispose]();
743
+ },
744
+ union(other) {
745
+ return makeSdfHandle(this.value.union(other.value));
746
+ },
747
+ intersection(other) {
748
+ return makeSdfHandle(this.value.intersection(other.value));
749
+ },
750
+ difference(other) {
751
+ return makeSdfHandle(this.value.difference(other.value));
752
+ },
753
+ smoothUnion(other, k) {
754
+ return makeSdfHandle(this.value.smooth_union(other.value, k));
755
+ },
756
+ smoothIntersection(other, k) {
757
+ return makeSdfHandle(this.value.smooth_intersection(other.value, k));
758
+ },
759
+ smoothDifference(other, k) {
760
+ return makeSdfHandle(this.value.smooth_difference(other.value, k));
761
+ },
762
+ offset(distance) {
763
+ return makeSdfHandle(this.value.offset(distance));
764
+ },
765
+ round(radius) {
766
+ return makeSdfHandle(this.value.round(radius));
767
+ },
768
+ shell(thickness) {
769
+ return makeSdfHandle(this.value.shell(thickness));
770
+ },
771
+ onion(thickness) {
772
+ return makeSdfHandle(this.value.onion(thickness));
773
+ },
774
+ translate(x, y, z) {
775
+ return makeSdfHandle(this.value.translate(x, y, z));
776
+ },
777
+ rotate(ax, ay, az, angle) {
778
+ return makeSdfHandle(this.value.rotate(ax, ay, az, angle));
779
+ },
780
+ scale(s) {
781
+ return makeSdfHandle(this.value.scale(s));
782
+ },
783
+ rasterize(opts) {
784
+ return rasterizeField(this.value, opts);
785
+ },
786
+ rasterizeIn(bounds, opts) {
787
+ return rasterizeFieldIn(this.value, bounds, opts);
788
+ }
789
+ };
790
+ }
791
+ function rasterizeField(sdf, opts) {
792
+ const params = resolveGridParams(opts);
793
+ if (require_errors.isErr(params)) return params;
794
+ try {
795
+ return require_errors.ok(makeFieldHandle(sdf.rasterize(params.value.resolution, params.value.padding)));
796
+ } catch (cause) {
797
+ return rasterizeError(cause);
798
+ }
799
+ }
800
+ function rasterizeFieldIn(sdf, bounds, opts) {
801
+ const params = resolveGridParams(opts);
802
+ if (require_errors.isErr(params)) return params;
803
+ const invalid = validateBounds(bounds);
804
+ if (invalid) return require_errors.err(invalid);
805
+ try {
806
+ return require_errors.ok(makeFieldHandle(sdf.rasterize_in(bounds.min[0], bounds.min[1], bounds.min[2], bounds.max[0], bounds.max[1], bounds.max[2], params.value.resolution, params.value.padding)));
807
+ } catch (cause) {
808
+ return rasterizeError(cause);
809
+ }
810
+ }
811
+ function rasterizeError(cause) {
812
+ return require_errors.err(require_errors.computationError("SDF_RASTERIZE_FAILED", cause instanceof Error ? cause.message : "SDF rasterization failed (grid too large?).", cause));
813
+ }
814
+ function validateBounds(bounds) {
815
+ if (![...bounds.min, ...bounds.max].every((v) => Number.isFinite(v))) return require_errors.validationError("SDF_INVALID_BOUNDS", "bounds must be finite numbers.");
816
+ for (let axis = 0; axis < 3; axis++) if (bounds.max[axis] <= bounds.min[axis]) return require_errors.validationError("SDF_INVALID_BOUNDS", "bounds max must exceed min on every axis.");
817
+ return null;
818
+ }
819
+ function build(make, id) {
820
+ const engine = resolveEngine(id);
821
+ if (require_errors.isErr(engine)) return engine;
822
+ try {
823
+ return require_errors.ok(makeSdfHandle(make(engine.value)));
824
+ } catch (cause) {
825
+ return require_errors.err(require_errors.computationError("SDF_BUILD_FAILED", cause instanceof Error ? cause.message : "SDF primitive construction failed.", cause));
826
+ }
827
+ }
828
+ /** A sphere of radius `r`, centered at the origin. */
829
+ function sphere(r, id) {
830
+ return build((e) => e.Sdf.sphere(r), id);
831
+ }
832
+ /** An axis-aligned box of half-extents `(hx, hy, hz)`, centered at the origin. */
833
+ function box$1(hx, hy, hz, id) {
834
+ return build((e) => e.Sdf.box_(hx, hy, hz), id);
835
+ }
836
+ /** A box with rounded edges of radius `r`. */
837
+ function roundedBox(hx, hy, hz, r, id) {
838
+ return build((e) => e.Sdf.rounded_box(hx, hy, hz, r), id);
839
+ }
840
+ /** A capped cylinder, axis +Z, radius `r`, total height `h`, centered at origin. */
841
+ function cylinder$1(r, h, id) {
842
+ return build((e) => e.Sdf.cylinder(r, h), id);
843
+ }
844
+ /** A capped cone centered at the origin: base radius `r` at z = −h/2 tapering to an apex at z = +h/2. */
845
+ function cone$1(r, h, id) {
846
+ return build((e) => e.Sdf.cone(r, h), id);
847
+ }
848
+ /** A capsule: a line segment `a`→`b` of radius `r`. */
849
+ function capsule(a, b, r, id) {
850
+ return build((e) => e.Sdf.capsule(a[0], a[1], a[2], b[0], b[1], b[2], r), id);
851
+ }
852
+ /** A torus in the XY plane (axis +Z): a `minor`-radius tube on a `major` circle. */
853
+ function torus(major, minor, id) {
854
+ return build((e) => e.Sdf.torus(major, minor), id);
855
+ }
856
+ /** A half-space: the plane through `h·n` with outward normal `n` (normalized). */
857
+ function plane(n, h, id) {
858
+ return build((e) => e.Sdf.plane(n[0], n[1], n[2], h), id);
859
+ }
860
+ //#endregion
701
861
  //#region src/lattice/latticeFns.ts
702
862
  var LATTICE_TAGS = {
703
863
  gyroid: 0,
@@ -751,7 +911,7 @@ function latticeInfill(mesh, opts, id) {
751
911
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return require_errors.err(require_errors.validationError("LATTICE_EMPTY_MESH", "latticeInfill requires a non-empty triangle mesh."));
752
912
  const resolved = validateOptions(opts);
753
913
  if (require_errors.isErr(resolved)) return resolved;
754
- const engine = resolveEngine(id);
914
+ const engine = resolveEngine$1(id);
755
915
  if (require_errors.isErr(engine)) return engine;
756
916
  const { tag, period, thickness, resolution, padding } = resolved.value;
757
917
  try {
@@ -792,7 +952,7 @@ function tpmsLattice(bounds, opts, id) {
792
952
  }
793
953
  const resolved = validateOptions(opts);
794
954
  if (require_errors.isErr(resolved)) return resolved;
795
- const engine = resolveEngine(id);
955
+ const engine = resolveEngine$1(id);
796
956
  if (require_errors.isErr(engine)) return engine;
797
957
  const { tag, period, thickness, resolution, padding } = resolved.value;
798
958
  const [minX, minY, minZ] = bounds.min;
@@ -4461,7 +4621,7 @@ function depsOf(...sources) {
4461
4621
  }
4462
4622
  return acc.size === 0 ? EMPTY_DEPS : acc;
4463
4623
  }
4464
- function box$1(x, y, z) {
4624
+ function box$2(x, y, z) {
4465
4625
  const xe = asScalarExpr(x);
4466
4626
  const ye = asScalarExpr(y);
4467
4627
  const ze = asScalarExpr(z);
@@ -4476,7 +4636,7 @@ function box$1(x, y, z) {
4476
4636
  freeParams: depsOf(xe, ye, ze)
4477
4637
  };
4478
4638
  }
4479
- function sphere$1(radius) {
4639
+ function sphere$2(radius) {
4480
4640
  const re = asScalarExpr(radius);
4481
4641
  return {
4482
4642
  kind: "Sphere",
@@ -4485,7 +4645,7 @@ function sphere$1(radius) {
4485
4645
  freeParams: re.freeParams
4486
4646
  };
4487
4647
  }
4488
- function cylinder$1(radius, height) {
4648
+ function cylinder$2(radius, height) {
4489
4649
  const re = asScalarExpr(radius);
4490
4650
  const he = asScalarExpr(height);
4491
4651
  return {
@@ -4496,7 +4656,7 @@ function cylinder$1(radius, height) {
4496
4656
  freeParams: depsOf(re, he)
4497
4657
  };
4498
4658
  }
4499
- function cone$1(radius1, radius2, height) {
4659
+ function cone$2(radius1, radius2, height) {
4500
4660
  const r1 = asScalarExpr(radius1);
4501
4661
  const r2 = asScalarExpr(radius2);
4502
4662
  const he = asScalarExpr(height);
@@ -4511,7 +4671,7 @@ function cone$1(radius1, radius2, height) {
4511
4671
  freeParams: depsOf(r1, r2, he)
4512
4672
  };
4513
4673
  }
4514
- function torus$1(majorRadius, minorRadius) {
4674
+ function torus$2(majorRadius, minorRadius) {
4515
4675
  const ma = asScalarExpr(majorRadius);
4516
4676
  const mi = asScalarExpr(minorRadius);
4517
4677
  return {
@@ -5416,7 +5576,7 @@ function readSingleExpr(j, key, build) {
5416
5576
  function readPrimitive(kind, j) {
5417
5577
  switch (kind) {
5418
5578
  case "Box": return readBox(j);
5419
- case "Sphere": return readSingleExpr(j, "radius", sphere$1);
5579
+ case "Sphere": return readSingleExpr(j, "radius", sphere$2);
5420
5580
  case "Cylinder": return readCylinder(j);
5421
5581
  case "Cone": return readCone(j);
5422
5582
  case "Torus": return readTorus(j);
@@ -5435,14 +5595,14 @@ function readBox(j) {
5435
5595
  if (!y.ok) return y;
5436
5596
  const z = readExpr(j["z"]);
5437
5597
  if (!z.ok) return z;
5438
- return require_errors.ok(box$1(x.value, y.value, z.value));
5598
+ return require_errors.ok(box$2(x.value, y.value, z.value));
5439
5599
  }
5440
5600
  function readCylinder(j) {
5441
5601
  const r = readExpr(j["radius"]);
5442
5602
  if (!r.ok) return r;
5443
5603
  const h = readExpr(j["height"]);
5444
5604
  if (!h.ok) return h;
5445
- return require_errors.ok(cylinder$1(r.value, h.value));
5605
+ return require_errors.ok(cylinder$2(r.value, h.value));
5446
5606
  }
5447
5607
  function readCone(j) {
5448
5608
  const r1 = readExpr(j["radius1"]);
@@ -5451,14 +5611,14 @@ function readCone(j) {
5451
5611
  if (!r2.ok) return r2;
5452
5612
  const h = readExpr(j["height"]);
5453
5613
  if (!h.ok) return h;
5454
- return require_errors.ok(cone$1(r1.value, r2.value, h.value));
5614
+ return require_errors.ok(cone$2(r1.value, r2.value, h.value));
5455
5615
  }
5456
5616
  function readTorus(j) {
5457
5617
  const ma = readExpr(j["majorRadius"]);
5458
5618
  if (!ma.ok) return ma;
5459
5619
  const mi = readExpr(j["minorRadius"]);
5460
5620
  if (!mi.ok) return mi;
5461
- return require_errors.ok(torus$1(ma.value, mi.value));
5621
+ return require_errors.ok(torus$2(ma.value, mi.value));
5462
5622
  }
5463
5623
  function readPolygon(j) {
5464
5624
  const pts = j["points"];
@@ -5634,11 +5794,11 @@ function foldBuildVec(dim, comps) {
5634
5794
  }
5635
5795
  function optimizeNode(n) {
5636
5796
  switch (n.kind) {
5637
- case "Box": return box$1(foldExpr(n.x), foldExpr(n.y), foldExpr(n.z));
5638
- case "Sphere": return sphere$1(foldExpr(n.radius));
5639
- case "Cylinder": return cylinder$1(foldExpr(n.radius), foldExpr(n.height));
5640
- case "Cone": return cone$1(foldExpr(n.radius1), foldExpr(n.radius2), foldExpr(n.height));
5641
- case "Torus": return torus$1(foldExpr(n.majorRadius), foldExpr(n.minorRadius));
5797
+ case "Box": return box$2(foldExpr(n.x), foldExpr(n.y), foldExpr(n.z));
5798
+ case "Sphere": return sphere$2(foldExpr(n.radius));
5799
+ case "Cylinder": return cylinder$2(foldExpr(n.radius), foldExpr(n.height));
5800
+ case "Cone": return cone$2(foldExpr(n.radius1), foldExpr(n.radius2), foldExpr(n.height));
5801
+ case "Torus": return torus$2(foldExpr(n.majorRadius), foldExpr(n.minorRadius));
5642
5802
  case "Polygon": return polygon$1(n.points.map(foldExpr));
5643
5803
  case "Circle": return circle$1(foldExpr(n.radius));
5644
5804
  case "Line": return line$1(foldExpr(n.from), foldExpr(n.to));
@@ -5796,15 +5956,15 @@ var csg_exports = /* @__PURE__ */ require_textBlueprints.__exportAll({
5796
5956
  asVec2Expr: () => asVec2Expr,
5797
5957
  asVec3Expr: () => asVec3Expr,
5798
5958
  binOp: () => binOp,
5799
- box: () => box$1,
5959
+ box: () => box$2,
5800
5960
  buildVec: () => buildVec,
5801
5961
  circle: () => circle$1,
5802
5962
  component: () => component,
5803
5963
  compound: () => compound$1,
5804
- cone: () => cone$1,
5964
+ cone: () => cone$2,
5805
5965
  cut: () => cut$1,
5806
5966
  cutAll: () => cutAll$1,
5807
- cylinder: () => cylinder$1,
5967
+ cylinder: () => cylinder$2,
5808
5968
  emptyFace: () => emptyFace,
5809
5969
  emptySolid: () => emptySolid,
5810
5970
  emptyWire: () => emptyWire,
@@ -5826,9 +5986,9 @@ var csg_exports = /* @__PURE__ */ require_textBlueprints.__exportAll({
5826
5986
  replaceNode: () => replaceNode,
5827
5987
  rotate: () => rotate$1,
5828
5988
  scale: () => scale$1,
5829
- sphere: () => sphere$1,
5989
+ sphere: () => sphere$2,
5830
5990
  toJSON: () => toJSON,
5831
- torus: () => torus$1,
5991
+ torus: () => torus$2,
5832
5992
  translate: () => translate$1,
5833
5993
  unaryOp: () => unaryOp,
5834
5994
  vec2Lit: () => vec2Lit,
@@ -6308,6 +6468,14 @@ exports.roundedRectangleBlueprint = require_boolean2D.roundedRectangleBlueprint;
6308
6468
  exports.scale = scale;
6309
6469
  exports.scale2D = require_blueprintFns.scale2D;
6310
6470
  exports.scaleDrawing = require_drawFns.scaleDrawing;
6471
+ exports.sdfBox = box$1;
6472
+ exports.sdfCapsule = capsule;
6473
+ exports.sdfCone = cone$1;
6474
+ exports.sdfCylinder = cylinder$1;
6475
+ exports.sdfPlane = plane;
6476
+ exports.sdfRoundedBox = roundedBox;
6477
+ exports.sdfSphere = sphere;
6478
+ exports.sdfTorus = torus;
6311
6479
  exports.section = section;
6312
6480
  exports.sectionToFace = sectionToFace;
6313
6481
  exports.serializeHistory = require_historyFns.serializeHistory;
package/dist/brepjs.js CHANGED
@@ -14,7 +14,7 @@ import { a as meshEdges$1, c as createMeshCache, i as mesh$1, n as exportSTEP, o
14
14
  import { n as getAtOrThrow, r as lastOrThrow, t as firstOrThrow } from "./arrayAccess-DrUGPADn.js";
15
15
  import { _ as makeThreePointArc, d as makeCircle, h as makeLine, l as makeBSplineInterpolation, n as fill, r as makeFace, s as assembleWire } from "./surfaceBuilders-jx81G_YJ.js";
16
16
  import { a as fuseAll, c as sectionToFace$1, i as fuse$2, l as slice$1, n as cut$2, o as intersect$2, r as cutAll, s as section$1, t as booleanPipeline, u as split$1 } from "./booleanFns-CaKngiuu.js";
17
- import { $ as fuseAllBisect, A as fixShape, B as offset$1, C as threePointArc, D as wireLoop, E as wire, F as isValid$1, G as chamferWithEvolution, H as thicken$1, I as solidFromShell, J as fuseWithEvolution, K as cutWithEvolution, L as chamfer$1, M as healFace, N as healSolid, O as autoHeal, P as healWire, Q as cutAllBisect, R as draft$1, S as tangentArc, T as vertex, U as variableFillet, V as shell$1, W as positionOnCurve, X as shellWithEvolution, Y as intersectWithEvolution, Z as checkBoolean, _ as polygon, a as circle, at as sharedEdges, b as sphere, c as cylinder, ct as chamferDistAngle, d as ellipsoid, dt as toLODGeometryData, et as getNurbsCurveData, f as face, ft as toLineGeometryData, g as offsetFace, h as line, i as bsplineApprox, it as facesOfEdge, j as heal$1, k as fixSelfIntersection, l as ellipse, lt as toBufferGeometryData, m as helix, n as bezier, nt as adjacentFaces, o as compound, ot as verticesOfEdge, p as filledFace, q as filletWithEvolution, r as box, rt as edgesOfFace, s as cone, st as wiresOfFace, t as addHoles, tt as getNurbsSurfaceData, u as ellipseArc, ut as toGroupedBufferGeometryData, v as sewShells, w as torus, x as subFace, y as solid, z as fillet$1 } from "./primitiveFns-ecKWNC5k.js";
17
+ import { $ as fuseAllBisect, A as fixShape, B as offset$1, C as threePointArc, D as wireLoop, E as wire, F as isValid$1, G as chamferWithEvolution, H as thicken$1, I as solidFromShell, J as fuseWithEvolution, K as cutWithEvolution, L as chamfer$1, M as healFace, N as healSolid, O as autoHeal, P as healWire, Q as cutAllBisect, R as draft$1, S as tangentArc, T as vertex, U as variableFillet, V as shell$1, W as positionOnCurve, X as shellWithEvolution, Y as intersectWithEvolution, Z as checkBoolean, _ as polygon, a as circle, at as sharedEdges, b as sphere$1, c as cylinder, ct as chamferDistAngle, d as ellipsoid, dt as toLODGeometryData, et as getNurbsCurveData, f as face, ft as toLineGeometryData, g as offsetFace, h as line, i as bsplineApprox, it as facesOfEdge, j as heal$1, k as fixSelfIntersection, l as ellipse, lt as toBufferGeometryData, m as helix, n as bezier, nt as adjacentFaces, o as compound, ot as verticesOfEdge, p as filledFace, q as filletWithEvolution, r as box, rt as edgesOfFace, s as cone, st as wiresOfFace, t as addHoles, tt as getNurbsSurfaceData, u as ellipseArc, ut as toGroupedBufferGeometryData, v as sewShells, w as torus$1, x as subFace, y as solid, z as fillet$1 } from "./primitiveFns-ecKWNC5k.js";
18
18
  import { C as walkAssembly, D as exportAssemblySTEP, E as linearPattern, O as createAssembly, S as updateNode, _ as collectShapes, a as findStep, b as findNode, c as registerOperation, d as replayHistory, f as serializeHistory, g as addChild, h as undoLast, i as deserializeHistory, l as registerShape, m as stepsFrom, n as createHistory, o as getShape, p as stepCount, r as createRegistry, s as modifyStep, t as addStep, u as replayFrom, v as countNodes, w as circularPattern, x as removeChild, y as createAssemblyNode } from "./historyFns-CZ9oNL7j.js";
19
19
  import { n as BaseSketcher2d, r as organiseBlueprints, t as BlueprintSketcher } from "./blueprintSketcher-0DeTMXwj.js";
20
20
  import { a as createTypedFinder, i as wireFinder, n as edgeFinder, r as faceFinder, t as getSingleFace } from "./helpers-BX-0e71G.js";
@@ -193,7 +193,7 @@ function validateInputs(mesh, queries) {
193
193
  return null;
194
194
  }
195
195
  /** Resolve a registered voxel engine, mapping an unregistered id to an error. */
196
- function resolveEngine(id) {
196
+ function resolveEngine$1(id) {
197
197
  try {
198
198
  return ok(getVoxel(id));
199
199
  } catch (cause) {
@@ -210,7 +210,7 @@ function resolveEngine(id) {
210
210
  function windingNumbers(mesh, queries, id) {
211
211
  const invalid = validateInputs(mesh, queries);
212
212
  if (invalid) return err(invalid);
213
- const engine = resolveEngine(id);
213
+ const engine = resolveEngine$1(id);
214
214
  if (isErr(engine)) return engine;
215
215
  return ok(engine.value.winding_numbers(mesh.vertices, mesh.triangles, queries));
216
216
  }
@@ -222,15 +222,15 @@ function windingNumbers(mesh, queries, id) {
222
222
  function pointsInside(mesh, queries, id) {
223
223
  const invalid = validateInputs(mesh, queries);
224
224
  if (invalid) return err(invalid);
225
- const engine = resolveEngine(id);
225
+ const engine = resolveEngine$1(id);
226
226
  if (isErr(engine)) return engine;
227
227
  const flags = engine.value.points_inside(mesh.vertices, mesh.triangles, queries);
228
228
  return ok(Array.from(flags, (flag) => flag === 1));
229
229
  }
230
230
  //#endregion
231
231
  //#region src/voxel/repairFns.ts
232
- var DEFAULT_RESOLUTION$3 = 48;
233
- var DEFAULT_PADDING$3 = 2;
232
+ var DEFAULT_RESOLUTION$4 = 48;
233
+ var DEFAULT_PADDING$4 = 2;
234
234
  /**
235
235
  * Repair a (possibly non-watertight) triangle-soup mesh into a closed surface:
236
236
  * voxelize an FWN-signed SDF, then Surface-Nets contour it back to triangles.
@@ -243,11 +243,11 @@ function repairMesh(mesh, opts, id) {
243
243
  const invalid = validateMesh(mesh);
244
244
  if (invalid) return err(invalid);
245
245
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "repairMesh requires a non-empty triangle mesh."));
246
- const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$3;
247
- const padding = opts?.padding ?? DEFAULT_PADDING$3;
246
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$4;
247
+ const padding = opts?.padding ?? DEFAULT_PADDING$4;
248
248
  if (!Number.isInteger(resolution) || resolution < 1) return err(validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
249
249
  if (!Number.isInteger(padding) || padding < 1) return err(validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
250
- const engine = resolveEngine(id);
250
+ const engine = resolveEngine$1(id);
251
251
  if (isErr(engine)) return engine;
252
252
  try {
253
253
  try {
@@ -297,16 +297,16 @@ function shapeToMeshInput(shape, deflection = DEFAULT_DEFLECTION) {
297
297
  }
298
298
  //#endregion
299
299
  //#region src/voxel/meshOpsFns.ts
300
- var DEFAULT_RESOLUTION$2 = 48;
301
- var DEFAULT_PADDING$2 = 2;
300
+ var DEFAULT_RESOLUTION$3 = 48;
301
+ var DEFAULT_PADDING$3 = 2;
302
302
  var BOOLEAN_OP_CODES$1 = {
303
303
  union: 0,
304
304
  intersection: 1,
305
305
  difference: 2
306
306
  };
307
- function resolveGridParams$1(opts) {
308
- const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$2;
309
- const padding = opts?.padding ?? DEFAULT_PADDING$2;
307
+ function resolveGridParams$2(opts) {
308
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$3;
309
+ const padding = opts?.padding ?? DEFAULT_PADDING$3;
310
310
  if (!Number.isInteger(resolution) || resolution < 1) return err(validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
311
311
  if (!Number.isInteger(padding) || padding < 1) return err(validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
312
312
  return ok({
@@ -341,9 +341,9 @@ function offsetMesh(mesh, distance, opts, id) {
341
341
  if (invalid) return err(invalid);
342
342
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "offsetMesh requires a non-empty triangle mesh."));
343
343
  if (!Number.isFinite(distance)) return err(validationError("VOXEL_INVALID_DISTANCE", "distance must be a finite number."));
344
- const params = resolveGridParams$1(opts);
344
+ const params = resolveGridParams$2(opts);
345
345
  if (isErr(params)) return params;
346
- const engine = resolveEngine(id);
346
+ const engine = resolveEngine$1(id);
347
347
  if (isErr(engine)) return engine;
348
348
  try {
349
349
  try {
@@ -370,9 +370,9 @@ function shellMesh(mesh, thickness, opts, id) {
370
370
  if (invalid) return err(invalid);
371
371
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "shellMesh requires a non-empty triangle mesh."));
372
372
  if (!Number.isFinite(thickness) || thickness <= 0) return err(validationError("VOXEL_INVALID_THICKNESS", "thickness must be a finite number > 0."));
373
- const params = resolveGridParams$1(opts);
373
+ const params = resolveGridParams$2(opts);
374
374
  if (isErr(params)) return params;
375
- const engine = resolveEngine(id);
375
+ const engine = resolveEngine$1(id);
376
376
  if (isErr(engine)) return engine;
377
377
  try {
378
378
  try {
@@ -402,9 +402,9 @@ function voxelBoolean(a, b, op, opts, id) {
402
402
  if (a.vertices.length === 0 || a.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "voxelBoolean requires a non-empty mesh for operand A."));
403
403
  if (b.vertices.length === 0 || b.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "voxelBoolean requires a non-empty mesh for operand B."));
404
404
  const opCode = BOOLEAN_OP_CODES$1[op];
405
- const params = resolveGridParams$1(opts);
405
+ const params = resolveGridParams$2(opts);
406
406
  if (isErr(params)) return params;
407
- const engine = resolveEngine(id);
407
+ const engine = resolveEngine$1(id);
408
408
  if (isErr(engine)) return engine;
409
409
  try {
410
410
  try {
@@ -453,16 +453,16 @@ function voxelBooleanShapes(a, b, op, opts, id) {
453
453
  }
454
454
  //#endregion
455
455
  //#region src/voxel/fieldFns.ts
456
- var DEFAULT_RESOLUTION$1 = 48;
457
- var DEFAULT_PADDING$1 = 2;
456
+ var DEFAULT_RESOLUTION$2 = 48;
457
+ var DEFAULT_PADDING$2 = 2;
458
458
  var BOOLEAN_OP_CODES = {
459
459
  union: 0,
460
460
  intersection: 1,
461
461
  difference: 2
462
462
  };
463
- function resolveGridParams(opts) {
464
- const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
465
- const padding = opts?.padding ?? DEFAULT_PADDING$1;
463
+ function resolveGridParams$1(opts) {
464
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$2;
465
+ const padding = opts?.padding ?? DEFAULT_PADDING$2;
466
466
  if (!Number.isInteger(resolution) || resolution < 1) return err(validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
467
467
  if (!Number.isInteger(padding) || padding < 1) return err(validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
468
468
  return ok({
@@ -512,6 +512,9 @@ function fieldDeletable(raw) {
512
512
  }
513
513
  };
514
514
  }
515
+ function makeFieldHandle(raw) {
516
+ return makeHandle(raw);
517
+ }
515
518
  function makeHandle(raw) {
516
519
  const inner = createKernelHandle(fieldDeletable(raw));
517
520
  const handle = {
@@ -568,9 +571,9 @@ function voxelField(mesh, opts, id) {
568
571
  const invalid = validateMesh(mesh);
569
572
  if (invalid) return err(invalid);
570
573
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "voxelField requires a non-empty triangle mesh."));
571
- const params = resolveGridParams(opts);
574
+ const params = resolveGridParams$1(opts);
572
575
  if (isErr(params)) return params;
573
- const engine = resolveEngine(id);
576
+ const engine = resolveEngine$1(id);
574
577
  if (isErr(engine)) return engine;
575
578
  try {
576
579
  return ok(makeHandle(new engine.value.VoxelField(mesh.vertices, mesh.triangles, params.value.resolution, params.value.padding)));
@@ -597,9 +600,9 @@ function voxelBooleanField(a, b, op, opts, id) {
597
600
  if (invalidB) return err(invalidB);
598
601
  if (a.vertices.length === 0 || a.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "voxelBooleanField requires a non-empty mesh for operand A."));
599
602
  if (b.vertices.length === 0 || b.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "voxelBooleanField requires a non-empty mesh for operand B."));
600
- const params = resolveGridParams(opts);
603
+ const params = resolveGridParams$1(opts);
601
604
  if (isErr(params)) return params;
602
- const engine = resolveEngine(id);
605
+ const engine = resolveEngine$1(id);
603
606
  if (isErr(engine)) return engine;
604
607
  try {
605
608
  return ok(makeHandle(engine.value.VoxelField.boolean_of(a.vertices, a.triangles, b.vertices, b.triangles, BOOLEAN_OP_CODES[op], params.value.resolution, params.value.padding)));
@@ -709,6 +712,163 @@ function voxelBooleanFieldShapes(a, b, op, opts, id) {
709
712
  return voxelBooleanField(meshA.value, meshB.value, op, opts, id);
710
713
  }
711
714
  //#endregion
715
+ //#region src/implicit/sdfFns.ts
716
+ var DEFAULT_RESOLUTION$1 = 48;
717
+ var DEFAULT_PADDING$1 = 2;
718
+ function resolveEngine(id) {
719
+ try {
720
+ return ok(getVoxel(id));
721
+ } catch (cause) {
722
+ return err(moduleInitError("VOXEL_NOT_INITIALIZED", cause instanceof Error ? cause.message : "voxel engine not initialized", cause));
723
+ }
724
+ }
725
+ function resolveGridParams(opts) {
726
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
727
+ const padding = opts?.padding ?? DEFAULT_PADDING$1;
728
+ if (!Number.isInteger(resolution) || resolution < 1) return err(validationError("SDF_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
729
+ if (!Number.isInteger(padding) || padding < 1) return err(validationError("SDF_INVALID_PADDING", "padding must be an integer >= 1."));
730
+ return ok({
731
+ resolution,
732
+ padding
733
+ });
734
+ }
735
+ function sdfDeletable(raw) {
736
+ return {
737
+ raw,
738
+ delete() {
739
+ raw.free();
740
+ }
741
+ };
742
+ }
743
+ function makeSdfHandle(raw) {
744
+ const inner = createKernelHandle(sdfDeletable(raw));
745
+ return {
746
+ get value() {
747
+ return inner.value.raw;
748
+ },
749
+ get disposed() {
750
+ return inner.disposed;
751
+ },
752
+ [Symbol.dispose]() {
753
+ inner[Symbol.dispose]();
754
+ },
755
+ union(other) {
756
+ return makeSdfHandle(this.value.union(other.value));
757
+ },
758
+ intersection(other) {
759
+ return makeSdfHandle(this.value.intersection(other.value));
760
+ },
761
+ difference(other) {
762
+ return makeSdfHandle(this.value.difference(other.value));
763
+ },
764
+ smoothUnion(other, k) {
765
+ return makeSdfHandle(this.value.smooth_union(other.value, k));
766
+ },
767
+ smoothIntersection(other, k) {
768
+ return makeSdfHandle(this.value.smooth_intersection(other.value, k));
769
+ },
770
+ smoothDifference(other, k) {
771
+ return makeSdfHandle(this.value.smooth_difference(other.value, k));
772
+ },
773
+ offset(distance) {
774
+ return makeSdfHandle(this.value.offset(distance));
775
+ },
776
+ round(radius) {
777
+ return makeSdfHandle(this.value.round(radius));
778
+ },
779
+ shell(thickness) {
780
+ return makeSdfHandle(this.value.shell(thickness));
781
+ },
782
+ onion(thickness) {
783
+ return makeSdfHandle(this.value.onion(thickness));
784
+ },
785
+ translate(x, y, z) {
786
+ return makeSdfHandle(this.value.translate(x, y, z));
787
+ },
788
+ rotate(ax, ay, az, angle) {
789
+ return makeSdfHandle(this.value.rotate(ax, ay, az, angle));
790
+ },
791
+ scale(s) {
792
+ return makeSdfHandle(this.value.scale(s));
793
+ },
794
+ rasterize(opts) {
795
+ return rasterizeField(this.value, opts);
796
+ },
797
+ rasterizeIn(bounds, opts) {
798
+ return rasterizeFieldIn(this.value, bounds, opts);
799
+ }
800
+ };
801
+ }
802
+ function rasterizeField(sdf, opts) {
803
+ const params = resolveGridParams(opts);
804
+ if (isErr(params)) return params;
805
+ try {
806
+ return ok(makeFieldHandle(sdf.rasterize(params.value.resolution, params.value.padding)));
807
+ } catch (cause) {
808
+ return rasterizeError(cause);
809
+ }
810
+ }
811
+ function rasterizeFieldIn(sdf, bounds, opts) {
812
+ const params = resolveGridParams(opts);
813
+ if (isErr(params)) return params;
814
+ const invalid = validateBounds(bounds);
815
+ if (invalid) return err(invalid);
816
+ try {
817
+ return ok(makeFieldHandle(sdf.rasterize_in(bounds.min[0], bounds.min[1], bounds.min[2], bounds.max[0], bounds.max[1], bounds.max[2], params.value.resolution, params.value.padding)));
818
+ } catch (cause) {
819
+ return rasterizeError(cause);
820
+ }
821
+ }
822
+ function rasterizeError(cause) {
823
+ return err(computationError("SDF_RASTERIZE_FAILED", cause instanceof Error ? cause.message : "SDF rasterization failed (grid too large?).", cause));
824
+ }
825
+ function validateBounds(bounds) {
826
+ if (![...bounds.min, ...bounds.max].every((v) => Number.isFinite(v))) return validationError("SDF_INVALID_BOUNDS", "bounds must be finite numbers.");
827
+ for (let axis = 0; axis < 3; axis++) if (bounds.max[axis] <= bounds.min[axis]) return validationError("SDF_INVALID_BOUNDS", "bounds max must exceed min on every axis.");
828
+ return null;
829
+ }
830
+ function build(make, id) {
831
+ const engine = resolveEngine(id);
832
+ if (isErr(engine)) return engine;
833
+ try {
834
+ return ok(makeSdfHandle(make(engine.value)));
835
+ } catch (cause) {
836
+ return err(computationError("SDF_BUILD_FAILED", cause instanceof Error ? cause.message : "SDF primitive construction failed.", cause));
837
+ }
838
+ }
839
+ /** A sphere of radius `r`, centered at the origin. */
840
+ function sphere(r, id) {
841
+ return build((e) => e.Sdf.sphere(r), id);
842
+ }
843
+ /** An axis-aligned box of half-extents `(hx, hy, hz)`, centered at the origin. */
844
+ function box$1(hx, hy, hz, id) {
845
+ return build((e) => e.Sdf.box_(hx, hy, hz), id);
846
+ }
847
+ /** A box with rounded edges of radius `r`. */
848
+ function roundedBox(hx, hy, hz, r, id) {
849
+ return build((e) => e.Sdf.rounded_box(hx, hy, hz, r), id);
850
+ }
851
+ /** A capped cylinder, axis +Z, radius `r`, total height `h`, centered at origin. */
852
+ function cylinder$1(r, h, id) {
853
+ return build((e) => e.Sdf.cylinder(r, h), id);
854
+ }
855
+ /** A capped cone centered at the origin: base radius `r` at z = −h/2 tapering to an apex at z = +h/2. */
856
+ function cone$1(r, h, id) {
857
+ return build((e) => e.Sdf.cone(r, h), id);
858
+ }
859
+ /** A capsule: a line segment `a`→`b` of radius `r`. */
860
+ function capsule(a, b, r, id) {
861
+ return build((e) => e.Sdf.capsule(a[0], a[1], a[2], b[0], b[1], b[2], r), id);
862
+ }
863
+ /** A torus in the XY plane (axis +Z): a `minor`-radius tube on a `major` circle. */
864
+ function torus(major, minor, id) {
865
+ return build((e) => e.Sdf.torus(major, minor), id);
866
+ }
867
+ /** A half-space: the plane through `h·n` with outward normal `n` (normalized). */
868
+ function plane(n, h, id) {
869
+ return build((e) => e.Sdf.plane(n[0], n[1], n[2], h), id);
870
+ }
871
+ //#endregion
712
872
  //#region src/lattice/latticeFns.ts
713
873
  var LATTICE_TAGS = {
714
874
  gyroid: 0,
@@ -762,7 +922,7 @@ function latticeInfill(mesh, opts, id) {
762
922
  if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return err(validationError("LATTICE_EMPTY_MESH", "latticeInfill requires a non-empty triangle mesh."));
763
923
  const resolved = validateOptions(opts);
764
924
  if (isErr(resolved)) return resolved;
765
- const engine = resolveEngine(id);
925
+ const engine = resolveEngine$1(id);
766
926
  if (isErr(engine)) return engine;
767
927
  const { tag, period, thickness, resolution, padding } = resolved.value;
768
928
  try {
@@ -803,7 +963,7 @@ function tpmsLattice(bounds, opts, id) {
803
963
  }
804
964
  const resolved = validateOptions(opts);
805
965
  if (isErr(resolved)) return resolved;
806
- const engine = resolveEngine(id);
966
+ const engine = resolveEngine$1(id);
807
967
  if (isErr(engine)) return engine;
808
968
  const { tag, period, thickness, resolution, padding } = resolved.value;
809
969
  const [minX, minY, minZ] = bounds.min;
@@ -4032,11 +4192,11 @@ var primitives_exports = /* @__PURE__ */ __exportAll({
4032
4192
  polyhedron: () => polyhedron,
4033
4193
  sewShells: () => sewShells,
4034
4194
  solid: () => solid,
4035
- sphere: () => sphere,
4195
+ sphere: () => sphere$1,
4036
4196
  subFace: () => subFace,
4037
4197
  tangentArc: () => tangentArc,
4038
4198
  threePointArc: () => threePointArc,
4039
- torus: () => torus,
4199
+ torus: () => torus$1,
4040
4200
  vertex: () => vertex,
4041
4201
  wire: () => wire,
4042
4202
  wireLoop: () => wireLoop
@@ -4464,7 +4624,7 @@ function depsOf(...sources) {
4464
4624
  }
4465
4625
  return acc.size === 0 ? EMPTY_DEPS : acc;
4466
4626
  }
4467
- function box$1(x, y, z) {
4627
+ function box$2(x, y, z) {
4468
4628
  const xe = asScalarExpr(x);
4469
4629
  const ye = asScalarExpr(y);
4470
4630
  const ze = asScalarExpr(z);
@@ -4479,7 +4639,7 @@ function box$1(x, y, z) {
4479
4639
  freeParams: depsOf(xe, ye, ze)
4480
4640
  };
4481
4641
  }
4482
- function sphere$1(radius) {
4642
+ function sphere$2(radius) {
4483
4643
  const re = asScalarExpr(radius);
4484
4644
  return {
4485
4645
  kind: "Sphere",
@@ -4488,7 +4648,7 @@ function sphere$1(radius) {
4488
4648
  freeParams: re.freeParams
4489
4649
  };
4490
4650
  }
4491
- function cylinder$1(radius, height) {
4651
+ function cylinder$2(radius, height) {
4492
4652
  const re = asScalarExpr(radius);
4493
4653
  const he = asScalarExpr(height);
4494
4654
  return {
@@ -4499,7 +4659,7 @@ function cylinder$1(radius, height) {
4499
4659
  freeParams: depsOf(re, he)
4500
4660
  };
4501
4661
  }
4502
- function cone$1(radius1, radius2, height) {
4662
+ function cone$2(radius1, radius2, height) {
4503
4663
  const r1 = asScalarExpr(radius1);
4504
4664
  const r2 = asScalarExpr(radius2);
4505
4665
  const he = asScalarExpr(height);
@@ -4514,7 +4674,7 @@ function cone$1(radius1, radius2, height) {
4514
4674
  freeParams: depsOf(r1, r2, he)
4515
4675
  };
4516
4676
  }
4517
- function torus$1(majorRadius, minorRadius) {
4677
+ function torus$2(majorRadius, minorRadius) {
4518
4678
  const ma = asScalarExpr(majorRadius);
4519
4679
  const mi = asScalarExpr(minorRadius);
4520
4680
  return {
@@ -4750,7 +4910,7 @@ function evalBox(node, ctx) {
4750
4910
  function evalSphere(node, ctx) {
4751
4911
  const r = evalScalar(node.radius, ctx.env, "Sphere.radius");
4752
4912
  if (!r.ok) return r;
4753
- return ok(sphere(r.value));
4913
+ return ok(sphere$1(r.value));
4754
4914
  }
4755
4915
  function evalCylinder(node, ctx) {
4756
4916
  const r = evalScalar(node.radius, ctx.env, "Cylinder.radius");
@@ -4773,7 +4933,7 @@ function evalTorus(node, ctx) {
4773
4933
  if (!ma.ok) return ma;
4774
4934
  const mi = evalScalar(node.minorRadius, ctx.env, "Torus.minorRadius");
4775
4935
  if (!mi.ok) return mi;
4776
- return ok(torus(ma.value, mi.value));
4936
+ return ok(torus$1(ma.value, mi.value));
4777
4937
  }
4778
4938
  function evalPolygon(node, ctx) {
4779
4939
  const pts = [];
@@ -5419,7 +5579,7 @@ function readSingleExpr(j, key, build) {
5419
5579
  function readPrimitive(kind, j) {
5420
5580
  switch (kind) {
5421
5581
  case "Box": return readBox(j);
5422
- case "Sphere": return readSingleExpr(j, "radius", sphere$1);
5582
+ case "Sphere": return readSingleExpr(j, "radius", sphere$2);
5423
5583
  case "Cylinder": return readCylinder(j);
5424
5584
  case "Cone": return readCone(j);
5425
5585
  case "Torus": return readTorus(j);
@@ -5438,14 +5598,14 @@ function readBox(j) {
5438
5598
  if (!y.ok) return y;
5439
5599
  const z = readExpr(j["z"]);
5440
5600
  if (!z.ok) return z;
5441
- return ok(box$1(x.value, y.value, z.value));
5601
+ return ok(box$2(x.value, y.value, z.value));
5442
5602
  }
5443
5603
  function readCylinder(j) {
5444
5604
  const r = readExpr(j["radius"]);
5445
5605
  if (!r.ok) return r;
5446
5606
  const h = readExpr(j["height"]);
5447
5607
  if (!h.ok) return h;
5448
- return ok(cylinder$1(r.value, h.value));
5608
+ return ok(cylinder$2(r.value, h.value));
5449
5609
  }
5450
5610
  function readCone(j) {
5451
5611
  const r1 = readExpr(j["radius1"]);
@@ -5454,14 +5614,14 @@ function readCone(j) {
5454
5614
  if (!r2.ok) return r2;
5455
5615
  const h = readExpr(j["height"]);
5456
5616
  if (!h.ok) return h;
5457
- return ok(cone$1(r1.value, r2.value, h.value));
5617
+ return ok(cone$2(r1.value, r2.value, h.value));
5458
5618
  }
5459
5619
  function readTorus(j) {
5460
5620
  const ma = readExpr(j["majorRadius"]);
5461
5621
  if (!ma.ok) return ma;
5462
5622
  const mi = readExpr(j["minorRadius"]);
5463
5623
  if (!mi.ok) return mi;
5464
- return ok(torus$1(ma.value, mi.value));
5624
+ return ok(torus$2(ma.value, mi.value));
5465
5625
  }
5466
5626
  function readPolygon(j) {
5467
5627
  const pts = j["points"];
@@ -5637,11 +5797,11 @@ function foldBuildVec(dim, comps) {
5637
5797
  }
5638
5798
  function optimizeNode(n) {
5639
5799
  switch (n.kind) {
5640
- case "Box": return box$1(foldExpr(n.x), foldExpr(n.y), foldExpr(n.z));
5641
- case "Sphere": return sphere$1(foldExpr(n.radius));
5642
- case "Cylinder": return cylinder$1(foldExpr(n.radius), foldExpr(n.height));
5643
- case "Cone": return cone$1(foldExpr(n.radius1), foldExpr(n.radius2), foldExpr(n.height));
5644
- case "Torus": return torus$1(foldExpr(n.majorRadius), foldExpr(n.minorRadius));
5800
+ case "Box": return box$2(foldExpr(n.x), foldExpr(n.y), foldExpr(n.z));
5801
+ case "Sphere": return sphere$2(foldExpr(n.radius));
5802
+ case "Cylinder": return cylinder$2(foldExpr(n.radius), foldExpr(n.height));
5803
+ case "Cone": return cone$2(foldExpr(n.radius1), foldExpr(n.radius2), foldExpr(n.height));
5804
+ case "Torus": return torus$2(foldExpr(n.majorRadius), foldExpr(n.minorRadius));
5645
5805
  case "Polygon": return polygon$1(n.points.map(foldExpr));
5646
5806
  case "Circle": return circle$1(foldExpr(n.radius));
5647
5807
  case "Line": return line$1(foldExpr(n.from), foldExpr(n.to));
@@ -5799,15 +5959,15 @@ var csg_exports = /* @__PURE__ */ __exportAll({
5799
5959
  asVec2Expr: () => asVec2Expr,
5800
5960
  asVec3Expr: () => asVec3Expr,
5801
5961
  binOp: () => binOp,
5802
- box: () => box$1,
5962
+ box: () => box$2,
5803
5963
  buildVec: () => buildVec,
5804
5964
  circle: () => circle$1,
5805
5965
  component: () => component,
5806
5966
  compound: () => compound$1,
5807
- cone: () => cone$1,
5967
+ cone: () => cone$2,
5808
5968
  cut: () => cut$1,
5809
5969
  cutAll: () => cutAll$1,
5810
- cylinder: () => cylinder$1,
5970
+ cylinder: () => cylinder$2,
5811
5971
  emptyFace: () => emptyFace,
5812
5972
  emptySolid: () => emptySolid,
5813
5973
  emptyWire: () => emptyWire,
@@ -5829,9 +5989,9 @@ var csg_exports = /* @__PURE__ */ __exportAll({
5829
5989
  replaceNode: () => replaceNode,
5830
5990
  rotate: () => rotate$1,
5831
5991
  scale: () => scale$1,
5832
- sphere: () => sphere$1,
5992
+ sphere: () => sphere$2,
5833
5993
  toJSON: () => toJSON,
5834
- torus: () => torus$1,
5994
+ torus: () => torus$2,
5835
5995
  translate: () => translate$1,
5836
5996
  unaryOp: () => unaryOp,
5837
5997
  vec2Lit: () => vec2Lit,
@@ -5840,4 +6000,4 @@ var csg_exports = /* @__PURE__ */ __exportAll({
5840
6000
  withEvaluator: () => withEvaluator
5841
6001
  });
5842
6002
  //#endregion
5843
- export { BaseSketcher2d, BlueprintSketcher, BrepBugError, BrepErrorCode, BrepWrapperError, BrepkitAdapter, CompoundSketch, DEFAULT_CAPABILITIES, DEG2RAD, DisposalScope, EXACT_BREP_CAPABILITIES, FaceSketcher, HASH_CODE_MAX, OK, OcctWasmAdapter, RAD2DEG, Sketch, Sketcher, Sketches, addChild, addHoles, addMate, addStep, adjacentFaces, all, andThen, applyGlue, applyMatrix, approximateCurve, as2D, as3D, asTopo, assignRoles, autoHeal, bezier, blueprintToDXF, booleanPipeline, booleans_exports as booleans, boss, box, bsplineApprox, bug, cameraFromPlane, cameraLookAt, captureHint, cast, castShape, castShape3D, chamfer, chamferDistAngle as chamferDistAngleShape, chamferWithEvolution, checkAllInterferences, checkBoolean, checkInterference, circle, circularPattern, classifyPointOnFace, clearMeshCache, clone, closedWire, collect, collectShapes, colorFaces, colorShape, complexExtrude, composeTransforms, compound, compoundSketchExtrude, compoundSketchFace, compoundSketchLoft, compoundSketchRevolve, computationError, computeStraightSkeleton, cone, construction_exports as construction, convexHull, cornerFinder, countNodes, createAssembly, createAssemblyNode, createBlueprint, createCamera, createCompound, createCompoundBlueprint, createDistanceQuery, createEdge, createFace, createHandle, createHistory, createKernelHandle, createMeshCache, createNamedPlane, createOperationRegistry, createPlane, createRef, createRegistry, createShell, createSolid, createTaskQueue, createVertex, createWire, createWorkerClient, createWorkerHandler, csg_exports as csg, currentQuality, curve2dBoundingBox, curve2dDistanceFrom, curve2dFirstPoint, curve2dIsOnCurve, curve2dLastPoint, curve2dParameter, curve2dSplitAt, curve2dTangentAt, curveEndPoint, curveIsClosed, curveIsPeriodic, curveLength, curvePeriod, curvePointAt, curveStartPoint, curveTangentAt, cut, cut2D, cutAll, cutAllBisect, cutBlueprints, cutWithEvolution, cylinder, defaultScorer, dequeueTask, describe, deserializeDrawing, deserializeHistory, fromBREP as deserializeShape, downcast, draft, draw, drawCircle, drawEllipse, drawFaceOutline, drawParametricFunction, drawPointsInterpolation, drawPolysides, drawProjection, drawRectangle, drawRoundedRectangle, drawSingleCircle, drawSingleEllipse, drawText, drawingChamfer, drawingCut, drawingFillet, drawingFuse, drawingIntersect, drawingToSketchOnPlane, drill, edgeFinder, edgesOfFace, ellipse, ellipseArc, ellipsoid, enqueueTask, err, exportAssemblySTEP, exportDXF, exportGlb, exportGltf, exportIGES, exportOBJ, exportSTEP, exportSTEPConfigured, exportSTL, exportThreeMF, extrude, extrudeAll, face, faceCenter, faceFinder, faceGeomType, faceOrientation, facesOfEdge, fieldBoolean, fieldContour, fieldOffset, fieldReinit, fieldShell, fill, filledFace, fillet, filletWithEvolution, findFacesByTag, findNode, findStep, fixSelfIntersection, fixShape, flatMap, flatten, flipFaceOrientation, flipOrientation, fontMetrics, fromBREP$1 as fromBREP, fromKernelDir, fromKernelPnt, fromKernelVec, fromNullable, fuse, fuse2D, fuseAll, fuseAllBisect, fuseBlueprints, fuseWithEvolution, gearGeometry, getActiveVoxelId, getBounds, getBounds2D, getCompSolids, getCurveType, getDisposalStats, getEdges, getFaceColor, getFaceOrigins, getFaceTags, getFaces, getFont, getHashCode, getShape as getHistoryShape, getKernel, getKernelCapabilities, getKernelTier, getNurbsCurveData, getNurbsSurfaceData, getOrientation, getOrientation2D, getPerformanceStats, getShapeColor, getShapeKind, getShells, getSingleFace, getSolids, getSurfaceType, getTagMetadata, getVertices, getVoxel, getWires, guidedSweep, heal, healFace, healSolid, healWire, helix, hull, importDXF, importGLB, importIGES, importOBJ, importSTEP, importSTL, importSVG, importSVGPathD, importThreeMF, init, initFromManifold, initFromOC, initVoxel, innerWires, interpolateCurve, intersect, intersect2D, intersectBlueprints, intersectWithEvolution, invalidateShapeCache, ioNs_exports as io, ioError, is2D, is3D, isChamferRadius, isClosedWire, isCompSolid, isCompound, isDisposeRequest, isEdge, isEmpty, isEqualShape, isErr, isErrorResponse, isFace, isFilletRadius, isInitRequest, isInside2D, isLive, isManifoldShell, isNumber, isOk, isOperationRequest, isOrientedFace, isPlanarFace, isPlanarWire, isProjectionPlane, isEmpty$1 as isQueueEmpty, isSameShape, isShape1D, isShape3D, isShell, isSolid, isSuccessResponse, isValid, isValidSolid, isVertex, isWire, iterCompSolids, iterEdges, iterFaces, iterShells, iterSolids, iterTopo, iterVertices, iterWires, kernelCall, kernelCallRaw, kernelCallScoped, kernelError, latticeInfill, latticeInfillShape, line, linearPattern, loadFont, loft, loftAll, makeBaseBox, makeExternalGear, makeInternalGear, makePlane, makePlanetaryGear, makeProjectedEdges, manifoldShell, map, mapBoth, mapErr, match, measureArea, measureCurvatureAt, measureCurvatureAtMid, measureDistance, measureDistanceProps, measureLength, measureLinearProps, measureSurfaceProps, measureVolume, measureVolumeProps, measurement_exports as measurement, mesh, meshEdges, meshMultiLOD, minkowski, mirror, mirror2D, mirrorDrawing, mirrorJoin, modifiers_exports as modifiers, modifyStep, moduleInitError, multiSectionSweep, normalAt, offset, offsetFace, offsetMesh, offsetShape, offsetWire2D, ok, or, orElse, organiseBlueprints, orientedFace, outerWire, patterns_exports as patterns, pendingCount, pipeline, pivotPlane, planarFace, planarWire, planetPlacements, pocket, pointOnSurface, pointsInside, polygon, polyhedron, polysideInnerRadius, polysidesBlueprint, positionOnCurve, prewarm, primitives_exports as primitives, projectEdges, projectPointOnFace, query_exports as query, queryError, rectangularPattern, registerHandler, registerKernel, registerKernelTier, registerOperation, registerShape, registerVoxel, rejectAll, removeChild, removeHolesFromFace, repairMesh, replayFrom, replayHistory, resetDisposalStats, resetPerformanceStats, resize, resolve, resolve3D, resolveDirection, resolvePlane, resolveRef, reverseCurve, revolve, roof, rotate, rotate2D, rotateDrawing, roundedRectangleBlueprint, scale, scale2D, scaleDrawing, section, sectionToFace, serializeHistory, setShapeOrigin, setTagMetadata, sewShells, shape, shapeToMeshInput, shapeType, sharedEdges, shell, shellMesh, shellShape, shellWithEvolution, simplify, sketchCircle, sketchEllipse, sketchExtrude, sketchFace, sketchFaceOffset, sketchHelix, sketchLoft, sketchOnFace2D, sketchOnPlane2D, sketchParametricFunction, sketchPolysides, sketchRectangle, sketchRevolve, sketchRoundedRectangle, sketchSweep, sketchText, sketchWires, sketcherStateError, slice, solid, solidFromShell, solveAssembly, sphere, split, stepCount, stepsFrom, stretch2D, subFace, supportExtrude, supportsConstraintSketch, supportsProjection, surfaceFromGrid, surfaceFromImage, sweep, tagFaces, tangentArc, tap, tapErr, textBlueprints, textMetrics, thicken, threePointArc, toBREP, toBufferGeometryData, toGroupedBufferGeometryData, toKernelVec, toLODGeometryData, toLineGeometryData, toSVGPathD, toVec2, toVec3, torus, tpmsLattice, transformCopy, transforms_exports as transforms, translate, translate2D, translateDrawing, translatePlane, tryCatch, tryCatchAsync, twistExtrude, typeCastError, undoLast, unsupportedError, unwrap, unwrapErr, unwrapOr, unwrapOrElse, updateNode, updateRoles, uvBounds, uvCoordinates, validSolid, validatePlanetary, validationError, variableFillet, vecAdd, vecAngle, vecCross, vecDistance, vecDot, vecEquals, vecIsZero, vecLength, vecLengthSq, vecNegate, vecNormalize, vecProjectToPlane, vecRepr, vecRotate, vecScale, vecSub, vertex, vertexFinder, vertexPosition, verticesOfEdge, voxelBoolean, voxelBooleanField, voxelBooleanFieldShapes, voxelBooleanShapes, voxelField, voxelFieldFromShape, walkAssembly, windingNumbers, wire, wireFinder, wireLoop, wiresOfFace, withKernel, withKernelDir, withKernelPnt, withKernelVec, withQuality, withScope, withScopeResult, withScopeResultAsync, withTier, zip as zipResults };
6003
+ export { BaseSketcher2d, BlueprintSketcher, BrepBugError, BrepErrorCode, BrepWrapperError, BrepkitAdapter, CompoundSketch, DEFAULT_CAPABILITIES, DEG2RAD, DisposalScope, EXACT_BREP_CAPABILITIES, FaceSketcher, HASH_CODE_MAX, OK, OcctWasmAdapter, RAD2DEG, Sketch, Sketcher, Sketches, addChild, addHoles, addMate, addStep, adjacentFaces, all, andThen, applyGlue, applyMatrix, approximateCurve, as2D, as3D, asTopo, assignRoles, autoHeal, bezier, blueprintToDXF, booleanPipeline, booleans_exports as booleans, boss, box, bsplineApprox, bug, cameraFromPlane, cameraLookAt, captureHint, cast, castShape, castShape3D, chamfer, chamferDistAngle as chamferDistAngleShape, chamferWithEvolution, checkAllInterferences, checkBoolean, checkInterference, circle, circularPattern, classifyPointOnFace, clearMeshCache, clone, closedWire, collect, collectShapes, colorFaces, colorShape, complexExtrude, composeTransforms, compound, compoundSketchExtrude, compoundSketchFace, compoundSketchLoft, compoundSketchRevolve, computationError, computeStraightSkeleton, cone, construction_exports as construction, convexHull, cornerFinder, countNodes, createAssembly, createAssemblyNode, createBlueprint, createCamera, createCompound, createCompoundBlueprint, createDistanceQuery, createEdge, createFace, createHandle, createHistory, createKernelHandle, createMeshCache, createNamedPlane, createOperationRegistry, createPlane, createRef, createRegistry, createShell, createSolid, createTaskQueue, createVertex, createWire, createWorkerClient, createWorkerHandler, csg_exports as csg, currentQuality, curve2dBoundingBox, curve2dDistanceFrom, curve2dFirstPoint, curve2dIsOnCurve, curve2dLastPoint, curve2dParameter, curve2dSplitAt, curve2dTangentAt, curveEndPoint, curveIsClosed, curveIsPeriodic, curveLength, curvePeriod, curvePointAt, curveStartPoint, curveTangentAt, cut, cut2D, cutAll, cutAllBisect, cutBlueprints, cutWithEvolution, cylinder, defaultScorer, dequeueTask, describe, deserializeDrawing, deserializeHistory, fromBREP as deserializeShape, downcast, draft, draw, drawCircle, drawEllipse, drawFaceOutline, drawParametricFunction, drawPointsInterpolation, drawPolysides, drawProjection, drawRectangle, drawRoundedRectangle, drawSingleCircle, drawSingleEllipse, drawText, drawingChamfer, drawingCut, drawingFillet, drawingFuse, drawingIntersect, drawingToSketchOnPlane, drill, edgeFinder, edgesOfFace, ellipse, ellipseArc, ellipsoid, enqueueTask, err, exportAssemblySTEP, exportDXF, exportGlb, exportGltf, exportIGES, exportOBJ, exportSTEP, exportSTEPConfigured, exportSTL, exportThreeMF, extrude, extrudeAll, face, faceCenter, faceFinder, faceGeomType, faceOrientation, facesOfEdge, fieldBoolean, fieldContour, fieldOffset, fieldReinit, fieldShell, fill, filledFace, fillet, filletWithEvolution, findFacesByTag, findNode, findStep, fixSelfIntersection, fixShape, flatMap, flatten, flipFaceOrientation, flipOrientation, fontMetrics, fromBREP$1 as fromBREP, fromKernelDir, fromKernelPnt, fromKernelVec, fromNullable, fuse, fuse2D, fuseAll, fuseAllBisect, fuseBlueprints, fuseWithEvolution, gearGeometry, getActiveVoxelId, getBounds, getBounds2D, getCompSolids, getCurveType, getDisposalStats, getEdges, getFaceColor, getFaceOrigins, getFaceTags, getFaces, getFont, getHashCode, getShape as getHistoryShape, getKernel, getKernelCapabilities, getKernelTier, getNurbsCurveData, getNurbsSurfaceData, getOrientation, getOrientation2D, getPerformanceStats, getShapeColor, getShapeKind, getShells, getSingleFace, getSolids, getSurfaceType, getTagMetadata, getVertices, getVoxel, getWires, guidedSweep, heal, healFace, healSolid, healWire, helix, hull, importDXF, importGLB, importIGES, importOBJ, importSTEP, importSTL, importSVG, importSVGPathD, importThreeMF, init, initFromManifold, initFromOC, initVoxel, innerWires, interpolateCurve, intersect, intersect2D, intersectBlueprints, intersectWithEvolution, invalidateShapeCache, ioNs_exports as io, ioError, is2D, is3D, isChamferRadius, isClosedWire, isCompSolid, isCompound, isDisposeRequest, isEdge, isEmpty, isEqualShape, isErr, isErrorResponse, isFace, isFilletRadius, isInitRequest, isInside2D, isLive, isManifoldShell, isNumber, isOk, isOperationRequest, isOrientedFace, isPlanarFace, isPlanarWire, isProjectionPlane, isEmpty$1 as isQueueEmpty, isSameShape, isShape1D, isShape3D, isShell, isSolid, isSuccessResponse, isValid, isValidSolid, isVertex, isWire, iterCompSolids, iterEdges, iterFaces, iterShells, iterSolids, iterTopo, iterVertices, iterWires, kernelCall, kernelCallRaw, kernelCallScoped, kernelError, latticeInfill, latticeInfillShape, line, linearPattern, loadFont, loft, loftAll, makeBaseBox, makeExternalGear, makeInternalGear, makePlane, makePlanetaryGear, makeProjectedEdges, manifoldShell, map, mapBoth, mapErr, match, measureArea, measureCurvatureAt, measureCurvatureAtMid, measureDistance, measureDistanceProps, measureLength, measureLinearProps, measureSurfaceProps, measureVolume, measureVolumeProps, measurement_exports as measurement, mesh, meshEdges, meshMultiLOD, minkowski, mirror, mirror2D, mirrorDrawing, mirrorJoin, modifiers_exports as modifiers, modifyStep, moduleInitError, multiSectionSweep, normalAt, offset, offsetFace, offsetMesh, offsetShape, offsetWire2D, ok, or, orElse, organiseBlueprints, orientedFace, outerWire, patterns_exports as patterns, pendingCount, pipeline, pivotPlane, planarFace, planarWire, planetPlacements, pocket, pointOnSurface, pointsInside, polygon, polyhedron, polysideInnerRadius, polysidesBlueprint, positionOnCurve, prewarm, primitives_exports as primitives, projectEdges, projectPointOnFace, query_exports as query, queryError, rectangularPattern, registerHandler, registerKernel, registerKernelTier, registerOperation, registerShape, registerVoxel, rejectAll, removeChild, removeHolesFromFace, repairMesh, replayFrom, replayHistory, resetDisposalStats, resetPerformanceStats, resize, resolve, resolve3D, resolveDirection, resolvePlane, resolveRef, reverseCurve, revolve, roof, rotate, rotate2D, rotateDrawing, roundedRectangleBlueprint, scale, scale2D, scaleDrawing, box$1 as sdfBox, capsule as sdfCapsule, cone$1 as sdfCone, cylinder$1 as sdfCylinder, plane as sdfPlane, roundedBox as sdfRoundedBox, sphere as sdfSphere, torus as sdfTorus, section, sectionToFace, serializeHistory, setShapeOrigin, setTagMetadata, sewShells, shape, shapeToMeshInput, shapeType, sharedEdges, shell, shellMesh, shellShape, shellWithEvolution, simplify, sketchCircle, sketchEllipse, sketchExtrude, sketchFace, sketchFaceOffset, sketchHelix, sketchLoft, sketchOnFace2D, sketchOnPlane2D, sketchParametricFunction, sketchPolysides, sketchRectangle, sketchRevolve, sketchRoundedRectangle, sketchSweep, sketchText, sketchWires, sketcherStateError, slice, solid, solidFromShell, solveAssembly, sphere$1 as sphere, split, stepCount, stepsFrom, stretch2D, subFace, supportExtrude, supportsConstraintSketch, supportsProjection, surfaceFromGrid, surfaceFromImage, sweep, tagFaces, tangentArc, tap, tapErr, textBlueprints, textMetrics, thicken, threePointArc, toBREP, toBufferGeometryData, toGroupedBufferGeometryData, toKernelVec, toLODGeometryData, toLineGeometryData, toSVGPathD, toVec2, toVec3, torus$1 as torus, tpmsLattice, transformCopy, transforms_exports as transforms, translate, translate2D, translateDrawing, translatePlane, tryCatch, tryCatchAsync, twistExtrude, typeCastError, undoLast, unsupportedError, unwrap, unwrapErr, unwrapOr, unwrapOrElse, updateNode, updateRoles, uvBounds, uvCoordinates, validSolid, validatePlanetary, validationError, variableFillet, vecAdd, vecAngle, vecCross, vecDistance, vecDot, vecEquals, vecIsZero, vecLength, vecLengthSq, vecNegate, vecNormalize, vecProjectToPlane, vecRepr, vecRotate, vecScale, vecSub, vertex, vertexFinder, vertexPosition, verticesOfEdge, voxelBoolean, voxelBooleanField, voxelBooleanFieldShapes, voxelBooleanShapes, voxelField, voxelFieldFromShape, walkAssembly, windingNumbers, wire, wireFinder, wireLoop, wiresOfFace, withKernel, withKernelDir, withKernelPnt, withKernelVec, withQuality, withScope, withScopeResult, withScopeResultAsync, withTier, zip as zipResults };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Field-first implicit CAD domain (ADR-0013, brepjs-implicit Phase 1).
3
+ *
4
+ * An analytic SDF expression tree that rasterizes DIRECTLY into the voxel
5
+ * substrate's dense grid with no input mesh — the field-first twin of the
6
+ * mesh-first voxel path. Primitives compose through CSG / smooth / domain
7
+ * combinators into an {@link SdfHandle}, which rasterizes to a banded-SDF
8
+ * {@link VoxelFieldHandle} for contour / offset / shell.
9
+ */
10
+ export type { SdfHandle, SdfBounds } from './sdfFns.js';
11
+ export { sphere, box, roundedBox, cylinder, cone, capsule, torus, plane } from './sdfFns.js';
@@ -0,0 +1,59 @@
1
+ import { Result } from '../core/result.js';
2
+ import { WasmSdf } from '../voxel/engine.js';
3
+ import { VoxelFieldHandle, VoxelFieldOptions } from '../voxel/fieldFns.js';
4
+ /** Explicit world bounds `[min..max]` for {@link SdfHandle.rasterizeIn}. */
5
+ export interface SdfBounds {
6
+ min: [number, number, number];
7
+ max: [number, number, number];
8
+ }
9
+ /**
10
+ * A disposable handle around an analytic SDF expression. Every combinator clones
11
+ * into a NEW wasm node and returns a NEW handle (the wasm `Sdf` is a value, not a
12
+ * mutable builder), so an `SdfHandle` is immutable — the receiver stays valid and
13
+ * must be disposed independently. `rasterize` produces a banded-SDF
14
+ * {@link VoxelFieldHandle} you can boolean / offset / shell / contour.
15
+ *
16
+ * Dispose is mandatory: use `using`, or call `[Symbol.dispose]()`, to free the
17
+ * WASM expression tree. Intermediate handles in a chain (`a.union(b).shell(t)`)
18
+ * each own a wasm allocation; bind them to `using` or dispose them explicitly.
19
+ */
20
+ export interface SdfHandle {
21
+ /** The wrapped WASM expression. Throws if the handle has been disposed. */
22
+ readonly value: WasmSdf;
23
+ /** Whether the backing WASM expression has been freed. */
24
+ readonly disposed: boolean;
25
+ [Symbol.dispose](): void;
26
+ union(other: SdfHandle): SdfHandle;
27
+ intersection(other: SdfHandle): SdfHandle;
28
+ difference(other: SdfHandle): SdfHandle;
29
+ smoothUnion(other: SdfHandle, k: number): SdfHandle;
30
+ smoothIntersection(other: SdfHandle, k: number): SdfHandle;
31
+ smoothDifference(other: SdfHandle, k: number): SdfHandle;
32
+ offset(distance: number): SdfHandle;
33
+ round(radius: number): SdfHandle;
34
+ shell(thickness: number): SdfHandle;
35
+ onion(thickness: number): SdfHandle;
36
+ translate(x: number, y: number, z: number): SdfHandle;
37
+ rotate(ax: number, ay: number, az: number, angle: number): SdfHandle;
38
+ scale(s: number): SdfHandle;
39
+ /** Rasterize into a banded-SDF field over the expression's analytic bounds. */
40
+ rasterize(opts?: VoxelFieldOptions): Result<VoxelFieldHandle>;
41
+ /** Rasterize over explicit world bounds (clips unbounded primitives). */
42
+ rasterizeIn(bounds: SdfBounds, opts?: VoxelFieldOptions): Result<VoxelFieldHandle>;
43
+ }
44
+ /** A sphere of radius `r`, centered at the origin. */
45
+ export declare function sphere(r: number, id?: string): Result<SdfHandle>;
46
+ /** An axis-aligned box of half-extents `(hx, hy, hz)`, centered at the origin. */
47
+ export declare function box(hx: number, hy: number, hz: number, id?: string): Result<SdfHandle>;
48
+ /** A box with rounded edges of radius `r`. */
49
+ export declare function roundedBox(hx: number, hy: number, hz: number, r: number, id?: string): Result<SdfHandle>;
50
+ /** A capped cylinder, axis +Z, radius `r`, total height `h`, centered at origin. */
51
+ export declare function cylinder(r: number, h: number, id?: string): Result<SdfHandle>;
52
+ /** A capped cone centered at the origin: base radius `r` at z = −h/2 tapering to an apex at z = +h/2. */
53
+ export declare function cone(r: number, h: number, id?: string): Result<SdfHandle>;
54
+ /** A capsule: a line segment `a`→`b` of radius `r`. */
55
+ export declare function capsule(a: [number, number, number], b: [number, number, number], r: number, id?: string): Result<SdfHandle>;
56
+ /** A torus in the XY plane (axis +Z): a `minor`-radius tube on a `major` circle. */
57
+ export declare function torus(major: number, minor: number, id?: string): Result<SdfHandle>;
58
+ /** A half-space: the plane through `h·n` with outward normal `n` (normalized). */
59
+ export declare function plane(n: [number, number, number], h: number, id?: string): Result<SdfHandle>;
package/dist/index.d.ts CHANGED
@@ -40,6 +40,8 @@ export { importSVGPathD, importSVG, type SVGImportOptions } from './io/svgImport
40
40
  export { exportSTEPConfigured, type StepExportOptions, type StepExportPart, } from './io/stepConfigFns.js';
41
41
  export { initVoxel, registerVoxel, getVoxel, getActiveVoxelId, windingNumbers, pointsInside, repairMesh, offsetMesh, shellMesh, voxelBoolean, offsetShape, shellShape, voxelBooleanShapes, voxelField, voxelBooleanField, fieldBoolean, fieldOffset, fieldShell, fieldReinit, fieldContour, voxelFieldFromShape, voxelBooleanFieldShapes, shapeToMeshInput, } from './voxel/index.js';
42
42
  export type { VoxelEngine, VoxelMeshInput, VoxelRepairResult, RepairOptions, VoxelOpOptions, VoxelFieldHandle, VoxelFieldOptions, VoxelBooleanOp, } from './voxel/index.js';
43
+ export { sphere as sdfSphere, box as sdfBox, roundedBox as sdfRoundedBox, cylinder as sdfCylinder, cone as sdfCone, capsule as sdfCapsule, torus as sdfTorus, plane as sdfPlane, } from './implicit/index.js';
44
+ export type { SdfHandle, SdfBounds } from './implicit/index.js';
43
45
  export { latticeInfill, latticeInfillShape, tpmsLattice } from './lattice/index.js';
44
46
  export type { LatticeType, LatticeOptions, LatticeBounds } from './lattice/index.js';
45
47
  export { default as Sketcher } from './sketching/sketcher.js';
@@ -29,9 +29,60 @@ export interface VoxelEngine {
29
29
  * satisfied by the generated `VoxelField` wasm-bindgen class.
30
30
  */
31
31
  VoxelField: WasmVoxelFieldConstructor;
32
+ /**
33
+ * Field-first analytic SDF builder (ADR-0013). Static primitive constructors
34
+ * and combinator methods compose an opaque expression tree that rasterizes
35
+ * directly into a {@link WasmVoxelField} with no input mesh. Structurally
36
+ * satisfied by the generated `Sdf` wasm-bindgen class.
37
+ */
38
+ Sdf: WasmSdfConstructor;
32
39
  /** Engine artifact version, for loader/artifact compatibility checks. */
33
40
  version(): string;
34
41
  }
42
+ /**
43
+ * Static surface of the wasm `Sdf` class: the primitive constructors that seed an
44
+ * expression tree (centered at the origin unless noted). Each returns a fresh
45
+ * {@link WasmSdf}. Structurally satisfied by the generated `Sdf` class.
46
+ */
47
+ export interface WasmSdfConstructor {
48
+ sphere(r: number): WasmSdf;
49
+ box_(hx: number, hy: number, hz: number): WasmSdf;
50
+ rounded_box(hx: number, hy: number, hz: number, r: number): WasmSdf;
51
+ cylinder(r: number, h: number): WasmSdf;
52
+ cone(r: number, h: number): WasmSdf;
53
+ capsule(ax: number, ay: number, az: number, bx: number, by: number, bz: number, r: number): WasmSdf;
54
+ torus(major: number, minor: number): WasmSdf;
55
+ plane(nx: number, ny: number, nz: number, h: number): WasmSdf;
56
+ }
57
+ /**
58
+ * An opaque analytic SDF expression. Every combinator CLONES into a new node and
59
+ * returns a fresh `WasmSdf` (wasm-bindgen has no shared borrow across calls), so an
60
+ * `Sdf` is a value, not a mutable builder. `rasterize` builds a banded-SDF
61
+ * {@link WasmVoxelField}; `free()` releases the backing WASM expression tree.
62
+ * Structurally satisfied by the generated `Sdf` wasm-bindgen class.
63
+ */
64
+ export interface WasmSdf {
65
+ union(other: WasmSdf): WasmSdf;
66
+ intersection(other: WasmSdf): WasmSdf;
67
+ difference(other: WasmSdf): WasmSdf;
68
+ smooth_union(other: WasmSdf, k: number): WasmSdf;
69
+ smooth_intersection(other: WasmSdf, k: number): WasmSdf;
70
+ smooth_difference(other: WasmSdf, k: number): WasmSdf;
71
+ offset(d: number): WasmSdf;
72
+ round(r: number): WasmSdf;
73
+ shell(t: number): WasmSdf;
74
+ onion(t: number): WasmSdf;
75
+ translate(x: number, y: number, z: number): WasmSdf;
76
+ rotate(ax: number, ay: number, az: number, angle: number): WasmSdf;
77
+ scale(s: number): WasmSdf;
78
+ /** Rasterize into a banded-SDF dense field over the expression's analytic bounds. */
79
+ rasterize(resolution: number, padding: number): WasmVoxelField;
80
+ /** Rasterize over explicit `[min..max]` bounds (clips unbounded primitives). */
81
+ rasterize_in(min_x: number, min_y: number, min_z: number, max_x: number, max_y: number, max_z: number, resolution: number, padding: number): WasmVoxelField;
82
+ /** Release the backing WASM expression-tree allocation (wasm-bindgen lifecycle). */
83
+ free(): void;
84
+ [Symbol.dispose](): void;
85
+ }
35
86
  /**
36
87
  * Constructor of the wasm `VoxelField` class. `new VoxelField(verts, tris, res,
37
88
  * padding)` voxelizes a mesh into a persistent dense field. Throws (as a JS
@@ -45,6 +45,7 @@ export interface VoxelFieldHandle {
45
45
  /** Surface-Nets contour the current field to a mesh (the field stays alive). */
46
46
  contour(): KernelMeshResult;
47
47
  }
48
+ export declare function makeFieldHandle(raw: WasmVoxelField): VoxelFieldHandle;
48
49
  /**
49
50
  * Voxelize a mesh into a persistent dense {@link VoxelFieldHandle}: one grid you
50
51
  * can boolean / offset / shell / reinit in place, then contour once. The handle
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.67.0",
3
+ "version": "18.68.0",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",