brepjs 18.66.2 → 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.
@@ -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
@@ -38,8 +38,10 @@ 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
+ 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';
@@ -23,9 +23,103 @@ 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;
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;
26
39
  /** Engine artifact version, for loader/artifact compatibility checks. */
27
40
  version(): string;
28
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
+ }
86
+ /**
87
+ * Constructor of the wasm `VoxelField` class. `new VoxelField(verts, tris, res,
88
+ * padding)` voxelizes a mesh into a persistent dense field. Throws (as a JS
89
+ * exception) on a non-dense grid or a grid over the voxel cap.
90
+ */
91
+ export interface WasmVoxelFieldConstructor {
92
+ new (verts: Float32Array, tris: Uint32Array, resolution: number, padding: number): WasmVoxelField;
93
+ /**
94
+ * Boolean two meshes onto ONE co-registered dense field (union bbox → voxelize
95
+ * both onto a shared grid → combine), ready to chain. The correct path for
96
+ * "boolean then offset/shell" two independently-described meshes, where
97
+ * {@link WasmVoxelField.boolean} requires the operands to already share grid
98
+ * geometry. `op`: 0=union, 1=intersection, 2=difference A−B.
99
+ */
100
+ boolean_of(verts_a: Float32Array, tris_a: Uint32Array, verts_b: Float32Array, tris_b: Uint32Array, op: number, resolution: number, padding: number): WasmVoxelField;
101
+ }
102
+ /**
103
+ * A persistent dense voxel field. All ops MUTATE IN PLACE (return void), so the
104
+ * same grid persists across a chain. `contour()` reads the zero set into a fresh
105
+ * {@link VoxelRepairResult}; `free()` releases the backing WASM grid (mandatory).
106
+ * Structurally satisfied by the generated `VoxelField` wasm-bindgen class.
107
+ */
108
+ export interface WasmVoxelField {
109
+ /** CSG-combine in place (op: 0=union, 1=intersection, 2=difference self−B). */
110
+ boolean(other: WasmVoxelField, op: number): void;
111
+ /** Offset the surface in place (>0 outward, <0 inward); auto-reinits if dirty. */
112
+ offset(distance: number): void;
113
+ /** Hollow into an inward shell in place (thickness > 0); auto-reinits if dirty. */
114
+ shell(thickness: number): void;
115
+ /** Reinitialize φ to a true SDF (|∇φ|=1) while preserving the zero set. */
116
+ reinit(): void;
117
+ /** Surface-Nets contour the current field to a triangle mesh. */
118
+ contour(): VoxelRepairResult;
119
+ /** Release the backing WASM grid allocation (wasm-bindgen lifecycle). */
120
+ free(): void;
121
+ [Symbol.dispose](): void;
122
+ }
29
123
  /**
30
124
  * The repaired-mesh handle the wasm `repair_mesh` returns. Flat xyz
31
125
  * `positions`/`normals` (length 3·V) and a triangle-list `indices` (3 per tri),
@@ -0,0 +1,121 @@
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
+ export declare function makeFieldHandle(raw: WasmVoxelField): VoxelFieldHandle;
49
+ /**
50
+ * Voxelize a mesh into a persistent dense {@link VoxelFieldHandle}: one grid you
51
+ * can boolean / offset / shell / reinit in place, then contour once. The handle
52
+ * is disposable — free the WASM grid with `using` (or `[Symbol.dispose]()`).
53
+ *
54
+ * `resolution` sizes the longest bbox axis; `padding` is the air-margin ring.
55
+ * Errors on an empty/invalid mesh, or if the grid would exceed the dense budget
56
+ * (the persistent path is dense-only) or the voxel cap.
57
+ */
58
+ export declare function voxelField(mesh: VoxelMeshInput, opts?: VoxelFieldOptions, id?: string): Result<VoxelFieldHandle>;
59
+ /**
60
+ * Boolean two meshes into ONE co-registered, chainable {@link VoxelFieldHandle}:
61
+ * voxelize both onto a single shared grid sized to their union bbox, combine by
62
+ * `op`, and keep the field. This is THE correct way to "boolean then chain
63
+ * offset/shell" two independently-described meshes — unlike {@link fieldBoolean},
64
+ * which requires the operands to already share grid geometry. The result is
65
+ * dirty (the blend drifts the gradient), so a subsequent offset/shell
66
+ * auto-reinitializes. The handle is disposable — free it with `using`.
67
+ *
68
+ * `op` is `'difference'` = A − B. Errors on an empty/invalid mesh, or if the
69
+ * shared grid would exceed the dense budget (the persistent path is dense-only).
70
+ */
71
+ export declare function voxelBooleanField(a: VoxelMeshInput, b: VoxelMeshInput, op: VoxelBooleanOp, opts?: VoxelFieldOptions, id?: string): Result<VoxelFieldHandle>;
72
+ /**
73
+ * CSG-combine two fields IN PLACE, returning the SAME `handle` for chaining. The
74
+ * min/max blend keeps the zero set exact but drifts the gradient near the join,
75
+ * so a subsequent {@link fieldOffset}/{@link fieldShell} auto-reinitializes.
76
+ *
77
+ * PRECONDITION: both operands must be CO-REGISTERED — same origin, spacing, AND
78
+ * dims. Two fields built by {@link voxelField} from DIFFERENT meshes generally do
79
+ * NOT share geometry (each sizes its grid to its own bbox), and the WASM guard
80
+ * rejects that mismatch as an `err(...)` rather than silently blending mismatched
81
+ * coordinate frames. For the easy co-registered path, build the field directly
82
+ * from both meshes with {@link voxelBooleanField}.
83
+ */
84
+ export declare function fieldBoolean(handle: VoxelFieldHandle, other: VoxelFieldHandle, op: VoxelBooleanOp): Result<VoxelFieldHandle>;
85
+ /**
86
+ * Offset the field's surface IN PLACE (>0 outward, <0 inward), returning the
87
+ * SAME `handle`. Auto-reinitializes first if the field is dirty (post-boolean),
88
+ * so the iso-shift always rides a true SDF.
89
+ */
90
+ export declare function fieldOffset(handle: VoxelFieldHandle, distance: number): Result<VoxelFieldHandle>;
91
+ /**
92
+ * Hollow the field into an inward shell of `thickness` IN PLACE, returning the
93
+ * SAME `handle`. Auto-reinitializes first if dirty; the result is dirty again
94
+ * (the shell re-introduces a kink).
95
+ */
96
+ export declare function fieldShell(handle: VoxelFieldHandle, thickness: number): Result<VoxelFieldHandle>;
97
+ /**
98
+ * Explicitly reinitialize φ to a true SDF (|∇φ|=1) while preserving the zero
99
+ * set, returning the SAME `handle`. Idempotent on a clean field. Offset/shell
100
+ * already auto-reinitialize, so this is for advanced control only.
101
+ */
102
+ export declare function fieldReinit(handle: VoxelFieldHandle): Result<VoxelFieldHandle>;
103
+ /**
104
+ * Surface-Nets contour the current field to a {@link KernelMeshResult}. The
105
+ * field stays alive and chainable afterwards (contour borrows it). An empty
106
+ * contour surfaces as `VOXEL_DEGENERATE_RESULT`.
107
+ */
108
+ export declare function fieldContour(handle: VoxelFieldHandle): Result<KernelMeshResult>;
109
+ /**
110
+ * Voxelize a B-rep shape into a persistent {@link VoxelFieldHandle}: tessellate
111
+ * it, then run {@link voxelField}. Threads a meshing failure back as an
112
+ * `err(...)`. The handle is disposable — free it with `using`.
113
+ */
114
+ export declare function voxelFieldFromShape(shape: AnyShape<Dimension>, opts?: VoxelFieldOptions, id?: string): Result<VoxelFieldHandle>;
115
+ /**
116
+ * Boolean two B-rep shapes into one co-registered, chainable
117
+ * {@link VoxelFieldHandle}: tessellate both, then run {@link voxelBooleanField}.
118
+ * `op` is `'difference'` = A − B. Threads either meshing failure back as an
119
+ * `err(...)`. The handle is disposable — free it with `using`.
120
+ */
121
+ 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.68.0",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",