brepjs 18.66.2 → 18.67.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
@@ -218,8 +218,8 @@ function pointsInside(mesh, queries, id) {
218
218
  }
219
219
  //#endregion
220
220
  //#region src/voxel/repairFns.ts
221
- var DEFAULT_RESOLUTION$2 = 48;
222
- var DEFAULT_PADDING$2 = 2;
221
+ var DEFAULT_RESOLUTION$3 = 48;
222
+ var DEFAULT_PADDING$3 = 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,16 +232,16 @@ 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$2;
236
- const padding = opts?.padding ?? DEFAULT_PADDING$2;
235
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$3;
236
+ const padding = opts?.padding ?? DEFAULT_PADDING$3;
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
239
  const engine = resolveEngine(id);
240
240
  if (require_errors.isErr(engine)) return engine;
241
241
  try {
242
242
  try {
243
- var _usingCtx$6 = require_shapeTypes._usingCtx();
244
- const repaired = _usingCtx$6.u(engine.value.repair_mesh(mesh.vertices, mesh.triangles, resolution, padding));
243
+ var _usingCtx$7 = require_shapeTypes._usingCtx();
244
+ const repaired = _usingCtx$7.u(engine.value.repair_mesh(mesh.vertices, mesh.triangles, resolution, padding));
245
245
  const vertexCount = repaired.positions.length / 3;
246
246
  return require_errors.ok({
247
247
  vertices: repaired.positions,
@@ -255,9 +255,9 @@ function repairMesh(mesh, opts, id) {
255
255
  }]
256
256
  });
257
257
  } catch (_) {
258
- _usingCtx$6.e = _;
258
+ _usingCtx$7.e = _;
259
259
  } finally {
260
- _usingCtx$6.d();
260
+ _usingCtx$7.d();
261
261
  }
262
262
  } catch (cause) {
263
263
  return require_errors.err(require_errors.computationError("VOXEL_REPAIR_FAILED", cause instanceof Error ? cause.message : "voxel repair failed (grid too large?).", cause));
@@ -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$1 = 48;
290
- var DEFAULT_PADDING$1 = 2;
291
- var BOOLEAN_OP_CODES = {
289
+ var DEFAULT_RESOLUTION$2 = 48;
290
+ var DEFAULT_PADDING$2 = 2;
291
+ var BOOLEAN_OP_CODES$1 = {
292
292
  union: 0,
293
293
  intersection: 1,
294
294
  difference: 2
295
295
  };
296
- function resolveGridParams(opts) {
297
- const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
298
- const padding = opts?.padding ?? DEFAULT_PADDING$1;
296
+ function resolveGridParams$1(opts) {
297
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$2;
298
+ const padding = opts?.padding ?? DEFAULT_PADDING$2;
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,18 +330,18 @@ 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(opts);
333
+ const params = resolveGridParams$1(opts);
334
334
  if (require_errors.isErr(params)) return params;
335
335
  const engine = resolveEngine(id);
336
336
  if (require_errors.isErr(engine)) return engine;
337
337
  try {
338
338
  try {
339
- var _usingCtx$5 = require_shapeTypes._usingCtx();
340
- return meshFromResult(_usingCtx$5.u(engine.value.offset_mesh(mesh.vertices, mesh.triangles, distance, params.value.resolution, params.value.padding)));
339
+ var _usingCtx$6 = require_shapeTypes._usingCtx();
340
+ return meshFromResult(_usingCtx$6.u(engine.value.offset_mesh(mesh.vertices, mesh.triangles, distance, params.value.resolution, params.value.padding)));
341
341
  } catch (_) {
342
- _usingCtx$5.e = _;
342
+ _usingCtx$6.e = _;
343
343
  } finally {
344
- _usingCtx$5.d();
344
+ _usingCtx$6.d();
345
345
  }
346
346
  } catch (cause) {
347
347
  return require_errors.err(require_errors.computationError("VOXEL_OFFSET_FAILED", cause instanceof Error ? cause.message : "voxel offset failed (grid too large?).", cause));
@@ -359,7 +359,7 @@ 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(opts);
362
+ const params = resolveGridParams$1(opts);
363
363
  if (require_errors.isErr(params)) return params;
364
364
  const engine = resolveEngine(id);
365
365
  if (require_errors.isErr(engine)) return engine;
@@ -390,8 +390,8 @@ function voxelBoolean(a, b, op, opts, id) {
390
390
  if (invalidB) return require_errors.err(invalidB);
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
- const opCode = BOOLEAN_OP_CODES[op];
394
- const params = resolveGridParams(opts);
393
+ const opCode = BOOLEAN_OP_CODES$1[op];
394
+ const params = resolveGridParams$1(opts);
395
395
  if (require_errors.isErr(params)) return params;
396
396
  const engine = resolveEngine(id);
397
397
  if (require_errors.isErr(engine)) return engine;
@@ -441,6 +441,263 @@ function voxelBooleanShapes(a, b, op, opts, id) {
441
441
  return voxelBoolean(meshA.value, meshB.value, op, opts, id);
442
442
  }
443
443
  //#endregion
444
+ //#region src/voxel/fieldFns.ts
445
+ var DEFAULT_RESOLUTION$1 = 48;
446
+ var DEFAULT_PADDING$1 = 2;
447
+ var BOOLEAN_OP_CODES = {
448
+ union: 0,
449
+ intersection: 1,
450
+ difference: 2
451
+ };
452
+ function resolveGridParams(opts) {
453
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
454
+ const padding = opts?.padding ?? DEFAULT_PADDING$1;
455
+ if (!Number.isInteger(resolution) || resolution < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
456
+ if (!Number.isInteger(padding) || padding < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
457
+ return require_errors.ok({
458
+ resolution,
459
+ padding
460
+ });
461
+ }
462
+ /**
463
+ * Copy the buffers out of a WASM {@link VoxelRepairResult} into a plain
464
+ * {@link KernelMeshResult}, freeing the WASM result via `using` (the getters
465
+ * copy, so the mesh survives the free). An empty contour surfaces as an error.
466
+ */
467
+ function meshFromField(field) {
468
+ try {
469
+ var _usingCtx$5 = require_shapeTypes._usingCtx();
470
+ const rawResult = field.contour();
471
+ const { positions, normals, indices } = _usingCtx$5.u({
472
+ value: rawResult,
473
+ [Symbol.dispose]() {
474
+ rawResult.free();
475
+ }
476
+ }).value;
477
+ if (positions.length === 0 || indices.length === 0) return require_errors.err(require_errors.computationError("VOXEL_DEGENERATE_RESULT", "the voxel field contoured to an empty mesh (over-shrunk offset or disjoint operands?)."));
478
+ const vertexCount = positions.length / 3;
479
+ return require_errors.ok({
480
+ vertices: positions,
481
+ normals,
482
+ triangles: indices,
483
+ uvs: new Float32Array(vertexCount * 2),
484
+ faceGroups: [{
485
+ start: 0,
486
+ count: indices.length / 3,
487
+ faceHash: 0
488
+ }]
489
+ });
490
+ } catch (_) {
491
+ _usingCtx$5.e = _;
492
+ } finally {
493
+ _usingCtx$5.d();
494
+ }
495
+ }
496
+ function fieldDeletable(raw) {
497
+ return {
498
+ raw,
499
+ delete() {
500
+ raw.free();
501
+ }
502
+ };
503
+ }
504
+ function makeHandle(raw) {
505
+ const inner = require_shapeTypes.createKernelHandle(fieldDeletable(raw));
506
+ const handle = {
507
+ get value() {
508
+ return inner.value.raw;
509
+ },
510
+ get disposed() {
511
+ return inner.disposed;
512
+ },
513
+ [Symbol.dispose]() {
514
+ inner[Symbol.dispose]();
515
+ },
516
+ boolean(other, op) {
517
+ this.value.boolean(other.value, BOOLEAN_OP_CODES[op]);
518
+ return handle;
519
+ },
520
+ offset(distance) {
521
+ this.value.offset(distance);
522
+ return handle;
523
+ },
524
+ shell(thickness) {
525
+ this.value.shell(thickness);
526
+ return handle;
527
+ },
528
+ reinit() {
529
+ this.value.reinit();
530
+ return handle;
531
+ },
532
+ contour() {
533
+ const mesh = meshFromField(this.value);
534
+ if (require_errors.isErr(mesh)) throw new Error(mesh.error.message);
535
+ return mesh.value;
536
+ }
537
+ };
538
+ return handle;
539
+ }
540
+ /** Live-handle guard (the `VoxelFieldHandle` analogue of disposal's `isLive`). */
541
+ function isLive$1(handle) {
542
+ return !handle.disposed;
543
+ }
544
+ function disposedErr() {
545
+ return require_errors.err(require_errors.validationError("VOXEL_FIELD_DISPOSED", "the voxel field handle has been disposed."));
546
+ }
547
+ /**
548
+ * Voxelize a mesh into a persistent dense {@link VoxelFieldHandle}: one grid you
549
+ * can boolean / offset / shell / reinit in place, then contour once. The handle
550
+ * is disposable — free the WASM grid with `using` (or `[Symbol.dispose]()`).
551
+ *
552
+ * `resolution` sizes the longest bbox axis; `padding` is the air-margin ring.
553
+ * Errors on an empty/invalid mesh, or if the grid would exceed the dense budget
554
+ * (the persistent path is dense-only) or the voxel cap.
555
+ */
556
+ function voxelField(mesh, opts, id) {
557
+ const invalid = validateMesh(mesh);
558
+ if (invalid) return require_errors.err(invalid);
559
+ 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);
561
+ if (require_errors.isErr(params)) return params;
562
+ const engine = resolveEngine(id);
563
+ if (require_errors.isErr(engine)) return engine;
564
+ try {
565
+ return require_errors.ok(makeHandle(new engine.value.VoxelField(mesh.vertices, mesh.triangles, params.value.resolution, params.value.padding)));
566
+ } catch (cause) {
567
+ return require_errors.err(require_errors.computationError("VOXEL_FIELD_VOXELIZE_FAILED", cause instanceof Error ? cause.message : "voxel field voxelization failed (grid too large or non-dense?).", cause));
568
+ }
569
+ }
570
+ /**
571
+ * Boolean two meshes into ONE co-registered, chainable {@link VoxelFieldHandle}:
572
+ * voxelize both onto a single shared grid sized to their union bbox, combine by
573
+ * `op`, and keep the field. This is THE correct way to "boolean then chain
574
+ * offset/shell" two independently-described meshes — unlike {@link fieldBoolean},
575
+ * which requires the operands to already share grid geometry. The result is
576
+ * dirty (the blend drifts the gradient), so a subsequent offset/shell
577
+ * auto-reinitializes. The handle is disposable — free it with `using`.
578
+ *
579
+ * `op` is `'difference'` = A − B. Errors on an empty/invalid mesh, or if the
580
+ * shared grid would exceed the dense budget (the persistent path is dense-only).
581
+ */
582
+ function voxelBooleanField(a, b, op, opts, id) {
583
+ const invalidA = validateMesh(a);
584
+ if (invalidA) return require_errors.err(invalidA);
585
+ const invalidB = validateMesh(b);
586
+ if (invalidB) return require_errors.err(invalidB);
587
+ 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
+ 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);
590
+ if (require_errors.isErr(params)) return params;
591
+ const engine = resolveEngine(id);
592
+ if (require_errors.isErr(engine)) return engine;
593
+ try {
594
+ 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)));
595
+ } catch (cause) {
596
+ return require_errors.err(require_errors.computationError("VOXEL_FIELD_BOOLEAN_FAILED", cause instanceof Error ? cause.message : "voxel field boolean failed (grid too large or non-dense?).", cause));
597
+ }
598
+ }
599
+ /**
600
+ * CSG-combine two fields IN PLACE, returning the SAME `handle` for chaining. The
601
+ * min/max blend keeps the zero set exact but drifts the gradient near the join,
602
+ * so a subsequent {@link fieldOffset}/{@link fieldShell} auto-reinitializes.
603
+ *
604
+ * PRECONDITION: both operands must be CO-REGISTERED — same origin, spacing, AND
605
+ * dims. Two fields built by {@link voxelField} from DIFFERENT meshes generally do
606
+ * NOT share geometry (each sizes its grid to its own bbox), and the WASM guard
607
+ * rejects that mismatch as an `err(...)` rather than silently blending mismatched
608
+ * coordinate frames. For the easy co-registered path, build the field directly
609
+ * from both meshes with {@link voxelBooleanField}.
610
+ */
611
+ function fieldBoolean(handle, other, op) {
612
+ if (!isLive$1(handle) || !isLive$1(other)) return disposedErr();
613
+ try {
614
+ handle.value.boolean(other.value, BOOLEAN_OP_CODES[op]);
615
+ return require_errors.ok(handle);
616
+ } catch (cause) {
617
+ return require_errors.err(require_errors.computationError("VOXEL_FIELD_BOOLEAN_FAILED", cause instanceof Error ? cause.message : "voxel field boolean failed (dim mismatch?).", cause));
618
+ }
619
+ }
620
+ /**
621
+ * Offset the field's surface IN PLACE (>0 outward, <0 inward), returning the
622
+ * SAME `handle`. Auto-reinitializes first if the field is dirty (post-boolean),
623
+ * so the iso-shift always rides a true SDF.
624
+ */
625
+ function fieldOffset(handle, distance) {
626
+ if (!isLive$1(handle)) return disposedErr();
627
+ if (!Number.isFinite(distance)) return require_errors.err(require_errors.validationError("VOXEL_INVALID_DISTANCE", "distance must be a finite number."));
628
+ try {
629
+ handle.value.offset(distance);
630
+ return require_errors.ok(handle);
631
+ } catch (cause) {
632
+ return require_errors.err(require_errors.computationError("VOXEL_FIELD_OFFSET_FAILED", cause instanceof Error ? cause.message : "voxel field offset failed.", cause));
633
+ }
634
+ }
635
+ /**
636
+ * Hollow the field into an inward shell of `thickness` IN PLACE, returning the
637
+ * SAME `handle`. Auto-reinitializes first if dirty; the result is dirty again
638
+ * (the shell re-introduces a kink).
639
+ */
640
+ function fieldShell(handle, thickness) {
641
+ if (!isLive$1(handle)) return disposedErr();
642
+ if (!Number.isFinite(thickness) || thickness <= 0) return require_errors.err(require_errors.validationError("VOXEL_INVALID_THICKNESS", "thickness must be a finite number > 0."));
643
+ try {
644
+ handle.value.shell(thickness);
645
+ return require_errors.ok(handle);
646
+ } catch (cause) {
647
+ return require_errors.err(require_errors.computationError("VOXEL_FIELD_SHELL_FAILED", cause instanceof Error ? cause.message : "voxel field shell failed.", cause));
648
+ }
649
+ }
650
+ /**
651
+ * Explicitly reinitialize φ to a true SDF (|∇φ|=1) while preserving the zero
652
+ * set, returning the SAME `handle`. Idempotent on a clean field. Offset/shell
653
+ * already auto-reinitialize, so this is for advanced control only.
654
+ */
655
+ function fieldReinit(handle) {
656
+ if (!isLive$1(handle)) return disposedErr();
657
+ try {
658
+ handle.value.reinit();
659
+ return require_errors.ok(handle);
660
+ } catch (cause) {
661
+ return require_errors.err(require_errors.computationError("VOXEL_FIELD_REINIT_FAILED", cause instanceof Error ? cause.message : "voxel field reinit failed.", cause));
662
+ }
663
+ }
664
+ /**
665
+ * Surface-Nets contour the current field to a {@link KernelMeshResult}. The
666
+ * field stays alive and chainable afterwards (contour borrows it). An empty
667
+ * contour surfaces as `VOXEL_DEGENERATE_RESULT`.
668
+ */
669
+ function fieldContour(handle) {
670
+ if (!isLive$1(handle)) return disposedErr();
671
+ try {
672
+ return meshFromField(handle.value);
673
+ } catch (cause) {
674
+ return require_errors.err(require_errors.computationError("VOXEL_FIELD_CONTOUR_FAILED", cause instanceof Error ? cause.message : "voxel field contour failed.", cause));
675
+ }
676
+ }
677
+ /**
678
+ * Voxelize a B-rep shape into a persistent {@link VoxelFieldHandle}: tessellate
679
+ * it, then run {@link voxelField}. Threads a meshing failure back as an
680
+ * `err(...)`. The handle is disposable — free it with `using`.
681
+ */
682
+ function voxelFieldFromShape(shape, opts, id) {
683
+ const meshInput = shapeToMeshInput(shape);
684
+ if (require_errors.isErr(meshInput)) return meshInput;
685
+ return voxelField(meshInput.value, opts, id);
686
+ }
687
+ /**
688
+ * Boolean two B-rep shapes into one co-registered, chainable
689
+ * {@link VoxelFieldHandle}: tessellate both, then run {@link voxelBooleanField}.
690
+ * `op` is `'difference'` = A − B. Threads either meshing failure back as an
691
+ * `err(...)`. The handle is disposable — free it with `using`.
692
+ */
693
+ function voxelBooleanFieldShapes(a, b, op, opts, id) {
694
+ const meshA = shapeToMeshInput(a);
695
+ if (require_errors.isErr(meshA)) return meshA;
696
+ const meshB = shapeToMeshInput(b);
697
+ if (require_errors.isErr(meshB)) return meshB;
698
+ return voxelBooleanField(meshA.value, meshB.value, op, opts, id);
699
+ }
700
+ //#endregion
444
701
  //#region src/lattice/latticeFns.ts
445
702
  var LATTICE_TAGS = {
446
703
  gyroid: 0,
@@ -5776,6 +6033,11 @@ exports.faceFinder = require_helpers.faceFinder;
5776
6033
  exports.faceGeomType = require_faceFns.faceGeomType;
5777
6034
  exports.faceOrientation = require_faceFns.faceOrientation;
5778
6035
  exports.facesOfEdge = require_primitiveFns.facesOfEdge;
6036
+ exports.fieldBoolean = fieldBoolean;
6037
+ exports.fieldContour = fieldContour;
6038
+ exports.fieldOffset = fieldOffset;
6039
+ exports.fieldReinit = fieldReinit;
6040
+ exports.fieldShell = fieldShell;
5779
6041
  exports.fill = require_surfaceBuilders.fill;
5780
6042
  exports.filledFace = require_primitiveFns.filledFace;
5781
6043
  exports.fillet = fillet;
@@ -6164,7 +6426,11 @@ exports.vertexFinder = vertexFinder;
6164
6426
  exports.vertexPosition = require_topologyQueryFns.vertexPosition;
6165
6427
  exports.verticesOfEdge = require_primitiveFns.verticesOfEdge;
6166
6428
  exports.voxelBoolean = voxelBoolean;
6429
+ exports.voxelBooleanField = voxelBooleanField;
6430
+ exports.voxelBooleanFieldShapes = voxelBooleanFieldShapes;
6167
6431
  exports.voxelBooleanShapes = voxelBooleanShapes;
6432
+ exports.voxelField = voxelField;
6433
+ exports.voxelFieldFromShape = voxelFieldFromShape;
6168
6434
  exports.walkAssembly = require_historyFns.walkAssembly;
6169
6435
  exports.windingNumbers = windingNumbers;
6170
6436
  exports.wire = require_primitiveFns.wire;
package/dist/brepjs.js CHANGED
@@ -229,8 +229,8 @@ function pointsInside(mesh, queries, id) {
229
229
  }
230
230
  //#endregion
231
231
  //#region src/voxel/repairFns.ts
232
- var DEFAULT_RESOLUTION$2 = 48;
233
- var DEFAULT_PADDING$2 = 2;
232
+ var DEFAULT_RESOLUTION$3 = 48;
233
+ var DEFAULT_PADDING$3 = 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,16 +243,16 @@ 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$2;
247
- const padding = opts?.padding ?? DEFAULT_PADDING$2;
246
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$3;
247
+ const padding = opts?.padding ?? DEFAULT_PADDING$3;
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
250
  const engine = resolveEngine(id);
251
251
  if (isErr(engine)) return engine;
252
252
  try {
253
253
  try {
254
- var _usingCtx$6 = _usingCtx();
255
- const repaired = _usingCtx$6.u(engine.value.repair_mesh(mesh.vertices, mesh.triangles, resolution, padding));
254
+ var _usingCtx$7 = _usingCtx();
255
+ const repaired = _usingCtx$7.u(engine.value.repair_mesh(mesh.vertices, mesh.triangles, resolution, padding));
256
256
  const vertexCount = repaired.positions.length / 3;
257
257
  return ok({
258
258
  vertices: repaired.positions,
@@ -266,9 +266,9 @@ function repairMesh(mesh, opts, id) {
266
266
  }]
267
267
  });
268
268
  } catch (_) {
269
- _usingCtx$6.e = _;
269
+ _usingCtx$7.e = _;
270
270
  } finally {
271
- _usingCtx$6.d();
271
+ _usingCtx$7.d();
272
272
  }
273
273
  } catch (cause) {
274
274
  return err(computationError("VOXEL_REPAIR_FAILED", cause instanceof Error ? cause.message : "voxel repair failed (grid too large?).", cause));
@@ -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$1 = 48;
301
- var DEFAULT_PADDING$1 = 2;
302
- var BOOLEAN_OP_CODES = {
300
+ var DEFAULT_RESOLUTION$2 = 48;
301
+ var DEFAULT_PADDING$2 = 2;
302
+ var BOOLEAN_OP_CODES$1 = {
303
303
  union: 0,
304
304
  intersection: 1,
305
305
  difference: 2
306
306
  };
307
- function resolveGridParams(opts) {
308
- const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
309
- const padding = opts?.padding ?? DEFAULT_PADDING$1;
307
+ function resolveGridParams$1(opts) {
308
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$2;
309
+ const padding = opts?.padding ?? DEFAULT_PADDING$2;
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,18 +341,18 @@ 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(opts);
344
+ const params = resolveGridParams$1(opts);
345
345
  if (isErr(params)) return params;
346
346
  const engine = resolveEngine(id);
347
347
  if (isErr(engine)) return engine;
348
348
  try {
349
349
  try {
350
- var _usingCtx$5 = _usingCtx();
351
- return meshFromResult(_usingCtx$5.u(engine.value.offset_mesh(mesh.vertices, mesh.triangles, distance, params.value.resolution, params.value.padding)));
350
+ var _usingCtx$6 = _usingCtx();
351
+ return meshFromResult(_usingCtx$6.u(engine.value.offset_mesh(mesh.vertices, mesh.triangles, distance, params.value.resolution, params.value.padding)));
352
352
  } catch (_) {
353
- _usingCtx$5.e = _;
353
+ _usingCtx$6.e = _;
354
354
  } finally {
355
- _usingCtx$5.d();
355
+ _usingCtx$6.d();
356
356
  }
357
357
  } catch (cause) {
358
358
  return err(computationError("VOXEL_OFFSET_FAILED", cause instanceof Error ? cause.message : "voxel offset failed (grid too large?).", cause));
@@ -370,7 +370,7 @@ 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(opts);
373
+ const params = resolveGridParams$1(opts);
374
374
  if (isErr(params)) return params;
375
375
  const engine = resolveEngine(id);
376
376
  if (isErr(engine)) return engine;
@@ -401,8 +401,8 @@ function voxelBoolean(a, b, op, opts, id) {
401
401
  if (invalidB) return err(invalidB);
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
- const opCode = BOOLEAN_OP_CODES[op];
405
- const params = resolveGridParams(opts);
404
+ const opCode = BOOLEAN_OP_CODES$1[op];
405
+ const params = resolveGridParams$1(opts);
406
406
  if (isErr(params)) return params;
407
407
  const engine = resolveEngine(id);
408
408
  if (isErr(engine)) return engine;
@@ -452,6 +452,263 @@ function voxelBooleanShapes(a, b, op, opts, id) {
452
452
  return voxelBoolean(meshA.value, meshB.value, op, opts, id);
453
453
  }
454
454
  //#endregion
455
+ //#region src/voxel/fieldFns.ts
456
+ var DEFAULT_RESOLUTION$1 = 48;
457
+ var DEFAULT_PADDING$1 = 2;
458
+ var BOOLEAN_OP_CODES = {
459
+ union: 0,
460
+ intersection: 1,
461
+ difference: 2
462
+ };
463
+ function resolveGridParams(opts) {
464
+ const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
465
+ const padding = opts?.padding ?? DEFAULT_PADDING$1;
466
+ if (!Number.isInteger(resolution) || resolution < 1) return err(validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
467
+ if (!Number.isInteger(padding) || padding < 1) return err(validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
468
+ return ok({
469
+ resolution,
470
+ padding
471
+ });
472
+ }
473
+ /**
474
+ * Copy the buffers out of a WASM {@link VoxelRepairResult} into a plain
475
+ * {@link KernelMeshResult}, freeing the WASM result via `using` (the getters
476
+ * copy, so the mesh survives the free). An empty contour surfaces as an error.
477
+ */
478
+ function meshFromField(field) {
479
+ try {
480
+ var _usingCtx$5 = _usingCtx();
481
+ const rawResult = field.contour();
482
+ const { positions, normals, indices } = _usingCtx$5.u({
483
+ value: rawResult,
484
+ [Symbol.dispose]() {
485
+ rawResult.free();
486
+ }
487
+ }).value;
488
+ if (positions.length === 0 || indices.length === 0) return err(computationError("VOXEL_DEGENERATE_RESULT", "the voxel field contoured to an empty mesh (over-shrunk offset or disjoint operands?)."));
489
+ const vertexCount = positions.length / 3;
490
+ return ok({
491
+ vertices: positions,
492
+ normals,
493
+ triangles: indices,
494
+ uvs: new Float32Array(vertexCount * 2),
495
+ faceGroups: [{
496
+ start: 0,
497
+ count: indices.length / 3,
498
+ faceHash: 0
499
+ }]
500
+ });
501
+ } catch (_) {
502
+ _usingCtx$5.e = _;
503
+ } finally {
504
+ _usingCtx$5.d();
505
+ }
506
+ }
507
+ function fieldDeletable(raw) {
508
+ return {
509
+ raw,
510
+ delete() {
511
+ raw.free();
512
+ }
513
+ };
514
+ }
515
+ function makeHandle(raw) {
516
+ const inner = createKernelHandle(fieldDeletable(raw));
517
+ const handle = {
518
+ get value() {
519
+ return inner.value.raw;
520
+ },
521
+ get disposed() {
522
+ return inner.disposed;
523
+ },
524
+ [Symbol.dispose]() {
525
+ inner[Symbol.dispose]();
526
+ },
527
+ boolean(other, op) {
528
+ this.value.boolean(other.value, BOOLEAN_OP_CODES[op]);
529
+ return handle;
530
+ },
531
+ offset(distance) {
532
+ this.value.offset(distance);
533
+ return handle;
534
+ },
535
+ shell(thickness) {
536
+ this.value.shell(thickness);
537
+ return handle;
538
+ },
539
+ reinit() {
540
+ this.value.reinit();
541
+ return handle;
542
+ },
543
+ contour() {
544
+ const mesh = meshFromField(this.value);
545
+ if (isErr(mesh)) throw new Error(mesh.error.message);
546
+ return mesh.value;
547
+ }
548
+ };
549
+ return handle;
550
+ }
551
+ /** Live-handle guard (the `VoxelFieldHandle` analogue of disposal's `isLive`). */
552
+ function isLive$1(handle) {
553
+ return !handle.disposed;
554
+ }
555
+ function disposedErr() {
556
+ return err(validationError("VOXEL_FIELD_DISPOSED", "the voxel field handle has been disposed."));
557
+ }
558
+ /**
559
+ * Voxelize a mesh into a persistent dense {@link VoxelFieldHandle}: one grid you
560
+ * can boolean / offset / shell / reinit in place, then contour once. The handle
561
+ * is disposable — free the WASM grid with `using` (or `[Symbol.dispose]()`).
562
+ *
563
+ * `resolution` sizes the longest bbox axis; `padding` is the air-margin ring.
564
+ * Errors on an empty/invalid mesh, or if the grid would exceed the dense budget
565
+ * (the persistent path is dense-only) or the voxel cap.
566
+ */
567
+ function voxelField(mesh, opts, id) {
568
+ const invalid = validateMesh(mesh);
569
+ if (invalid) return err(invalid);
570
+ 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);
572
+ if (isErr(params)) return params;
573
+ const engine = resolveEngine(id);
574
+ if (isErr(engine)) return engine;
575
+ try {
576
+ return ok(makeHandle(new engine.value.VoxelField(mesh.vertices, mesh.triangles, params.value.resolution, params.value.padding)));
577
+ } catch (cause) {
578
+ return err(computationError("VOXEL_FIELD_VOXELIZE_FAILED", cause instanceof Error ? cause.message : "voxel field voxelization failed (grid too large or non-dense?).", cause));
579
+ }
580
+ }
581
+ /**
582
+ * Boolean two meshes into ONE co-registered, chainable {@link VoxelFieldHandle}:
583
+ * voxelize both onto a single shared grid sized to their union bbox, combine by
584
+ * `op`, and keep the field. This is THE correct way to "boolean then chain
585
+ * offset/shell" two independently-described meshes — unlike {@link fieldBoolean},
586
+ * which requires the operands to already share grid geometry. The result is
587
+ * dirty (the blend drifts the gradient), so a subsequent offset/shell
588
+ * auto-reinitializes. The handle is disposable — free it with `using`.
589
+ *
590
+ * `op` is `'difference'` = A − B. Errors on an empty/invalid mesh, or if the
591
+ * shared grid would exceed the dense budget (the persistent path is dense-only).
592
+ */
593
+ function voxelBooleanField(a, b, op, opts, id) {
594
+ const invalidA = validateMesh(a);
595
+ if (invalidA) return err(invalidA);
596
+ const invalidB = validateMesh(b);
597
+ if (invalidB) return err(invalidB);
598
+ 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
+ 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);
601
+ if (isErr(params)) return params;
602
+ const engine = resolveEngine(id);
603
+ if (isErr(engine)) return engine;
604
+ try {
605
+ 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)));
606
+ } catch (cause) {
607
+ return err(computationError("VOXEL_FIELD_BOOLEAN_FAILED", cause instanceof Error ? cause.message : "voxel field boolean failed (grid too large or non-dense?).", cause));
608
+ }
609
+ }
610
+ /**
611
+ * CSG-combine two fields IN PLACE, returning the SAME `handle` for chaining. The
612
+ * min/max blend keeps the zero set exact but drifts the gradient near the join,
613
+ * so a subsequent {@link fieldOffset}/{@link fieldShell} auto-reinitializes.
614
+ *
615
+ * PRECONDITION: both operands must be CO-REGISTERED — same origin, spacing, AND
616
+ * dims. Two fields built by {@link voxelField} from DIFFERENT meshes generally do
617
+ * NOT share geometry (each sizes its grid to its own bbox), and the WASM guard
618
+ * rejects that mismatch as an `err(...)` rather than silently blending mismatched
619
+ * coordinate frames. For the easy co-registered path, build the field directly
620
+ * from both meshes with {@link voxelBooleanField}.
621
+ */
622
+ function fieldBoolean(handle, other, op) {
623
+ if (!isLive$1(handle) || !isLive$1(other)) return disposedErr();
624
+ try {
625
+ handle.value.boolean(other.value, BOOLEAN_OP_CODES[op]);
626
+ return ok(handle);
627
+ } catch (cause) {
628
+ return err(computationError("VOXEL_FIELD_BOOLEAN_FAILED", cause instanceof Error ? cause.message : "voxel field boolean failed (dim mismatch?).", cause));
629
+ }
630
+ }
631
+ /**
632
+ * Offset the field's surface IN PLACE (>0 outward, <0 inward), returning the
633
+ * SAME `handle`. Auto-reinitializes first if the field is dirty (post-boolean),
634
+ * so the iso-shift always rides a true SDF.
635
+ */
636
+ function fieldOffset(handle, distance) {
637
+ if (!isLive$1(handle)) return disposedErr();
638
+ if (!Number.isFinite(distance)) return err(validationError("VOXEL_INVALID_DISTANCE", "distance must be a finite number."));
639
+ try {
640
+ handle.value.offset(distance);
641
+ return ok(handle);
642
+ } catch (cause) {
643
+ return err(computationError("VOXEL_FIELD_OFFSET_FAILED", cause instanceof Error ? cause.message : "voxel field offset failed.", cause));
644
+ }
645
+ }
646
+ /**
647
+ * Hollow the field into an inward shell of `thickness` IN PLACE, returning the
648
+ * SAME `handle`. Auto-reinitializes first if dirty; the result is dirty again
649
+ * (the shell re-introduces a kink).
650
+ */
651
+ function fieldShell(handle, thickness) {
652
+ if (!isLive$1(handle)) return disposedErr();
653
+ if (!Number.isFinite(thickness) || thickness <= 0) return err(validationError("VOXEL_INVALID_THICKNESS", "thickness must be a finite number > 0."));
654
+ try {
655
+ handle.value.shell(thickness);
656
+ return ok(handle);
657
+ } catch (cause) {
658
+ return err(computationError("VOXEL_FIELD_SHELL_FAILED", cause instanceof Error ? cause.message : "voxel field shell failed.", cause));
659
+ }
660
+ }
661
+ /**
662
+ * Explicitly reinitialize φ to a true SDF (|∇φ|=1) while preserving the zero
663
+ * set, returning the SAME `handle`. Idempotent on a clean field. Offset/shell
664
+ * already auto-reinitialize, so this is for advanced control only.
665
+ */
666
+ function fieldReinit(handle) {
667
+ if (!isLive$1(handle)) return disposedErr();
668
+ try {
669
+ handle.value.reinit();
670
+ return ok(handle);
671
+ } catch (cause) {
672
+ return err(computationError("VOXEL_FIELD_REINIT_FAILED", cause instanceof Error ? cause.message : "voxel field reinit failed.", cause));
673
+ }
674
+ }
675
+ /**
676
+ * Surface-Nets contour the current field to a {@link KernelMeshResult}. The
677
+ * field stays alive and chainable afterwards (contour borrows it). An empty
678
+ * contour surfaces as `VOXEL_DEGENERATE_RESULT`.
679
+ */
680
+ function fieldContour(handle) {
681
+ if (!isLive$1(handle)) return disposedErr();
682
+ try {
683
+ return meshFromField(handle.value);
684
+ } catch (cause) {
685
+ return err(computationError("VOXEL_FIELD_CONTOUR_FAILED", cause instanceof Error ? cause.message : "voxel field contour failed.", cause));
686
+ }
687
+ }
688
+ /**
689
+ * Voxelize a B-rep shape into a persistent {@link VoxelFieldHandle}: tessellate
690
+ * it, then run {@link voxelField}. Threads a meshing failure back as an
691
+ * `err(...)`. The handle is disposable — free it with `using`.
692
+ */
693
+ function voxelFieldFromShape(shape, opts, id) {
694
+ const meshInput = shapeToMeshInput(shape);
695
+ if (isErr(meshInput)) return meshInput;
696
+ return voxelField(meshInput.value, opts, id);
697
+ }
698
+ /**
699
+ * Boolean two B-rep shapes into one co-registered, chainable
700
+ * {@link VoxelFieldHandle}: tessellate both, then run {@link voxelBooleanField}.
701
+ * `op` is `'difference'` = A − B. Threads either meshing failure back as an
702
+ * `err(...)`. The handle is disposable — free it with `using`.
703
+ */
704
+ function voxelBooleanFieldShapes(a, b, op, opts, id) {
705
+ const meshA = shapeToMeshInput(a);
706
+ if (isErr(meshA)) return meshA;
707
+ const meshB = shapeToMeshInput(b);
708
+ if (isErr(meshB)) return meshB;
709
+ return voxelBooleanField(meshA.value, meshB.value, op, opts, id);
710
+ }
711
+ //#endregion
455
712
  //#region src/lattice/latticeFns.ts
456
713
  var LATTICE_TAGS = {
457
714
  gyroid: 0,
@@ -5583,4 +5840,4 @@ var csg_exports = /* @__PURE__ */ __exportAll({
5583
5840
  withEvaluator: () => withEvaluator
5584
5841
  });
5585
5842
  //#endregion
5586
- 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, 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, voxelBooleanShapes, walkAssembly, windingNumbers, wire, wireFinder, wireLoop, wiresOfFace, withKernel, withKernelDir, withKernelPnt, withKernelVec, withQuality, withScope, withScopeResult, withScopeResultAsync, withTier, zip as zipResults };
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 };
package/dist/index.d.ts CHANGED
@@ -38,8 +38,8 @@ export { exportDXF, blueprintToDXF, type DXFEntity, type DXFExportOptions, } fro
38
38
  export { exportThreeMF, type ThreeMFExportOptions, type ThreeMFMaterial, } from './io/threemfExportFns.js';
39
39
  export { importSVGPathD, importSVG, type SVGImportOptions } from './io/svgImportFns.js';
40
40
  export { exportSTEPConfigured, type StepExportOptions, type StepExportPart, } from './io/stepConfigFns.js';
41
- export { initVoxel, registerVoxel, getVoxel, getActiveVoxelId, windingNumbers, pointsInside, repairMesh, offsetMesh, shellMesh, voxelBoolean, offsetShape, shellShape, voxelBooleanShapes, shapeToMeshInput, } from './voxel/index.js';
42
- export type { VoxelEngine, VoxelMeshInput, VoxelRepairResult, RepairOptions, VoxelOpOptions, } from './voxel/index.js';
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
+ export type { VoxelEngine, VoxelMeshInput, VoxelRepairResult, RepairOptions, VoxelOpOptions, VoxelFieldHandle, VoxelFieldOptions, VoxelBooleanOp, } from './voxel/index.js';
43
43
  export { latticeInfill, latticeInfillShape, tpmsLattice } from './lattice/index.js';
44
44
  export type { LatticeType, LatticeOptions, LatticeBounds } from './lattice/index.js';
45
45
  export { default as Sketcher } from './sketching/sketcher.js';
@@ -23,9 +23,52 @@ export interface VoxelEngine {
23
23
  shell_mesh(verts: Float32Array, tris: Uint32Array, thickness: number, resolution: number, padding: number): VoxelRepairResult;
24
24
  /** Voxel CSG of two meshes (op: 0=union, 1=intersection, 2=difference A−B). */
25
25
  voxel_boolean(verts_a: Float32Array, tris_a: Uint32Array, verts_b: Float32Array, tris_b: Uint32Array, op: number, resolution: number, padding: number): VoxelRepairResult;
26
+ /**
27
+ * Persistent dense voxel-field class, for same-grid op chains: voxelize a mesh
28
+ * once, then boolean/offset/shell/reinit in place, contour once. Structurally
29
+ * satisfied by the generated `VoxelField` wasm-bindgen class.
30
+ */
31
+ VoxelField: WasmVoxelFieldConstructor;
26
32
  /** Engine artifact version, for loader/artifact compatibility checks. */
27
33
  version(): string;
28
34
  }
35
+ /**
36
+ * Constructor of the wasm `VoxelField` class. `new VoxelField(verts, tris, res,
37
+ * padding)` voxelizes a mesh into a persistent dense field. Throws (as a JS
38
+ * exception) on a non-dense grid or a grid over the voxel cap.
39
+ */
40
+ export interface WasmVoxelFieldConstructor {
41
+ new (verts: Float32Array, tris: Uint32Array, resolution: number, padding: number): WasmVoxelField;
42
+ /**
43
+ * Boolean two meshes onto ONE co-registered dense field (union bbox → voxelize
44
+ * both onto a shared grid → combine), ready to chain. The correct path for
45
+ * "boolean then offset/shell" two independently-described meshes, where
46
+ * {@link WasmVoxelField.boolean} requires the operands to already share grid
47
+ * geometry. `op`: 0=union, 1=intersection, 2=difference A−B.
48
+ */
49
+ boolean_of(verts_a: Float32Array, tris_a: Uint32Array, verts_b: Float32Array, tris_b: Uint32Array, op: number, resolution: number, padding: number): WasmVoxelField;
50
+ }
51
+ /**
52
+ * A persistent dense voxel field. All ops MUTATE IN PLACE (return void), so the
53
+ * same grid persists across a chain. `contour()` reads the zero set into a fresh
54
+ * {@link VoxelRepairResult}; `free()` releases the backing WASM grid (mandatory).
55
+ * Structurally satisfied by the generated `VoxelField` wasm-bindgen class.
56
+ */
57
+ export interface WasmVoxelField {
58
+ /** CSG-combine in place (op: 0=union, 1=intersection, 2=difference self−B). */
59
+ boolean(other: WasmVoxelField, op: number): void;
60
+ /** Offset the surface in place (>0 outward, <0 inward); auto-reinits if dirty. */
61
+ offset(distance: number): void;
62
+ /** Hollow into an inward shell in place (thickness > 0); auto-reinits if dirty. */
63
+ shell(thickness: number): void;
64
+ /** Reinitialize φ to a true SDF (|∇φ|=1) while preserving the zero set. */
65
+ reinit(): void;
66
+ /** Surface-Nets contour the current field to a triangle mesh. */
67
+ contour(): VoxelRepairResult;
68
+ /** Release the backing WASM grid allocation (wasm-bindgen lifecycle). */
69
+ free(): void;
70
+ [Symbol.dispose](): void;
71
+ }
29
72
  /**
30
73
  * The repaired-mesh handle the wasm `repair_mesh` returns. Flat xyz
31
74
  * `positions`/`normals` (length 3·V) and a triangle-list `indices` (3 per tri),
@@ -0,0 +1,120 @@
1
+ import { Result } from '../core/result.js';
2
+ import { KernelMeshResult } from '../kernel/types.js';
3
+ import { AnyShape, Dimension } from '../core/shapeTypes.js';
4
+ import { VoxelMeshInput } from './signFns.js';
5
+ import { WasmVoxelField } from './engine.js';
6
+ /** Field tuning. `resolution` sizes the longest bbox axis in voxels; `padding`
7
+ * is the air-margin ring (>= 1) Surface Nets needs AND the headroom an outward
8
+ * offset has before it clips at the grid boundary (the grid is fixed at
9
+ * voxelize time — size both for the intended maximum offset). */
10
+ export interface VoxelFieldOptions {
11
+ resolution?: number;
12
+ padding?: number;
13
+ }
14
+ /** Boolean op selector for {@link fieldBoolean} / {@link VoxelFieldHandle.boolean}. */
15
+ export type VoxelBooleanOp = 'union' | 'intersection' | 'difference';
16
+ /**
17
+ * A persistent, disposable voxel field. Carries the wrapped WASM field as
18
+ * `value` and a fluent op-chain (`.boolean().offset().shell().contour()`) that
19
+ * throws on the rare WASM error (mirroring the `shape()` facade's
20
+ * throw-on-`Err` convention), so a `using`-scoped chain reads cleanly:
21
+ *
22
+ * ```ts
23
+ * using field = voxelField(meshA).unwrap();
24
+ * using other = voxelField(meshB).unwrap();
25
+ * const mesh = field.boolean(other, 'union').offset(2).contour();
26
+ * ```
27
+ *
28
+ * Dispose is mandatory (FinalizationRegistry is an unreliable safety net): use
29
+ * `using`, or call `[Symbol.dispose]()` explicitly, to free the WASM grid.
30
+ */
31
+ export interface VoxelFieldHandle {
32
+ /** The wrapped WASM field. Throws if the handle has been disposed. */
33
+ readonly value: WasmVoxelField;
34
+ /** Whether the backing WASM grid has been freed. */
35
+ readonly disposed: boolean;
36
+ [Symbol.dispose](): void;
37
+ /** CSG-combine with `other` in place (marks the field for lazy reinit). */
38
+ boolean(other: VoxelFieldHandle, op: VoxelBooleanOp): VoxelFieldHandle;
39
+ /** Offset the surface in place (>0 outward, <0 inward); auto-reinits if dirty. */
40
+ offset(distance: number): VoxelFieldHandle;
41
+ /** Hollow into an inward shell in place (thickness > 0); auto-reinits if dirty. */
42
+ shell(thickness: number): VoxelFieldHandle;
43
+ /** Reinitialize φ to a true SDF while preserving the zero set. */
44
+ reinit(): VoxelFieldHandle;
45
+ /** Surface-Nets contour the current field to a mesh (the field stays alive). */
46
+ contour(): KernelMeshResult;
47
+ }
48
+ /**
49
+ * Voxelize a mesh into a persistent dense {@link VoxelFieldHandle}: one grid you
50
+ * can boolean / offset / shell / reinit in place, then contour once. The handle
51
+ * is disposable — free the WASM grid with `using` (or `[Symbol.dispose]()`).
52
+ *
53
+ * `resolution` sizes the longest bbox axis; `padding` is the air-margin ring.
54
+ * Errors on an empty/invalid mesh, or if the grid would exceed the dense budget
55
+ * (the persistent path is dense-only) or the voxel cap.
56
+ */
57
+ export declare function voxelField(mesh: VoxelMeshInput, opts?: VoxelFieldOptions, id?: string): Result<VoxelFieldHandle>;
58
+ /**
59
+ * Boolean two meshes into ONE co-registered, chainable {@link VoxelFieldHandle}:
60
+ * voxelize both onto a single shared grid sized to their union bbox, combine by
61
+ * `op`, and keep the field. This is THE correct way to "boolean then chain
62
+ * offset/shell" two independently-described meshes — unlike {@link fieldBoolean},
63
+ * which requires the operands to already share grid geometry. The result is
64
+ * dirty (the blend drifts the gradient), so a subsequent offset/shell
65
+ * auto-reinitializes. The handle is disposable — free it with `using`.
66
+ *
67
+ * `op` is `'difference'` = A − B. Errors on an empty/invalid mesh, or if the
68
+ * shared grid would exceed the dense budget (the persistent path is dense-only).
69
+ */
70
+ export declare function voxelBooleanField(a: VoxelMeshInput, b: VoxelMeshInput, op: VoxelBooleanOp, opts?: VoxelFieldOptions, id?: string): Result<VoxelFieldHandle>;
71
+ /**
72
+ * CSG-combine two fields IN PLACE, returning the SAME `handle` for chaining. The
73
+ * min/max blend keeps the zero set exact but drifts the gradient near the join,
74
+ * so a subsequent {@link fieldOffset}/{@link fieldShell} auto-reinitializes.
75
+ *
76
+ * PRECONDITION: both operands must be CO-REGISTERED — same origin, spacing, AND
77
+ * dims. Two fields built by {@link voxelField} from DIFFERENT meshes generally do
78
+ * NOT share geometry (each sizes its grid to its own bbox), and the WASM guard
79
+ * rejects that mismatch as an `err(...)` rather than silently blending mismatched
80
+ * coordinate frames. For the easy co-registered path, build the field directly
81
+ * from both meshes with {@link voxelBooleanField}.
82
+ */
83
+ export declare function fieldBoolean(handle: VoxelFieldHandle, other: VoxelFieldHandle, op: VoxelBooleanOp): Result<VoxelFieldHandle>;
84
+ /**
85
+ * Offset the field's surface IN PLACE (>0 outward, <0 inward), returning the
86
+ * SAME `handle`. Auto-reinitializes first if the field is dirty (post-boolean),
87
+ * so the iso-shift always rides a true SDF.
88
+ */
89
+ export declare function fieldOffset(handle: VoxelFieldHandle, distance: number): Result<VoxelFieldHandle>;
90
+ /**
91
+ * Hollow the field into an inward shell of `thickness` IN PLACE, returning the
92
+ * SAME `handle`. Auto-reinitializes first if dirty; the result is dirty again
93
+ * (the shell re-introduces a kink).
94
+ */
95
+ export declare function fieldShell(handle: VoxelFieldHandle, thickness: number): Result<VoxelFieldHandle>;
96
+ /**
97
+ * Explicitly reinitialize φ to a true SDF (|∇φ|=1) while preserving the zero
98
+ * set, returning the SAME `handle`. Idempotent on a clean field. Offset/shell
99
+ * already auto-reinitialize, so this is for advanced control only.
100
+ */
101
+ export declare function fieldReinit(handle: VoxelFieldHandle): Result<VoxelFieldHandle>;
102
+ /**
103
+ * Surface-Nets contour the current field to a {@link KernelMeshResult}. The
104
+ * field stays alive and chainable afterwards (contour borrows it). An empty
105
+ * contour surfaces as `VOXEL_DEGENERATE_RESULT`.
106
+ */
107
+ export declare function fieldContour(handle: VoxelFieldHandle): Result<KernelMeshResult>;
108
+ /**
109
+ * Voxelize a B-rep shape into a persistent {@link VoxelFieldHandle}: tessellate
110
+ * it, then run {@link voxelField}. Threads a meshing failure back as an
111
+ * `err(...)`. The handle is disposable — free it with `using`.
112
+ */
113
+ export declare function voxelFieldFromShape(shape: AnyShape<Dimension>, opts?: VoxelFieldOptions, id?: string): Result<VoxelFieldHandle>;
114
+ /**
115
+ * Boolean two B-rep shapes into one co-registered, chainable
116
+ * {@link VoxelFieldHandle}: tessellate both, then run {@link voxelBooleanField}.
117
+ * `op` is `'difference'` = A − B. Threads either meshing failure back as an
118
+ * `err(...)`. The handle is disposable — free it with `using`.
119
+ */
120
+ export declare function voxelBooleanFieldShapes(a: AnyShape<Dimension>, b: AnyShape<Dimension>, op: VoxelBooleanOp, opts?: VoxelFieldOptions, id?: string): Result<VoxelFieldHandle>;
@@ -9,8 +9,10 @@ export type { VoxelEngine, VoxelRepairResult } from './engine.js';
9
9
  export type { VoxelMeshInput } from './signFns.js';
10
10
  export type { RepairOptions } from './repairFns.js';
11
11
  export type { VoxelOpOptions } from './meshOpsFns.js';
12
+ export type { VoxelFieldHandle, VoxelFieldOptions, VoxelBooleanOp } from './fieldFns.js';
12
13
  export { registerVoxel, getVoxel, getActiveVoxelId, initVoxel } from './registry.js';
13
14
  export { windingNumbers, pointsInside } from './signFns.js';
14
15
  export { repairMesh } from './repairFns.js';
15
16
  export { offsetMesh, shellMesh, voxelBoolean, offsetShape, shellShape, voxelBooleanShapes, } from './meshOpsFns.js';
17
+ export { voxelField, voxelBooleanField, fieldBoolean, fieldOffset, fieldShell, fieldReinit, fieldContour, voxelFieldFromShape, voxelBooleanFieldShapes, } from './fieldFns.js';
16
18
  export { shapeToMeshInput } from './shapeMesh.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.66.2",
3
+ "version": "18.67.0",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",