brepjs-verify 0.3.0 → 0.8.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,1810 @@
1
+ # brepjs
2
+
3
+ > Web CAD library with a layered architecture and pluggable kernel abstraction layer (supports OpenCascade and brepkit WASM backends). Create 2D sketches, extrude/revolve/loft/sweep into 3D solids, apply booleans/fillets/chamfers/shells, query topology, measure geometry, heal shapes, manage assemblies, and import/export STEP, STL, IGES, glTF, DXF, 3MF, OBJ, and SVG.
4
+
5
+ ## API Reference & Discoverability
6
+
7
+ - **Hosted API docs**: https://andymai.github.io/brepjs/ — searchable TypeDoc reference for all exports
8
+ - **Function lookup table**: `docs/function-lookup.md` — alphabetical index mapping every symbol to its sub-path
9
+ - **Which API guide**: `docs/which-api.md` — choosing between Sketcher, functional API, Drawing
10
+
11
+ ## Authoring with an AI Agent (brepjs-verify)
12
+
13
+ If you are an LLM generating brepjs CAD, use `brepjs-verify` — a CLI that runs your model on a real kernel and returns a deterministic report. **Judge a part by the report, never by how the code reads.** Published on npm as `brepjs-verify`; the companion Claude Code skill installs via `/plugin marketplace add andymai/brepjs` then `/plugin install brepjs-verify@brepjs`.
14
+
15
+ Install the runtime where `brepjs` resolves (it also bundles its own `brepjs` + `occt-wasm`, so it runs standalone):
16
+ ```bash
17
+ npm i -D brepjs-verify
18
+ ```
19
+
20
+ A model is a `.brep.ts` module: a default-exported zero-arg function returning a shape (or `Result<shape>`), optionally with an `expected` block that the CLI asserts:
21
+ ```ts
22
+ import { box } from 'brepjs';
23
+ export const expected = { volume: 8000, tolerancePct: 1 }; // optional: volume|area|bounds + tolerancePct
24
+ export default () => box(40, 20, 10, { centered: true });
25
+ ```
26
+
27
+ The loop, in order: brief → author `.brep.ts` → declare `expected` → `brepjs-verify verify part.brep.ts --check --json report.json` → review `--snapshot shots/` → repair the smallest responsible section → `--step part.step` and hand off. Use `npx -y brepjs-verify …` if not installed.
28
+
29
+ The report (stdout JSON) is the source of truth:
30
+ - `ok` — `true` only if valid AND every assertion passes. `false` → not done.
31
+ - `checks` — kernel validity (manifold solid, positive volume). A failed check = broken geometry.
32
+ - `measurements` — measured `volume`/`area`/`bounds`.
33
+ - `assertions` — declared intent vs reality. A failed assertion = valid-but-wrong (off dimensions).
34
+ - `hints` — actionable fix + next step keyed on an error `code`; read before guessing.
35
+ - `errorInfos` — raw `{code,message}` failures (authoring/kernel/export). Cite the `code` when repairing.
36
+
37
+ CLI subcommands: `verify <file>` (default; flags `--check`, `--json`, `--step`, `--glb`, `--snapshot <dir>`, `--serve`), `init <name>` (scaffold), `watch <file>`, `export <file>` (`--step`/`--glb`/`--stl`/`--all` behind a validity gate), `measure <a> [b]`, `diff <a> <b>`. Exits non-zero when not `ok`.
38
+
39
+ Reliable first-try: primitives, booleans, 2D sketch → extrude, fillet/chamfer, shell/offset, transforms. Advanced (verify carefully, expect iteration): sweeps, lofts, revolves, multi-section, assemblies, text. Author parts in ESM (a CJS project needs `"type":"module"` or a `.mts` file); Node 24+ strips the part's types natively.
40
+
41
+ ## Setup & Initialization
42
+
43
+ Install:
44
+ ```bash
45
+ npm install brepjs occt-wasm
46
+ ```
47
+
48
+ ### Quick start (zero ceremony, ESM only)
49
+
50
+ ```typescript
51
+ import { box } from 'brepjs/quick';
52
+ const myBox = box(10, 10, 10); // just works
53
+ ```
54
+
55
+ `brepjs/quick` auto-initializes the WASM kernel via top-level await. No setup code needed. Requires ESM (top-level await is incompatible with CJS).
56
+
57
+ ### Standard (explicit registration, works with CJS)
58
+
59
+ ```typescript
60
+ import { OcctKernel } from 'occt-wasm';
61
+ import { registerKernel, OcctWasmAdapter } from 'brepjs';
62
+
63
+ const kernel = await OcctKernel.init();
64
+ registerKernel('occt-wasm', OcctWasmAdapter.fromKernel(kernel));
65
+ ```
66
+
67
+ The kernel must be registered once before using any brepjs API. All shape-creating functions require the kernel to be initialized.
68
+
69
+ ### Custom or alternative WASM build
70
+
71
+ `initFromOC(oc)` accepts any OpenCascade WASM instance — it is not tied to `brepjs-opencascade`. You can use a custom or alternative WASM build as long as it exposes the standard OpenCascade API:
72
+
73
+ ```typescript
74
+ import { initFromOC, box } from 'brepjs';
75
+
76
+ // Use a custom OpenCascade WASM build
77
+ import customOC from 'my-custom-opencascade';
78
+ const oc = await customOC({ locateFile: (file) => `/custom-wasm/${file}` });
79
+ initFromOC(oc);
80
+
81
+ // All brepjs functions now use the custom kernel
82
+ const b = box(10, 10, 10);
83
+ ```
84
+
85
+ The kernel abstraction layer (`src/kernel/`) translates all brepjs calls into OCCT operations. Any WASM build exposing the standard OpenCascade C++ API (via Emscripten bindings) is compatible. This enables: custom WASM builds with additional OCCT modules, builds optimized for specific use cases (smaller footprint, specific features), and self-hosted WASM files served from your own CDN.
86
+
87
+ ### Quick reference
88
+
89
+ See `docs/cheat-sheet.md` for a single-page reference covering all common operations (primitives, booleans, transforms, fillets, measurement, export, memory management, error handling).
90
+
91
+ Works in both Node.js and browsers. For browser setup with Vite, see `docs/getting-started.md` (Browser Setup section). Try the [interactive playground](https://brepjs.dev/playground) for live experimentation.
92
+
93
+ ## API Style
94
+
95
+ brepjs uses a functional API with branded types (`Solid`, `Face`, `Edge`, etc.) and `Result<T>` for error handling. Functions like `fuse()`, `translate()`, `fillet()` are pure — they never dispose inputs. Use `unwrap()` to extract values from `Result<T>`, or `isOk()`/`isErr()` to check success.
96
+
97
+ ### Immutability
98
+
99
+ All brepjs operations are immutable — they return new shapes and never modify the original:
100
+
101
+ ```typescript
102
+ import { box, translate, rotate, fuse, cylinder, measureVolume, unwrap } from 'brepjs';
103
+
104
+ const original = box(30, 20, 10);
105
+ const moved = translate(original, [100, 0, 0]);
106
+ const rotated = rotate(moved, 45, { axis: [0, 0, 1] });
107
+ const combined = unwrap(fuse(original, cylinder(5, 15)));
108
+
109
+ // original is unchanged after all operations
110
+ console.log(measureVolume(original)); // still the original box volume
111
+ console.log(measureVolume(moved)); // same volume, different position
112
+ console.log(measureVolume(rotated)); // same volume, different orientation
113
+ ```
114
+
115
+ This means you can safely chain multiple transformations while preserving access to intermediate and original shapes. Each call produces a new independent shape.
116
+
117
+ ### Branded Shape Types
118
+
119
+ Shape types are lightweight branded handles (not classes) with a phantom `D extends Dimension` parameter for compile-time 2D/3D safety (default `'3D'`):
120
+ - `Vertex<D>`, `Edge<D>`, `Wire<D>`, `Face<D>`, `Compound<D>` — dimension-parameterized (default `'3D'`)
121
+ - `Shell`, `Solid`, `CompSolid` — always 3D (no dimension parameter)
122
+ - `AnyShape<D>` — union of all shape types
123
+ - `Shape3D` — union of Shell | Solid | CompSolid | Compound<'3D'>
124
+ - `Shape1D<D>` — Edge<D> | Wire<D>
125
+
126
+ #### Validity Brands
127
+
128
+ Layered on top of base types to encode topological invariants at compile time:
129
+ - `ClosedWire<D>` — wire proven to form a closed loop. Required by `face()`.
130
+ - `OrientedFace<D>` — face with consistent normal. Required by `extrude()`, `revolve()`.
131
+ - `ManifoldShell` — shell proven to be watertight.
132
+ - `ValidSolid` — solid passing BRepCheck validation. Returned by `box()`, `cylinder()`, etc.
133
+
134
+ Smart constructors prove validity at runtime: `closedWire(w)`, `orientedFace(f)`, `manifoldShell(s)`, `validSolid(s)` → `Result<T, string>` (check with `isOk`, extract with `unwrap` — there is no `.valid`/`.value` field).
135
+ Type guards narrow in-place: `isClosedWire(w)`, `isOrientedFace(f)`, `isManifoldShell(s)`, `isValidSolid(s)`.
136
+ Convenience: `wireLoop(edges)` assembles edges and verifies closure → `Result<ClosedWire>`.
137
+
138
+ All shapes have `.wrapped` (kernel handle) and `.delete()`. Use `using` syntax for auto-cleanup.
139
+
140
+ Also exported: `Sketch`, `CompoundSketch`, `Sketches` (multi-profile container), `Drawing`, `DrawingPen`, `Blueprint`, `CompoundBlueprint`, `Blueprints`
141
+
142
+ ### Type Conversion Helpers
143
+
144
+ ```typescript
145
+ import { toVec3, toVec2, resolveDirection } from 'brepjs';
146
+
147
+ toVec3([x, y]): Vec3 // 2D → 3D (z=0)
148
+ toVec3([x, y, z]): Vec3 // identity
149
+ toVec2([x, y, z]): Vec2 // 3D → 2D (drop z)
150
+ resolveDirection('X'): Vec3 // → [1, 0, 0]
151
+ resolveDirection('Y'): Vec3 // → [0, 1, 0]
152
+ resolveDirection('Z'): Vec3 // → [0, 0, 1]
153
+ resolveDirection([a, b, c]): Vec3 // identity
154
+ ```
155
+
156
+ ### Shape Serialization
157
+
158
+ ```typescript
159
+ import { toBREP, fromBREP } from 'brepjs';
160
+
161
+ const brep = toBREP(shape); // shape → BREP string
162
+ const restored = fromBREP(brep); // BREP string → AnyShape
163
+ ```
164
+
165
+ ## Drawing & Sketching (Primary Entry Point)
166
+
167
+ Most workflows start by drawing a 2D profile, then extruding or revolving it into 3D.
168
+
169
+ ### DrawingPen (2D builder)
170
+
171
+ ```typescript
172
+ import { draw, drawRectangle, drawCircle, drawPolysides } from 'brepjs';
173
+
174
+ // Freeform drawing with a pen
175
+ const shape2d = draw()
176
+ .movePointerTo([0, 0])
177
+ .lineTo([10, 0])
178
+ .lineTo([10, 5])
179
+ .hLine(-5)
180
+ .vLine(5)
181
+ .close();
182
+
183
+ // Canned shapes
184
+ const rect = drawRectangle(100, 50);
185
+ const circle = drawCircle(25);
186
+ const hex = drawPolysides(10, 6);
187
+ ```
188
+
189
+ DrawingPen methods:
190
+ - `movePointerTo(point)` — Move without drawing
191
+ - `lineTo(point)`, `line(dx, dy)`, `hLine(d)`, `vLine(d)` — Straight lines
192
+ - `hLineTo(xPos)`, `vLineTo(yPos)` — Absolute line targets
193
+ - `polarLine(distance, angle)`, `polarLineTo(point)` — Polar coordinates
194
+ - `tangentLine(distance)` — Tangent continuation
195
+ - `threePointsArcTo(end, innerPoint)`, `threePointsArc(dx, dy, viaX, viaY)` — 3-point arcs
196
+ - `tangentArcTo(end)`, `tangentArc(dx, dy)` — Tangent arcs
197
+ - `sagittaArcTo(end, sagitta)`, `sagittaArc(dx, dy, sagitta)` — Sagitta arcs
198
+ - `vSagittaArc(d, sagitta)`, `hSagittaArc(d, sagitta)` — Vertical/horizontal sagitta arcs
199
+ - `bulgeArcTo(end, bulge)`, `bulgeArc(dx, dy, bulge)` — Bulge arcs
200
+ - `vBulgeArc(d, bulge)`, `hBulgeArc(d, bulge)` — Vertical/horizontal bulge arcs
201
+ - `ellipseTo(end, hRadius, vRadius, rotation?, longAxis?, sweep?)` — Elliptical arcs
202
+ - `halfEllipseTo(end, vRadius, sweep?)` — Half-ellipse arcs
203
+ - `bezierCurveTo(end, controlPoints)` — General Bezier
204
+ - `quadraticBezierCurveTo(end, control)` — Quadratic Bezier
205
+ - `cubicBezierCurveTo(end, c1, c2)` — Cubic Bezier
206
+ - `smoothSplineTo(end, config?)`, `smoothSpline(dx, dy, config?)` — Smooth splines
207
+ - `fillet(radius)`, `chamfer(radius)` — Corner treatments (call before next segment)
208
+ - `close()` — Close the path and return a Drawing
209
+ - `closeWithMirror()` — Close by mirroring the path
210
+ - `closeWithCustomCorner(radius, mode?)` — Close with a fillet or chamfer at the join. `mode`: `'fillet'` (default) or `'chamfer'`
211
+ - `done()` — Leave path open and return a Drawing
212
+
213
+ Drawing factory functions:
214
+ - `draw(origin?)` — Start a new pen at optional `[x, y]` point
215
+ - `drawRectangle(w, h, r?)` — Rectangle centered at origin, optional corner radius `r: number` or `{rx?, ry?}`
216
+ - `drawRoundedRectangle(w, h, r?)` — Alias for `drawRectangle`
217
+ - `drawCircle(radius)` — Circle centered at origin (built from two sagitta arcs)
218
+ - `drawSingleCircle(radius)` — Circle as a single curve primitive (more efficient, less compatible with corner ops)
219
+ - `drawEllipse(major, minor)` — Ellipse centered at origin (built from two half-ellipse arcs)
220
+ - `drawSingleEllipse(major, minor)` — Ellipse as a single curve primitive
221
+ - `drawPolysides(radius, sides, sagitta?)` — Regular polygon. `sagitta` curves the sides (positive = outward)
222
+ - `drawText(text, {startX?, startY?, fontSize?, fontFamily?})` — Text outline (requires `loadFont()` first)
223
+ - `drawPointsInterpolation(points, approxConfig?, {closeShape?})` — BSpline through Point2D[]. `approxConfig`: `{tolerance?, degMax?, degMin?, smoothing?}`
224
+ - `drawParametricFunction(fn, {pointsCount?, start?, stop?, closeShape?}, approxConfig?)` — Parametric curve `fn: (t: number) => Point2D`
225
+ - `drawProjection(shape, camera?)` — Project 3D shape to 2D. `camera`: `ProjectionPlane | Camera` (default `'front'`). Returns `{visible: Drawing, hidden: Drawing}`
226
+ - `drawFaceOutline(face)` — Extract face outer wire as a 2D Drawing
227
+ - `deserializeDrawing(data)` — Reconstruct from string produced by `drawing.serialize()`
228
+
229
+ Drawing instance methods:
230
+ - `clone()`, `serialize()` — Copy and serialization
231
+ - `boundingBox` — Get `BoundingBox2d`
232
+ - `repr` — String representation of the drawing
233
+ - `blueprint` — Access the underlying `Blueprint` (throws if compound)
234
+ - `translate(dx, dy)` or `translate([dx, dy])` — Move the drawing
235
+ - `rotate(angle, center?)` — Rotate in degrees around optional center point
236
+ - `scale(factor, center?)` — Uniform scale around optional center
237
+ - `mirror(dirOrCenter, origin?, mode?)` — Mirror. `mode`: `'center'` (default) or `'plane'`
238
+ - `stretch(ratio, direction, origin)` — Non-uniform scaling along a direction
239
+ - `cut(other)`, `fuse(other)`, `intersect(other)` — 2D boolean operations (return new Drawing)
240
+ - `fillet(radius, filter?)`, `chamfer(radius, filter?)` — Corner treatments. `filter`: `(c: CornerFinderFn) => CornerFinderFn`
241
+ - `offset(distance, config?)` — Offset all curves by distance
242
+ - `approximate('svg', options?)` — Approximate curves for SVG compatibility
243
+ - `sketchOnPlane(plane?, origin?)` — Convert to 3D Sketch/Sketches. Returns `SketchInterface | Sketches`
244
+ - `sketchOnFace(face, scaleMode)` — Project onto a face. `scaleMode`: controls UV mapping behavior
245
+ - `punchHole(shape, faceFinder, {height?, origin?, draftAngle?}?)` — Punch this 2D profile through a 3D shape
246
+ - `toSVG(margin?)` — Full SVG string with `<svg>` tag
247
+ - `toSVGViewBox(margin?)` — SVG string with viewBox attribute
248
+ - `toSVGPaths()` — Array of SVG path `d` strings
249
+
250
+ ### Sketcher (3D sketching on a plane)
251
+
252
+ ```typescript
253
+ import { Sketcher, sketchCircle, sketchRectangle } from 'brepjs';
254
+
255
+ // Sketch on XY plane, then extrude
256
+ const box = new Sketcher('XY')
257
+ .movePointerTo([-5, -5])
258
+ .lineTo([5, -5])
259
+ .lineTo([5, 5])
260
+ .lineTo([-5, 5])
261
+ .close()
262
+ .extrude(10);
263
+
264
+ // Canned sketch shortcuts
265
+ const cylinder = sketchCircle(10).extrude(20);
266
+ const prism = sketchRectangle(30, 20).extrude(15);
267
+
268
+ // Sketching on offset planes
269
+ const top = sketchCircle(5, { plane: 'XY', origin: 20 }); // XY plane at Z=20
270
+ const angled = sketchCircle(5, { plane: myCustomPlane }); // custom Plane object
271
+ ```
272
+
273
+ Sketcher has the same drawing methods as DrawingPen plus arc/ellipse/spline methods, closing with `.close()` or `.done()` to produce a `Sketch`.
274
+
275
+ Canned sketch functions — all accept an optional last argument `PlaneConfig = { plane?: PlaneName | Plane, origin?: PointInput | number }`. When `origin` is a number, it offsets the named plane along its normal by that distance:
276
+ - `sketchCircle(radius, planeConfig?)` — Circle sketch
277
+ - `sketchRectangle(w, h, planeConfig?)` — Rectangle sketch
278
+ - `sketchRoundedRectangle(w, h, r?, planeConfig?)` — Rounded rectangle. `r`: `number` or `{rx?, ry?}`
279
+ - `sketchPolysides(radius, sides, sagitta?, planeConfig?)` — Regular polygon
280
+ - `sketchEllipse(xRadius?, yRadius?, planeConfig?)` — Ellipse (defaults: xRadius=1, yRadius=2)
281
+ - `sketchHelix(pitch, height, radius, center?, dir?, lefthand?)` — Helix curve (no PlaneConfig; uses center/dir directly)
282
+ - `sketchFaceOffset(face, offset)` — Offset a face boundary (negative = inward, positive = outward)
283
+ - `sketchParametricFunction(fn, planeConfig?, {pointsCount?, start?, stop?}?, approxConfig?)` — Parametric curve on plane
284
+ - `polysideInnerRadius(outerRadius, sidesCount, sagitta?)` — Helper: compute inner radius of a polyside
285
+
286
+ Sketch instance methods:
287
+ - `extrude(distance, options?)` — Options: `{extrusionDirection?, extrusionProfile?, twistAngle?, origin?}`
288
+ - `revolve(axis?, {origin?})` — Revolve around axis
289
+ - `loftWith(otherSketches, config?, returnShell?)` — Loft between profiles
290
+ - `sweepSketch(sketchOnPlane, config?)` — Sweep along a spine
291
+ - `face()` — Convert to face
292
+ - `wires()` — Get wire(s)
293
+ - `clone()`, `delete()` — Copy and cleanup
294
+
295
+ ### Drawing to 3D
296
+
297
+ Extrude a 2D rectangular sketch into a 3D solid:
298
+ ```typescript
299
+ import { drawRectangle, drawCircle, drawingCut, drawingToSketchOnPlane, shape } from 'brepjs';
300
+
301
+ // Simple rectangle → extrude to box
302
+ const rect = drawRectangle(50, 30);
303
+ const sketch = rect.sketchOnPlane('XY');
304
+ const solid = sketch.extrude(20); // 20mm height → Solid
305
+
306
+ // Complex profile: rectangle with circular cutout → extrude
307
+ const profile = drawingCut(drawRectangle(50, 30), drawCircle(8).translate([25, 15]));
308
+ const profileSketch = drawingToSketchOnPlane(profile, 'XY');
309
+ const complexSolid = shape(profileSketch.face()).extrude(20).val;
310
+
311
+ // Or use the sketchRectangle shortcut
312
+ import { sketchRectangle } from 'brepjs';
313
+ const quickBox = sketchRectangle(50, 30).extrude(20);
314
+ ```
315
+
316
+ Plane names: `'XY'`, `'XZ'`, `'YZ'`, `'ZX'`, `'YX'`, `'ZY'`, `'front'`, `'back'`, `'top'`, `'bottom'`, `'left'`, `'right'`
317
+
318
+ ## 3D Operations
319
+
320
+ ### Extrude & Revolve
321
+
322
+ ```typescript
323
+ // Extrude a sketch into a solid
324
+ const box = sketchRectangle(10, 10).extrude(20);
325
+
326
+ // Twist extrude
327
+ const twisted = sketchRectangle(10, 10).extrude(20, { twistAngle: 45 });
328
+
329
+ // Revolve around an axis (default direction: sketch's defaultDirection)
330
+ const sphere = sketchCircle(5, { plane: 'XZ' }).revolve();
331
+ const halfTorus = sketchCircle(2, { plane: 'XZ' }).revolve([10, 0, 0], { origin: [0, 0, 0] });
332
+ ```
333
+
334
+ Functional API:
335
+ - `extrude(face: OrientedFace, height: number | Vec3): Result<Solid>` — Extrude a face. Pass a number for Z-axis extrusion or a Vec3 for arbitrary direction (vector length = distance)
336
+ - `revolve(face: OrientedFace, { axis?, at?, angle? }?): Result<Shape3D>` — Revolve a face. `at` default `[0,0,0]`, `axis` default `[0,0,1]` (Z). **`angle` is in RADIANS** — a full turn is `Math.PI * 2`; if omitted it defaults to a HALF turn (`Math.PI`), so always pass it explicitly. (Note: pattern `fullAngle` is in degrees — brepjs is not uniform, so don't assume degrees here.)
337
+ - `sweep(wire, spine, config?, shellMode?): Result<Shape3D | [Shape3D, Wire, Wire]>` — Sweep profile along spine
338
+ - `complexExtrude(wire, center, normal, profileShape?, shellMode?): Result<Shape3D>` — Extrude with scaling profile
339
+ - `twistExtrude(wire, angleDeg, center, normal, profileShape?, shellMode?): Result<Shape3D>` — Twist extrude with rotation
340
+ - `supportExtrude(wire, center, normal, support): Result<Shape3D>` — Extrude constrained to a support surface
341
+
342
+ `ExtrusionProfile`: `{ profile?: 's-curve' | 'linear', endFactor?: number }` — Controls scaling along extrusion path. `endFactor` 1 = same size, 0.5 = half size at end.
343
+
344
+ `SweepOptions`: `{ frenet?, auxiliarySpine?, law?, transitionMode?: 'right' | 'transformed' | 'round', withContact?, support?, forceProfileSpineOthogonality? }`
345
+
346
+ ### Loft & Sweep
347
+
348
+ ```typescript
349
+ import { sketchCircle, sketchRectangle } from 'brepjs';
350
+
351
+ // Loft between profiles
352
+ const bottom = sketchCircle(10);
353
+ const top = sketchCircle(5, { plane: 'XY', origin: 20 }); // XY plane offset to Z=20
354
+ const lofted = bottom.loftWith([top]);
355
+
356
+ // Sweep a profile along a spine (sweepSketch takes a function that builds the profile)
357
+ const spine = sketchHelix(10, 50, 20);
358
+ const coil = spine.sweepSketch((plane, origin) =>
359
+ new Sketcher(plane).movePointerTo([-2, -2]).lineTo([2, -2]).lineTo([2, 2]).lineTo([-2, 2]).close()
360
+ );
361
+ ```
362
+
363
+ Functional API:
364
+ - `loft(wires, config?): Result<Shape3D>` — Loft config: `{ruled?: boolean (default true), startPoint?: PointInput, endPoint?: PointInput}`
365
+ - `sketchLoft(sketch, otherSketches, config?, returnShell?): Shape3D`
366
+ - `sketchSweep(sketch, sketchOnPlane, sweepConfig?): Shape3D`
367
+ - `sketchExtrude(sketch, height, config?): Shape3D` — Functional version of `sketch.extrude()`
368
+ - `sketchRevolve(sketch, axis?, {origin?}?): Shape3D` — Functional version of `sketch.revolve()`
369
+ - `sketchFace(sketch): Face` — Get the face from a closed sketch
370
+ - `sketchWires(sketch): Wire` — Get the wire from a sketch
371
+
372
+ CompoundSketch functions (for multi-contour profiles like text):
373
+ - `compoundSketchExtrude(sketch, height, config?): Shape3D`
374
+ - `compoundSketchRevolve(sketch, axis?, {origin?}?): Shape3D`
375
+ - `compoundSketchFace(sketch): Face`
376
+ - `compoundSketchLoft(sketch, other, loftConfig): Shape3D`
377
+
378
+ Drawing functional API:
379
+ - `drawingToSketchOnPlane(drawing, plane?, origin?): SketchInterface | Sketches`
380
+ - `drawingFuse(a, b): Drawing`
381
+ - `drawingCut(a, b): Drawing`
382
+ - `drawingIntersect(a, b): Drawing`
383
+ - `drawingFillet(drawing, radius, filter?): Drawing`
384
+ - `drawingChamfer(drawing, radius, filter?): Drawing`
385
+ - `translateDrawing(drawing, dx, dy): Drawing` or `translateDrawing(drawing, [dx, dy])`
386
+ - `rotateDrawing(drawing, angle, center?): Drawing`
387
+ - `scaleDrawing(drawing, factor, center?): Drawing`
388
+ - `mirrorDrawing(drawing, dir, origin?, mode?): Drawing`
389
+
390
+ ### Boolean Operations
391
+
392
+ ```typescript
393
+ import { fuse, cut, intersect, unwrap } from 'brepjs';
394
+
395
+ const myBox = sketchRectangle(20, 20).extrude(20);
396
+ const hole = sketchCircle(5).extrude(30);
397
+
398
+ const withHole = unwrap(cut(myBox, hole)); // Subtraction → Result<Shape3D>
399
+ const merged = unwrap(fuse(myBox, hole)); // Union → Result<Shape3D>
400
+ const common = unwrap(intersect(myBox, hole)); // Intersection → Result<Shape3D>
401
+ ```
402
+
403
+ Full boolean API:
404
+ ```typescript
405
+ import { fuse, cut, intersect, section, split, slice } from 'brepjs';
406
+
407
+ fuse(a, b, options?): Result<Shape3D>
408
+ cut(base, tool, options?): Result<Shape3D>
409
+ intersect(a: Shape3D, b: Shape3D, options?): Result<Shape3D>
410
+ section(shape, plane, {approximation?, planeSize?}?): Result<AnyShape>
411
+ split(shape, tools): Result<AnyShape>
412
+ slice(shape, planes, options?): Result<AnyShape[]>
413
+ ```
414
+
415
+ All boolean operations validate inputs before calling OCCT: null shapes return `VALIDATION` errors with code `NULL_SHAPE_INPUT` and a message identifying which operand was invalid.
416
+
417
+ Boolean options: `{ optimisation?: 'none' | 'commonFace' | 'sameFace', simplify?: boolean, strategy?: 'native' | 'pairwise', signal?: AbortSignal }`
418
+
419
+ Batch: `fuseAll(shapes, options?): Result<Shape3D>`, `cutAll(base, tools, options?): Result<Shape3D>`
420
+
421
+ ### Fillet & Chamfer
422
+
423
+ ```typescript
424
+ import { fillet, chamfer, chamferDistAngleShape, getEdges, edgeFinder } from 'brepjs';
425
+
426
+ // Fillet all edges — returns Result<Shape3D>
427
+ const rounded = unwrap(fillet(myBox, getEdges(myBox), 2));
428
+
429
+ // Fillet specific edges using edgeFinder
430
+ const selective = unwrap(fillet(myBox, edgeFinder().ofLength(20).findAll(myBox), 2));
431
+
432
+ // Chamfer
433
+ const chamfered = unwrap(chamfer(myBox, getEdges(myBox), 1));
434
+ ```
435
+
436
+ Additional functional API:
437
+ ```typescript
438
+ import { fillet, chamfer, chamferDistAngleShape } from 'brepjs';
439
+
440
+ fillet(shape, radius): Result<Shape3D> // All edges
441
+ fillet(shape, edges, radius): Result<Shape3D> // Selected edges
442
+ // edges: Edge[] | FinderFn<Edge> | ShapeFinder<Edge>
443
+ // radius: number | [r1, r2] | (edge => number | [r1, r2] | null)
444
+
445
+ chamfer(shape, distance): Result<Shape3D> // All edges
446
+ chamfer(shape, edges, distance): Result<Shape3D> // Selected edges
447
+ // distance: number | [d1, d2] | (edge => number | [d1, d2] | null)
448
+
449
+ chamferDistAngleShape(shape, edges, distance, angleDeg): Result<Shape3D>
450
+ ```
451
+
452
+ ### Shell (Hollow Out)
453
+
454
+ ```typescript
455
+ import { shell, faceFinder, unwrap } from 'brepjs';
456
+
457
+ // Remove top face and shell to 1mm thickness — returns Result<Shape3D>
458
+ const topFaces = faceFinder().parallelTo('XY').findAll(b);
459
+ const hollowed = unwrap(shell(b, topFaces, 1));
460
+ ```
461
+
462
+ `shell(shape, faces, thickness, {tolerance?}?): Result<Shape3D>`
463
+
464
+ All modifier operations (`fillet`, `chamfer`, `shell`, `offset`, `thicken`) validate that the input shape is not null before calling OCCT, returning `NULL_SHAPE_INPUT` validation errors. Error messages from OCCT failures include operation name and parameter metadata (edge count, radius, distance).
465
+
466
+ ### Offset & Thicken
467
+
468
+ ```typescript
469
+ import { offset, thicken } from 'brepjs';
470
+
471
+ offset(shape: Shape3D, distance, {tolerance?}?): Result<Shape3D>
472
+ thicken(shape: Face | Shell, thickness): Result<Solid>
473
+ ```
474
+
475
+ ### Transformations
476
+
477
+ All transforms return new shapes — the original is never modified.
478
+
479
+ ```typescript
480
+ import { translate, rotate, mirror, scale } from 'brepjs';
481
+
482
+ translate(shape, [10, 0, 0]): T
483
+ rotate(shape, angle, { at?, axis? }?): T
484
+ mirror(shape, { normal?, at? }?): T
485
+ scale(shape, factor, { center? }?): T
486
+ ```
487
+
488
+ Sequential transforms (functional API):
489
+ ```typescript
490
+ import { box, translate, rotate, scale } from 'brepjs';
491
+
492
+ const b = box(30, 20, 10);
493
+ const moved = translate(b, [50, 0, 0]); // translate first
494
+ const rotated = rotate(moved, 45, { axis: [0, 0, 1] }); // then rotate
495
+ const scaled = scale(rotated, 2); // then scale
496
+ // b is unchanged; each step produces a new shape
497
+ ```
498
+
499
+ Sequential transforms (wrapper API):
500
+ ```typescript
501
+ import { box, shape } from 'brepjs';
502
+
503
+ const result = shape(box(30, 20, 10))
504
+ .translate([50, 0, 0])
505
+ .rotate(45, { axis: [0, 0, 1] })
506
+ .scale(2)
507
+ .val;
508
+ ```
509
+
510
+ ### Patterns
511
+
512
+ ```typescript
513
+ import { linearPattern, circularPattern } from 'brepjs';
514
+
515
+ // 5 total copies along X with 10mm spacing (original + 4 copies, fused together)
516
+ const row = linearPattern(shape, [1, 0, 0], 5, 10);
517
+
518
+ // 8 copies in a full circle around Z axis (fused together)
519
+ const ring = circularPattern(shape, [0, 0, 1], 8);
520
+
521
+ // 6 copies in 180-degree arc around Z, centered at [10, 0, 0]
522
+ const arc = circularPattern(shape, [0, 0, 1], 6, 180, [10, 0, 0]);
523
+ ```
524
+
525
+ `linearPattern(shape, direction, count, spacing, options?)`: `count` includes the original. Returns `Result<Shape3D>` (all copies fused).
526
+
527
+ `circularPattern(shape, axis, count, fullAngle?, center?, options?)`: `fullAngle` default 360 degrees. `center` default `[0,0,0]`. Returns `Result<Shape3D>`.
528
+
529
+ `rectangularPattern(shape, { xDir, xCount, xSpacing, yDir, yCount, ySpacing })`: 2D grid pattern. Returns `Result<Shape3D>`.
530
+
531
+ ### Compound Operations
532
+
533
+ High-level operations that combine primitives with booleans:
534
+
535
+ ```typescript
536
+ import { drill, pocket, boss, mirrorJoin } from 'brepjs';
537
+
538
+ // Drill a hole into a shape
539
+ const drilled = unwrap(drill(myBox, { at: [10, 10], radius: 3, depth: 15 }));
540
+
541
+ // Cut a pocket (shaped recess)
542
+ const pocketed = unwrap(pocket(myBox, { profile: drawRectangle(20, 10), depth: 5 }));
543
+
544
+ // Add a boss (raised feature)
545
+ const bossed = unwrap(boss(myBox, { profile: drawCircle(8), height: 10 }));
546
+
547
+ // Mirror and fuse with the original
548
+ const symmetric = unwrap(mirrorJoin(halfShape, { normal: [1, 0, 0] }));
549
+ ```
550
+
551
+ ```typescript
552
+ drill(shape, { at, radius, depth?, axis? }): Result<Shape3D>
553
+ pocket(shape, { profile, face?, depth }): Result<Shape3D>
554
+ boss(shape, { profile, face?, height }): Result<Shape3D>
555
+ mirrorJoin(shape, { normal?, at? }?): Result<Shape3D>
556
+ ```
557
+
558
+ ### Matrix Transforms
559
+
560
+ ```typescript
561
+ import { applyMatrix, composeTransforms, transformCopy } from 'brepjs';
562
+
563
+ applyMatrix(shape, matrix): T // Apply a 4x4 matrix transform
564
+ composeTransforms(ops): ComposedTransform // Pre-compose multiple transforms
565
+ transformCopy(shape, composed): T // Apply composed transform (fast for repeated use)
566
+ ```
567
+
568
+ `TransformOp`: `{ type: 'translate', v: Vec3 } | { type: 'rotate', angle: number, axis?: Vec3, center?: Vec3 }`
569
+
570
+ ## Shape Queries
571
+
572
+ ### Shape Introspection
573
+
574
+ ```typescript
575
+ import { clone, describe, getBounds, isEmpty, isSameShape, isEqualShape, simplify, toBREP } from 'brepjs';
576
+
577
+ clone(shape): T // Deep clone
578
+ describe(shape): ShapeDescription // {kind, faceCount, edgeCount, wireCount, vertexCount, valid, bounds}
579
+ getBounds(shape): Bounds3D // {xMin, xMax, yMin, yMax, zMin, zMax}
580
+ isEmpty(shape): boolean // Check if shape is null/empty
581
+ isSameShape(a, b): boolean // Same topology reference
582
+ isEqualShape(a, b): boolean // Geometrically equal
583
+ simplify(shape): T
584
+ toBREP(shape): string // Serialize to BREP format
585
+ getHashCode(shape): number
586
+ ```
587
+
588
+ ### Topology Traversal
589
+
590
+ ```typescript
591
+ import { getEdges, getFaces, getWires, getVertices, vertexPosition } from 'brepjs';
592
+
593
+ getEdges(shape): Edge[]
594
+ getFaces(shape): Face[]
595
+ getWires(shape): Wire[]
596
+ getVertices(shape): Vertex[]
597
+ vertexPosition(vertex): Vec3
598
+
599
+ // Iterator versions (lazy)
600
+ iterEdges(shape): Generator<Edge>
601
+ iterFaces(shape): Generator<Face>
602
+ iterWires(shape): Generator<Wire>
603
+ iterVertices(shape): Generator<Vertex>
604
+ ```
605
+
606
+ ### Adjacency Queries
607
+
608
+ ```typescript
609
+ import { facesOfEdge, edgesOfFace, wiresOfFace, verticesOfEdge, adjacentFaces, sharedEdges } from 'brepjs';
610
+
611
+ facesOfEdge(parent, edge): Face[]
612
+ edgesOfFace(face): Edge[]
613
+ wiresOfFace(face): Wire[]
614
+ verticesOfEdge(edge): Vertex[]
615
+ adjacentFaces(parent, face): Face[]
616
+ sharedEdges(face1, face2): Edge[]
617
+ ```
618
+
619
+ ### Immutable Finders (Functional API)
620
+
621
+ ```typescript
622
+ import { edgeFinder, faceFinder, wireFinder, vertexFinder } from 'brepjs';
623
+
624
+ // Composable, immutable chain — findAll returns T[]
625
+ const topEdges = edgeFinder()
626
+ .inDirection([0, 0, 1])
627
+ .ofLength(10, tolerance?)
628
+ .ofCurveType('LINE')
629
+ .findAll(shape);
630
+
631
+ const topFaces = faceFinder()
632
+ .inDirection('Z') // Faces whose normal aligns with Z (shorthand for [0,0,1])
633
+ .ofSurfaceType('PLANE')
634
+ .ofArea(100, tolerance?)
635
+ .findAll(shape);
636
+ // faceFinder also has: .parallelTo(dir) (alias for inDirection(dir, 0)), .atDistance(dist, point?)
637
+
638
+ const closedWires = wireFinder()
639
+ .isClosed()
640
+ .ofEdgeCount(4)
641
+ .findAll(shape);
642
+
643
+ const cornerVerts = vertexFinder()
644
+ .atPosition([0, 0, 0], tolerance?)
645
+ .nearestTo([10, 0, 0])
646
+ .withinBox([0, 0, 0], [10, 10, 10])
647
+ .findAll(shape);
648
+
649
+ // findUnique — returns Result<T>, errors if 0 or >1 matches
650
+ const uniqueEdge = edgeFinder().ofLength(10).findUnique(shape);
651
+
652
+ // Combinators (available on all finders)
653
+ edgeFinder().not(f => f.ofCurveType('LINE')).findAll(shape);
654
+ edgeFinder().either([f => f.ofLength(10), f => f.ofLength(20)]).findAll(shape);
655
+ edgeFinder().when(edge => customPredicate(edge)).findAll(shape);
656
+ edgeFinder().inList(knownEdges).findAll(shape);
657
+ ```
658
+
659
+ ### cornerFinder (2D)
660
+
661
+ ```typescript
662
+ import { cornerFinder } from 'brepjs';
663
+
664
+ // Immutable builder pattern — each method returns a new finder
665
+ cornerFinder().inList(points).find(blueprint);
666
+ cornerFinder().atDistance(dist, point?).find(blueprint);
667
+ cornerFinder().atPoint(point).find(blueprint);
668
+ cornerFinder().inBox(corner1, corner2).find(blueprint);
669
+ cornerFinder().ofAngle(angle).find(blueprint);
670
+ cornerFinder().not(f => f.atPoint([0, 0])).find(blueprint);
671
+ cornerFinder().either([f => f.atPoint([0, 0]), f => f.atPoint([1, 1])]).find(blueprint);
672
+ cornerFinder().when(corner => customPredicate(corner)).find(blueprint);
673
+ ```
674
+
675
+ ## Curve Operations
676
+
677
+ ```typescript
678
+ import {
679
+ getCurveType, curveStartPoint, curveEndPoint,
680
+ curvePointAt, curveTangentAt, curveLength,
681
+ curveIsClosed, curveIsPeriodic, curvePeriod,
682
+ getOrientation, flipOrientation, offsetWire2D,
683
+ interpolateCurve, approximateCurve
684
+ } from 'brepjs';
685
+
686
+ getCurveType(edge): CurveType // 'LINE' | 'CIRCLE' | 'ELLIPSE' | 'BEZIER_CURVE' | 'BSPLINE_CURVE' | ...
687
+ curveStartPoint(shape): Vec3
688
+ curveEndPoint(shape): Vec3
689
+ curvePointAt(shape, t?): Vec3 // Point at parameter t (0-1)
690
+ curveTangentAt(shape, t?): Vec3 // Tangent at parameter t
691
+ curveLength(shape): number
692
+ curveIsClosed(shape): boolean
693
+ curveIsPeriodic(shape): boolean
694
+ curvePeriod(shape): number
695
+ getOrientation(shape): 'forward' | 'backward'
696
+ flipOrientation(shape): Edge | Wire
697
+
698
+ // Create curves from points
699
+ interpolateCurve(points, {periodic?, tolerance?}?): Result<Edge>
700
+ approximateCurve(points, {tolerance?, degMin?, degMax?, smoothing?}?): Result<Edge>
701
+
702
+ // 2D wire offset
703
+ offsetWire2D(wire, offset, kind?): Result<Wire> // kind: 'arc' | 'intersection' | 'tangent'
704
+ ```
705
+
706
+ ## Face Operations
707
+
708
+ ```typescript
709
+ import {
710
+ getSurfaceType, faceGeomType, faceOrientation, flipFaceOrientation,
711
+ uvBounds, pointOnSurface, uvCoordinates, normalAt,
712
+ faceCenter, classifyPointOnFace, outerWire, innerWires,
713
+ projectPointOnFace
714
+ } from 'brepjs';
715
+
716
+ getSurfaceType(face): Result<SurfaceType> // 'PLANE' | 'CYLINDRE' | 'CONE' | 'SPHERE' | 'TORUS' | 'BSPLINE_SURFACE' | ...
717
+ faceGeomType(face): SurfaceType
718
+ faceOrientation(face): 'forward' | 'backward'
719
+ flipFaceOrientation(face): Face
720
+ uvBounds(face): { uMin, uMax, vMin, vMax }
721
+ pointOnSurface(face, u, v): Vec3
722
+ uvCoordinates(face, point): [number, number]
723
+ normalAt(face, point?): Vec3
724
+ faceCenter(face): Vec3
725
+ classifyPointOnFace(face, point, tolerance?): 'in' | 'on' | 'out'
726
+ outerWire(face): Wire
727
+ innerWires(face): Wire[]
728
+ projectPointOnFace(face, point): Result<{ uv, point, distance }>
729
+ ```
730
+
731
+ ## Measurements
732
+
733
+ ```typescript
734
+ import {
735
+ measureVolume, measureArea, measureLength,
736
+ measureDistance
737
+ } from 'brepjs';
738
+
739
+ measureVolume(solid); // number
740
+ measureArea(face); // number
741
+ measureLength(edge); // number
742
+ measureDistance(shape1, shape2); // number
743
+ ```
744
+
745
+ Functional measurement API:
746
+ ```typescript
747
+ import {
748
+ measureVolume, measureArea, measureLength, measureDistance,
749
+ measureVolumeProps, measureSurfaceProps, measureLinearProps,
750
+ createDistanceQuery, measureCurvatureAt, measureCurvatureAtMid
751
+ } from 'brepjs';
752
+
753
+ measureVolumeProps(shape): { volume, mass, centerOfMass }
754
+ measureSurfaceProps(shape): { area, mass, centerOfMass }
755
+ measureLinearProps(shape): { length, mass, centerOfMass }
756
+ measureDistance(shape1, shape2): number
757
+ createDistanceQuery(ref): { distanceTo(other): number, dispose(): void }
758
+
759
+ // Surface curvature
760
+ measureCurvatureAt(face, u, v): CurvatureResult
761
+ measureCurvatureAtMid(face): CurvatureResult
762
+ // CurvatureResult: { mean, gaussian, maxCurvature, minCurvature, maxDirection, minDirection }
763
+ ```
764
+
765
+ All measurement functions throw on null shape input with descriptive messages (e.g. `"measureVolumeProps: shape is a null shape"`). Use `isEmpty()` to check before measuring if the shape may be null.
766
+
767
+ ### Interference Detection
768
+
769
+ ```typescript
770
+ import { checkInterference, checkAllInterferences } from 'brepjs';
771
+
772
+ checkInterference(shape1, shape2, tolerance?): Result<InterferenceResult>
773
+ // InterferenceResult: { hasInterference, minDistance, pointOnShape1, pointOnShape2 }
774
+
775
+ checkAllInterferences(shapes, tolerance?): InterferencePair[]
776
+ // InterferencePair: { i, j, result: InterferenceResult }
777
+ ```
778
+
779
+ `checkInterference` returns a `Result` error with `kind: 'VALIDATION'` and `code: 'NULL_SHAPE_INPUT'` if either shape is null. `checkAllInterferences` propagates via `unwrap` (throws on null).
780
+
781
+ ## Shape Healing & Validation
782
+
783
+ ```typescript
784
+ import { isValid, healSolid, healFace, healWire, heal, autoHeal } from 'brepjs';
785
+
786
+ isValid(shape): boolean
787
+ healSolid(solid): Result<Solid>
788
+ healFace(face): Result<Face>
789
+ healWire(wire, face?): Result<Wire>
790
+ heal(shape): Result<T>
791
+
792
+ // Auto-healing pipeline with diagnostics
793
+ autoHeal(shape, options?): Result<{ shape, report: HealingReport }>
794
+ // Options: { fixWires?: boolean (default true), fixFaces?: boolean (default true),
795
+ // fixSolids?: boolean (default true), sewTolerance?: number,
796
+ // fixSelfIntersection?: boolean (default FALSE — unlike the others) }
797
+ // HealingReport: { isValid, alreadyValid, wiresHealed, facesHealed, solidHealed, steps, diagnostics }
798
+ // alreadyValid: true when shape was valid before healing — distinguishes "nothing to fix" from "fix succeeded"
799
+ ```
800
+
801
+ ## Import / Export
802
+
803
+ ### STEP Files
804
+
805
+ End-to-end import → modify → export:
806
+ ```typescript
807
+ import { importSTEP, exportSTEP, shape, unwrap } from 'brepjs';
808
+ import { readFileSync, writeFileSync } from 'fs';
809
+
810
+ // Import a STEP file
811
+ const stepBytes = readFileSync('input.step');
812
+ const stepBlob = new Blob([stepBytes]);
813
+ const imported = unwrap(await importSTEP(stepBlob)); // Result<AnyShape>
814
+
815
+ // Modify the imported shape
816
+ const modified = shape(imported).fillet(2).translate([0, 0, 10]).val;
817
+
818
+ // Export back to STEP
819
+ const outputBlob = unwrap(exportSTEP(modified)); // Result<Blob>
820
+ writeFileSync('output.step', Buffer.from(await outputBlob.arrayBuffer()));
821
+ ```
822
+
823
+ Export assembly with colors and names:
824
+ ```typescript
825
+ import { exportAssemblySTEP, unwrap } from 'brepjs';
826
+
827
+ const stepBlob = unwrap(exportAssemblySTEP([
828
+ { shape: body, color: '#3366cc', name: 'Body' },
829
+ { shape: lid, color: '#cc6633', name: 'Lid' },
830
+ ], { unit: 'millimeter' }));
831
+ ```
832
+
833
+ Functional API:
834
+ ```typescript
835
+ import { importSTEP, exportSTEP, exportAssemblySTEP } from 'brepjs';
836
+
837
+ importSTEP(blob): Promise<Result<AnyShape>>
838
+ exportSTEP(shape): Result<Blob>
839
+ exportAssemblySTEP(shapes, { unit?, modelUnit? }?): Result<Blob>
840
+ // ShapeConfig: { shape, color?, alpha?, name? }
841
+ // SupportedUnit: 'millimeter' | 'centimeter' | 'meter' | 'inch' | 'foot'
842
+ ```
843
+
844
+ ### STL Files
845
+
846
+ ```typescript
847
+ import { importSTL, exportSTL } from 'brepjs';
848
+
849
+ importSTL(blob): Promise<Result<AnyShape>>
850
+ exportSTL(shape, { tolerance?, angularTolerance?, binary? }?): Result<Blob>
851
+ ```
852
+
853
+ ### IGES Files
854
+
855
+ ```typescript
856
+ import { importIGES, exportIGES } from 'brepjs';
857
+
858
+ importIGES(blob): Promise<Result<AnyShape>>
859
+ exportIGES(shape): Result<Blob>
860
+ ```
861
+
862
+ ### glTF / GLB (with PBR materials)
863
+
864
+ ```typescript
865
+ import { exportGltf, exportGlb } from 'brepjs';
866
+
867
+ const json = exportGltf(mesh, options?); // glTF JSON string
868
+ const binary = exportGlb(mesh, options?); // GLB ArrayBuffer
869
+
870
+ // GltfExportOptions: { materials?: Map<faceId, GltfMaterial> }
871
+ // GltfMaterial: { name?, baseColor?: [r,g,b,a], metallic?: number, roughness?: number }
872
+ ```
873
+
874
+ ### DXF
875
+
876
+ ```typescript
877
+ import { importDXF, exportDXF, blueprintToDXF } from 'brepjs';
878
+
879
+ importDXF(blob, options?): Promise<Result<Wire[]>>
880
+ // DXFImportOptions: { layer?: string }
881
+
882
+ exportDXF(entities, options?): string
883
+ blueprintToDXF(drawing, options?): string
884
+ // DXFExportOptions: { layer?, curveSegments? }
885
+ // DXFEntity: { type: 'LINE', start, end, layer? } | { type: 'POLYLINE', points, closed?, layer? }
886
+ ```
887
+
888
+ ### 3MF
889
+
890
+ ```typescript
891
+ import { importThreeMF, exportThreeMF } from 'brepjs';
892
+
893
+ importThreeMF(blob): Promise<Result<AnyShape>>
894
+ exportThreeMF(mesh, options?): ArrayBuffer
895
+ // ThreeMFExportOptions: { name?, unit?: 'micron' | 'millimeter' | 'centimeter' | 'meter' | 'inch' | 'foot' }
896
+ ```
897
+
898
+ ### OBJ
899
+
900
+ ```typescript
901
+ import { importOBJ, exportOBJ } from 'brepjs';
902
+
903
+ importOBJ(blob): Promise<Result<AnyShape>>
904
+ exportOBJ(mesh): string
905
+ ```
906
+
907
+ ### SVG Import
908
+
909
+ ```typescript
910
+ import { importSVGPathD, importSVG } from 'brepjs';
911
+
912
+ importSVGPathD(pathD): Result<Blueprint> // Single SVG path d attribute
913
+ importSVG(svgString): Result<Blueprint[]> // Extract all <path> elements from SVG string
914
+ // SVGImportOptions type is exported: { flipY?: boolean } (Y-axis is flipped by default since SVG Y is down)
915
+ ```
916
+
917
+ ## Topology Helpers
918
+
919
+ Create shapes directly without the sketching API:
920
+
921
+ ```typescript
922
+ import {
923
+ line, circle, ellipse, helix,
924
+ threePointArc, ellipseArc, tangentArc,
925
+ bsplineApprox, bezier,
926
+ wire, face, filledFace, subFace,
927
+ addHoles, polygon,
928
+ cylinder, sphere, cone, torus, ellipsoid,
929
+ box, vertex, offsetFace, makeBaseBox,
930
+ compound, sewShells, solid
931
+ } from 'brepjs';
932
+
933
+ // 1D shapes (edges & wires)
934
+ line(from, to): Edge
935
+ circle(radius, { at?, normal? }?): Edge
936
+ ellipse(major, minor, { at?, normal?, xDir? }?): Result<Edge>
937
+ helix(pitch, height, radius, { at?, axis?, lefthand? }?): Wire
938
+ threePointArc(v1, v2, v3): Edge
939
+ ellipseArc(major, minor, startAngleDeg, endAngleDeg, { at?, normal?, xDir? }?): Result<Edge>
940
+ tangentArc(startPoint, startTangent, endPoint): Edge
941
+ bsplineApprox(points, config?): Result<Edge>
942
+ bezier(points): Result<Edge>
943
+ wire(edgesOrWires): Result<Wire>
944
+ wireLoop(edgesOrWires): Result<ClosedWire> // Assemble + verify closure
945
+
946
+ // 2D faces (require ClosedWire, return OrientedFace)
947
+ face(wire: ClosedWire, holes?: ClosedWire[]): Result<OrientedFace>
948
+ filledFace(wire: ClosedWire): Result<OrientedFace>
949
+ subFace(originFace, wire: ClosedWire): OrientedFace
950
+ addHoles(face, holes: ClosedWire[]): OrientedFace
951
+ polygon(points): Result<OrientedFace>
952
+
953
+ // 3D solids (return ValidSolid)
954
+ box(width, depth, height, { at?, centered? }?): ValidSolid
955
+ sphere(radius, { at? }?): ValidSolid
956
+ cylinder(radius, height, { at?, axis?, centered? }?): ValidSolid
957
+ cone(bottomRadius, topRadius, height, { at?, axis?, centered? }?): ValidSolid
958
+ torus(majorRadius, minorRadius, { at?, axis? }?): ValidSolid
959
+ ellipsoid(rx, ry, rz, { at? }?): ValidSolid
960
+ makeBaseBox(x, y, z): Shape3D
961
+ solid(facesOrShells): Result<ValidSolid>
962
+
963
+ // Vertex
964
+ vertex(point): Vertex
965
+
966
+ // Compound & shell
967
+ compound(shapes): Compound
968
+ sewShells(facesOrShells, ignoreType?): Result<Shell>
969
+ offsetFace(face, offset, tolerance?): Result<Shape3D>
970
+ ```
971
+
972
+ ## Constructive Geometry
973
+
974
+ ```typescript
975
+ import { hull, minkowski, polyhedron, surfaceFromGrid, surfaceFromImage, roof } from 'brepjs';
976
+
977
+ hull(shapes, options?): Result<Solid> // Convex hull of shapes
978
+ minkowski(shape, tool, options?): Result<Solid> // Minkowski sum
979
+ polyhedron(points, faces, options?): Result<Solid> // From vertices + face indices
980
+ surfaceFromGrid(heights, options?): Result<AnyShape> // Height-map surface
981
+ surfaceFromImage(blob, options?): Promise<Result<AnyShape>> // Image-based surface
982
+ roof(wire, { angle? }?): Result<Solid> // Roof from closed wire outline
983
+ ```
984
+
985
+ `SurfaceFromGridOptions`: `{ width?, depth?, scaleZ? }`
986
+ `SurfaceFromImageOptions`: extends grid options + `{ channel?: 'r' | 'g' | 'b' | 'luminance', downsample? }`
987
+
988
+ ### Advanced Sweeps
989
+
990
+ ```typescript
991
+ import { multiSectionSweep, guidedSweep } from 'brepjs';
992
+
993
+ multiSectionSweep(sections, spine, options?): Result<Solid | Shell>
994
+ // sections: { wire: Wire, location?: number }[]
995
+ // MultiSweepOptions: { solid?, ruled?, tolerance? }
996
+
997
+ guidedSweep(profile, spine, guides, options?): Result<Solid | Shell>
998
+ // GuidedSweepOptions: { transition?: 'transformed' | 'round' | 'right', solid?, tolerance? }
999
+ ```
1000
+
1001
+ ## Shape Coloring & Tagging
1002
+
1003
+ ```typescript
1004
+ import { colorFaces, colorShape, getFaceColor, getShapeColor } from 'brepjs';
1005
+
1006
+ colorFaces(shape, faces, color): T // Color specific faces
1007
+ colorShape(shape, color): T // Color entire shape
1008
+ getFaceColor(shape, face): Color | undefined // Get face color
1009
+ getShapeColor(shape): Color | undefined // Get shape color
1010
+ // ColorInput: string ('#ff0000') | [r, g, b] | [r, g, b, a]
1011
+ ```
1012
+
1013
+ ```typescript
1014
+ import { tagFaces, findFacesByTag, getFaceTags, setTagMetadata, getTagMetadata } from 'brepjs';
1015
+
1016
+ tagFaces(shape, selector, tag): AnyShape // Tag faces by array or predicate
1017
+ findFacesByTag(shape, tag): Face[] // Find faces by tag name
1018
+ getFaceTags(shape): Map<string, Face[]> // Get all tags
1019
+ setTagMetadata(shape, tag, metadata): AnyShape // Attach metadata to a tag
1020
+ getTagMetadata(shape, tag): Record<string, unknown> | undefined
1021
+ ```
1022
+
1023
+ ## 2D Blueprints & Curves
1024
+
1025
+ The `Drawing` class supports 2D boolean operations and transformations:
1026
+
1027
+ ```typescript
1028
+ const plate = drawRectangle(100, 50);
1029
+ const hole = drawCircle(10).translate(20, 0); // Drawing.translate is still available
1030
+ const withHole = plate.cut(hole); // Drawing.cut is still available
1031
+ const filleted = withHole.fillet(3); // Drawing.fillet is still available
1032
+ const svg = filleted.toSVG();
1033
+ ```
1034
+
1035
+ Functional Blueprint API:
1036
+ ```typescript
1037
+ import {
1038
+ createBlueprint, getBounds2D, getOrientation2D,
1039
+ translate2D, rotate2D, scale2D, mirror2D,
1040
+ stretch2D, toSVGPathD, isInside2D,
1041
+ sketchOnPlane2D, sketchOnFace2D
1042
+ } from 'brepjs';
1043
+
1044
+ createBlueprint(curves): Blueprint
1045
+ getBounds2D(bp): BoundingBox2d
1046
+ getOrientation2D(bp): 'clockwise' | 'counterClockwise'
1047
+ translate2D(bp, dx, dy): Blueprint
1048
+ rotate2D(bp, angle, center?): Blueprint
1049
+ scale2D(bp, factor, center?): Blueprint
1050
+ mirror2D(bp, dir, origin?, mode?): Blueprint
1051
+ stretch2D(bp, ratio, direction, origin?): Blueprint
1052
+ toSVGPathD(bp): string
1053
+ isInside2D(bp, point): boolean
1054
+ sketchOnPlane2D(bp, plane?, origin?): Sketch
1055
+ sketchOnFace2D(bp, face, scaleMode?): Sketch
1056
+ ```
1057
+
1058
+ Blueprint construction helpers:
1059
+ ```typescript
1060
+ import { polysidesBlueprint, roundedRectangleBlueprint, organiseBlueprints } from 'brepjs';
1061
+
1062
+ polysidesBlueprint(radius, sidesCount, sagitta?): Blueprint // Regular polygon as a Blueprint (lower-level than drawPolysides)
1063
+ roundedRectangleBlueprint(width, height, r?): Blueprint // Rounded rect as Blueprint. r: number | {rx?, ry?}
1064
+ organiseBlueprints(blueprints: Blueprint[]): Blueprints // Group flat blueprints into compound blueprints with hole detection
1065
+ ```
1066
+
1067
+ Low-level Blueprint boolean operations (single blueprint → single blueprint):
1068
+ ```typescript
1069
+ import { fuseBlueprints, cutBlueprints, intersectBlueprints } from 'brepjs';
1070
+
1071
+ fuseBlueprints(first: Blueprint, second: Blueprint): null | Blueprint | Blueprints
1072
+ cutBlueprints(first: Blueprint, second: Blueprint): null | Blueprint | Blueprints
1073
+ intersectBlueprints(first: Blueprint, second: Blueprint): null | Blueprint | Blueprints
1074
+ ```
1075
+
1076
+ 2D Boolean (functional):
1077
+ ```typescript
1078
+ import { fuse2D, cut2D, intersect2D } from 'brepjs';
1079
+
1080
+ fuse2D(first, second): Shape2D
1081
+ cut2D(first, second): Shape2D
1082
+ intersect2D(first, second): Shape2D
1083
+ // Shape2D = Blueprint | Blueprints | CompoundBlueprint | null
1084
+ ```
1085
+
1086
+ 2D Curve functions:
1087
+ ```typescript
1088
+ import {
1089
+ reverseCurve, curve2dBoundingBox, curve2dFirstPoint, curve2dLastPoint,
1090
+ curve2dSplitAt, curve2dParameter, curve2dTangentAt, curve2dIsOnCurve, curve2dDistanceFrom
1091
+ } from 'brepjs';
1092
+ ```
1093
+
1094
+ ## Projection & Camera
1095
+
1096
+ Project 3D shapes to 2D for technical drawings:
1097
+
1098
+ ```typescript
1099
+ import { drawProjection, createCamera, cameraLookAt, unwrap } from 'brepjs';
1100
+
1101
+ // Quick projection from a named plane
1102
+ const { visible, hidden } = drawProjection(shape, 'front');
1103
+ const svg = visible.toSVG();
1104
+
1105
+ // Custom camera
1106
+ const camera = unwrap(createCamera([100, 100, 100], [0, 0, -1]));
1107
+ const lookingAt = unwrap(cameraLookAt(camera, [0, 0, 0]));
1108
+ const projected = drawProjection(shape, lookingAt);
1109
+ ```
1110
+
1111
+ Camera API:
1112
+ ```typescript
1113
+ import { createCamera, cameraLookAt, cameraFromPlane, projectEdges } from 'brepjs';
1114
+
1115
+ createCamera(position?, direction?, xAxis?): Result<Camera>
1116
+ cameraLookAt(camera, target): Result<Camera>
1117
+ cameraFromPlane(planeName): Result<Camera>
1118
+ projectEdges(shape, camera, withHiddenLines?): { visible: Edge[], hidden: Edge[] }
1119
+ ```
1120
+
1121
+ Additional projection helpers:
1122
+ ```typescript
1123
+ import { isProjectionPlane, makeProjectedEdges } from 'brepjs';
1124
+
1125
+ isProjectionPlane(plane): plane is ProjectionPlane // Type guard for ProjectionPlane strings
1126
+ makeProjectedEdges(shape, camera, withHiddenLines?): { visible: Edge[], hidden: Edge[] } // HLR projection
1127
+ ```
1128
+
1129
+ ## Text
1130
+
1131
+ Render text as 2D outlines (requires font loading):
1132
+
1133
+ ```typescript
1134
+ import { loadFont, getFont, drawText, sketchText, textBlueprints } from 'brepjs';
1135
+
1136
+ await loadFont('/fonts/Roboto-Regular.ttf', 'Roboto');
1137
+
1138
+ // 2D text drawing
1139
+ const text2d = drawText('Hello', { fontSize: 20, fontFamily: 'Roboto' });
1140
+
1141
+ // 3D text (sketch on plane, ready to extrude)
1142
+ const text3d = sketchText('Hello', { fontSize: 20, fontFamily: 'Roboto' }, { plane: 'XY' });
1143
+
1144
+ // Get raw blueprints
1145
+ const bps = textBlueprints('Hello', { fontSize: 20, fontFamily: 'Roboto' });
1146
+ ```
1147
+
1148
+ ## Assembly Tree
1149
+
1150
+ Build hierarchical assemblies with transforms:
1151
+
1152
+ ```typescript
1153
+ import {
1154
+ createAssemblyNode, addChild, removeChild, updateNode,
1155
+ findNode, walkAssembly, countNodes, collectShapes
1156
+ } from 'brepjs';
1157
+
1158
+ const root = createAssemblyNode('Root');
1159
+ const part = createAssemblyNode('Part', {
1160
+ shape: myShape,
1161
+ translate: [10, 0, 0],
1162
+ rotate: { angle: 45, axis: [0, 0, 1] },
1163
+ metadata: { material: 'steel' }
1164
+ });
1165
+
1166
+ const assembly = addChild(root, part);
1167
+ const updated = updateNode(assembly, { translate: [20, 0, 0] });
1168
+ const found = findNode(assembly, 'Part');
1169
+ walkAssembly(assembly, (node, depth) => console.log(node.name, depth));
1170
+ const count = countNodes(assembly);
1171
+ const shapes = collectShapes(assembly);
1172
+ const pruned = removeChild(assembly, 'Part');
1173
+ ```
1174
+
1175
+ ### Assembly Mates (Constraints)
1176
+
1177
+ ```typescript
1178
+ import { addMate, solveAssembly } from 'brepjs';
1179
+
1180
+ // Add a constraint between parts
1181
+ const constrained = addMate(assembly, {
1182
+ type: 'coincident',
1183
+ entityA: { node: 'Lid', face: topFace },
1184
+ entityB: { node: 'Body', face: bottomFace },
1185
+ });
1186
+
1187
+ // Solve constraints to compute transforms
1188
+ const solved = unwrap(solveAssembly(constrained));
1189
+ // solved: { transforms: Map<string, { position, rotation }>, dof, converged }
1190
+ ```
1191
+
1192
+ Mate types: `'coincident'`, `'concentric'`, `'distance'`, `'angle'`, `'fixed'`
1193
+
1194
+ ## Parametric History
1195
+
1196
+ Track modeling operations for undo/replay:
1197
+
1198
+ ```typescript
1199
+ import {
1200
+ createHistory, addStep, undoLast, findStep,
1201
+ getHistoryShape, stepCount, stepsFrom,
1202
+ registerShape, createRegistry, registerOperation,
1203
+ replayHistory, replayFrom, modifyStep
1204
+ } from 'brepjs';
1205
+
1206
+ // Create history and register operations
1207
+ let history = createHistory();
1208
+ let registry = createRegistry();
1209
+ registry = registerOperation(registry, 'extrude', (inputs, params) => {
1210
+ return sketchRectangle(params.w, params.h).extrude(params.depth);
1211
+ });
1212
+
1213
+ // Add steps
1214
+ history = registerShape(history, 'base', baseShape);
1215
+ history = addStep(history, {
1216
+ id: 'step1', type: 'extrude',
1217
+ parameters: { w: 10, h: 10, depth: 20 },
1218
+ inputIds: ['base'], outputId: 'extruded'
1219
+ }, resultShape);
1220
+
1221
+ // Undo, replay, modify
1222
+ history = undoLast(history);
1223
+ const replayed = replayHistory(history, registry);
1224
+ const modified = modifyStep(history, 'step1', { depth: 30 }, registry);
1225
+ ```
1226
+
1227
+ ## Meshing
1228
+
1229
+ Convert shapes to triangle meshes for rendering:
1230
+
1231
+ ```typescript
1232
+ import { mesh, meshEdges } from 'brepjs';
1233
+
1234
+ const m = mesh(shape, { tolerance: 0.5, angularTolerance: 20, includeUVs: true });
1235
+ // m.vertices: Float32Array (flat xyz)
1236
+ // m.triangles: Uint32Array (triangle indices)
1237
+ // m.normals: Float32Array (flat normals)
1238
+ // m.uvs: Float32Array (UV coordinates when includeUVs: true)
1239
+ // m.faceGroups: {start, count, faceId}[]
1240
+
1241
+ const edgeMesh = meshEdges(shape, { tolerance: 0.5 });
1242
+ // edgeMesh.lines: number[]
1243
+ // edgeMesh.edgeGroups: {start, count, edgeId}[]
1244
+
1245
+ // Full mesh options (with defaults):
1246
+ // mesh(shape, {
1247
+ // tolerance: 1e-3, // linear deflection
1248
+ // angularTolerance: 0.1, // angular deflection (radians)
1249
+ // skipNormals: false, // omit normals from output
1250
+ // includeUVs: false, // include UV coordinates per-vertex
1251
+ // cache: true, // cache results (WeakMap by shape)
1252
+ // signal?: AbortSignal, // abort between face iterations
1253
+ // })
1254
+ ```
1255
+
1256
+ Mesh caching:
1257
+ ```typescript
1258
+ import { clearMeshCache, createMeshCache } from 'brepjs';
1259
+
1260
+ clearMeshCache(); // Clear global cache
1261
+ const cache = createMeshCache(); // Create isolated cache
1262
+ ```
1263
+
1264
+ ### Three.js Integration
1265
+
1266
+ ```typescript
1267
+ import { toBufferGeometryData, toLineGeometryData, toGroupedBufferGeometryData } from 'brepjs';
1268
+
1269
+ const bufferData = toBufferGeometryData(mesh);
1270
+ // { position: Float32Array, normal: Float32Array, index: Uint32Array }
1271
+
1272
+ const lineData = toLineGeometryData(edgeMesh);
1273
+ // { position: Float32Array }
1274
+
1275
+ const grouped = toGroupedBufferGeometryData(mesh);
1276
+ // extends bufferData with: groups: [{start, count, materialIndex, faceId}]
1277
+ ```
1278
+
1279
+ ## Memory Management
1280
+
1281
+ OCCT objects are allocated in WASM memory and are **not** garbage-collected by the JavaScript engine. You must explicitly clean them up. brepjs provides four cleanup patterns:
1282
+
1283
+ ### Pattern 1: `using` keyword (recommended, TS 5.9+)
1284
+
1285
+ The `using` declaration automatically disposes shapes when they go out of scope via TC39 `Symbol.dispose`:
1286
+
1287
+ ```typescript
1288
+ import { box, cylinder, cut, unwrap } from 'brepjs';
1289
+
1290
+ {
1291
+ using b = box(10, 10, 10);
1292
+ using hole = cylinder(3, 15);
1293
+ const result = unwrap(cut(b, hole));
1294
+ // b and hole are automatically freed at block end
1295
+ // result survives because it was not declared with `using`
1296
+ }
1297
+
1298
+ // Works in loops too
1299
+ for (let i = 0; i < 100; i++) {
1300
+ using temp = box(1, 1, 1);
1301
+ processBox(temp);
1302
+ // temp freed each iteration — no memory leak
1303
+ }
1304
+ ```
1305
+
1306
+ Requires TypeScript 5.9+ with `"lib": ["ES2022", "ESNext.Disposable"]` and Node.js 20+ or modern browsers.
1307
+
1308
+ ### Pattern 2: `DisposalScope` (deterministic, multiple temporaries)
1309
+
1310
+ ```typescript
1311
+ import { DisposalScope, box, cylinder, cut, unwrap } from 'brepjs';
1312
+
1313
+ function buildPart() {
1314
+ using scope = new DisposalScope();
1315
+ const b = scope.register(box(10, 10, 10)); // register for cleanup
1316
+ const hole = scope.register(cylinder(3, 15)); // register for cleanup
1317
+ return unwrap(cut(b, hole)); // result escapes; b and hole freed when scope exits
1318
+ }
1319
+ ```
1320
+
1321
+ ### Pattern 3: `withScope()` (scoped, returns result)
1322
+
1323
+ ```typescript
1324
+ import { withScope, box, cylinder, cut, unwrap } from 'brepjs';
1325
+
1326
+ const result = withScope((scope) => {
1327
+ const b = scope.register(box(10, 10, 10));
1328
+ const hole = scope.register(cylinder(3, 15));
1329
+ return unwrap(cut(b, hole)); // returned value survives the scope
1330
+ });
1331
+ ```
1332
+
1333
+ `withScopeResult(fn)` and `withScopeResultAsync(fn)` are variants that accept functions returning `Result<T>` — useful inside operations that already use Result-based error handling:
1334
+ ```typescript
1335
+ import { withScopeResult } from 'brepjs';
1336
+
1337
+ const result: Result<Solid> = withScopeResult((scope) => {
1338
+ const temp = scope.register(cylinder(5, 20));
1339
+ return cut(myBox, temp); // returns Result directly
1340
+ });
1341
+ ```
1342
+
1343
+ ### Low-level handles
1344
+
1345
+ ```typescript
1346
+ import { createHandle, createKernelHandle } from 'brepjs';
1347
+
1348
+ const handle = createHandle(ocShape); // ShapeHandle: { wrapped, disposed, [Symbol.dispose] }
1349
+ const kernelHandle = createKernelHandle(ocObj); // KernelHandle<T>: { value, disposed, [Symbol.dispose] }
1350
+ ```
1351
+
1352
+ `FinalizationRegistry` provides a safety net for missed cleanup, but relying on it is not recommended because GC timing is unpredictable. Always use one of the explicit patterns above.
1353
+
1354
+ ## Error Handling
1355
+
1356
+ Many operations return `Result<T, BrepError>`:
1357
+
1358
+ ```typescript
1359
+ import { ok, err, OK, isOk, isErr, unwrap, unwrapOr, unwrapOrElse, match, map, andThen, collect, tryCatch, pipeline } from 'brepjs';
1360
+
1361
+ // Construction
1362
+ ok(value): Ok<T>
1363
+ err(error): Err<E>
1364
+ OK // Pre-built Ok<undefined> for void success
1365
+
1366
+ // Type guards
1367
+ isOk(result): result is Ok<T>
1368
+ isErr(result): result is Err<E>
1369
+
1370
+ // Extraction
1371
+ unwrap(result): T // throws on Err
1372
+ unwrapOr(result, defaultValue): T
1373
+ unwrapOrElse(result, fn): T
1374
+ unwrapErr(result): E // throws on Ok
1375
+
1376
+ // Combinators
1377
+ map(result, fn): Result<U, E>
1378
+ mapErr(result, fn): Result<T, F>
1379
+ andThen(result, fn): Result<U, E> // flatMap alias
1380
+ collect(results): Result<T[], E> // All-or-nothing
1381
+
1382
+ // Pattern matching
1383
+ match(result, { ok: fn, err: fn }): U
1384
+
1385
+ // Try-catch boundary
1386
+ tryCatch(fn, mapError): Result<T, E>
1387
+ tryCatchAsync(fn, mapError): Promise<Result<T, E>>
1388
+
1389
+ // Pipeline
1390
+ pipeline(input).then(fn).then(fn).result // Chain Result operations
1391
+ ```
1392
+
1393
+ BrepError structure:
1394
+ ```typescript
1395
+ interface BrepError {
1396
+ kind: BrepErrorKind; // 'KERNEL_OPERATION' | 'VALIDATION' | 'TYPE_CAST' | 'SKETCHER_STATE' | 'MODULE_INIT' | 'COMPUTATION' | 'IO' | 'QUERY'
1397
+ code: string; // e.g. 'FUSE_FAILED', 'STEP_IMPORT_FAILED'
1398
+ message: string;
1399
+ cause?: unknown;
1400
+ metadata?: Record<string, unknown>;
1401
+ }
1402
+ ```
1403
+
1404
+ Error constructors: `kernelError(code, msg, cause?, meta?)`, `validationError(...)`, `typeCastError(...)`, `ioError(...)`, `computationError(...)`, `queryError(...)`, `sketcherStateError(...)`, `moduleInitError(...)`
1405
+
1406
+ ## Worker Protocol
1407
+
1408
+ Off-main-thread CAD operations:
1409
+
1410
+ ```typescript
1411
+ import {
1412
+ createWorkerClient, createOperationRegistry, registerHandler, createWorkerHandler,
1413
+ createTaskQueue, enqueueTask, dequeueTask, pendingCount, isQueueEmpty, rejectAll,
1414
+ isInitRequest, isOperationRequest, isDisposeRequest, isSuccessResponse, isErrorResponse
1415
+ } from 'brepjs';
1416
+
1417
+ // Client side
1418
+ const client = createWorkerClient({ worker: myWorker, wasmUrl: '/wasm/oc.wasm' });
1419
+ await client.init();
1420
+ const result = await client.execute('fuse', [shape1Brep, shape2Brep], {});
1421
+ client.dispose();
1422
+
1423
+ // Worker side
1424
+ let registry = createOperationRegistry();
1425
+ registry = registerHandler(registry, 'fuse', (shapesBrep, params) => {
1426
+ // shapesBrep: ReadonlyArray<string> — BREP-serialized input shapes
1427
+ // params: Readonly<Record<string, unknown>> — operation parameters
1428
+ // Must return { resultBrep?: string, resultData?: unknown }
1429
+ return { resultBrep: outputBrep };
1430
+ });
1431
+ createWorkerHandler(registry, async (wasmUrl) => { /* init WASM */ });
1432
+ ```
1433
+
1434
+ ## Vec3 Math Utilities
1435
+
1436
+ ```typescript
1437
+ import {
1438
+ vecAdd, vecSub, vecScale, vecNegate,
1439
+ vecDot, vecCross, vecLength, vecLengthSq, vecDistance,
1440
+ vecNormalize, vecEquals, vecIsZero,
1441
+ vecAngle, vecProjectToPlane, vecRotate, vecRepr
1442
+ } from 'brepjs';
1443
+
1444
+ vecAdd([1,0,0], [0,1,0]): Vec3 // [1, 1, 0]
1445
+ vecSub(a, b): Vec3
1446
+ vecScale(v, scalar): Vec3
1447
+ vecNegate(v): Vec3
1448
+ vecDot(a, b): number
1449
+ vecCross(a, b): Vec3
1450
+ vecLength(v): number
1451
+ vecLengthSq(v): number
1452
+ vecDistance(a, b): number
1453
+ vecNormalize(v): Vec3
1454
+ vecEquals(a, b, tolerance?): boolean
1455
+ vecIsZero(v, tolerance?): boolean
1456
+ vecAngle(a, b): number // radians
1457
+ vecProjectToPlane(v, origin, normal): Vec3
1458
+ vecRotate(v, axis, angleRad): Vec3
1459
+ vecRepr(v): string // e.g. "(1.00, 2.00, 3.00)"
1460
+ ```
1461
+
1462
+ ## Plane Operations
1463
+
1464
+ ```typescript
1465
+ import { createPlane, createNamedPlane, resolvePlane, translatePlane, pivotPlane, makePlane } from 'brepjs';
1466
+
1467
+ createPlane(origin, xDirection?, normal?): Plane
1468
+ createNamedPlane(name, origin?): Result<Plane>
1469
+ resolvePlane(input, origin?): Plane // PlaneName | Plane → Plane
1470
+ translatePlane(plane, offset): Plane
1471
+ pivotPlane(plane, angleDeg, axis?): Plane
1472
+ makePlane(plane?, origin?): Plane // From PlaneName + origin
1473
+ ```
1474
+
1475
+ ## Branded Shape Types (Functional API)
1476
+
1477
+ ```typescript
1478
+ import {
1479
+ castShape, getShapeKind,
1480
+ createVertex, createEdge, createWire, createFace, createShell, createSolid, createCompound,
1481
+ isVertex, isEdge, isWire, isFace, isShell, isSolid, isCompound, isShape3D, isShape1D,
1482
+ is3D, is2D,
1483
+ // Validity constructors and guards
1484
+ closedWire, orientedFace, manifoldShell, validSolid,
1485
+ isClosedWire, isOrientedFace, isManifoldShell, isValidSolid,
1486
+ } from 'brepjs';
1487
+
1488
+ castShape(ocShape): AnyShape // Auto-detect and wrap
1489
+ getShapeKind(shape): ShapeKind // 'vertex' | 'edge' | ... | 'compound'
1490
+ isVertex(s): s is Vertex // Type guards for shape kinds
1491
+ is3D(s): s is AnyShape<'3D'> // Dimension guards
1492
+ is2D(s): s is AnyShape<'2D'>
1493
+ closedWire(w): Result<ClosedWire, string> // Validity smart constructors
1494
+ orientedFace(f): Result<OrientedFace, string>
1495
+ isClosedWire(w): w is ClosedWire // Validity type guards
1496
+ isOrientedFace(f): f is OrientedFace
1497
+ isManifoldShell(s): s is ManifoldShell
1498
+ isValidSolid(s): s is ValidSolid
1499
+ ```
1500
+
1501
+ ## Kernel Boundary Conversions
1502
+
1503
+ Low-level helpers for interop with raw kernel objects:
1504
+
1505
+ ```typescript
1506
+ import { toKernelVec, fromKernelVec, fromKernelPnt, fromKernelDir, withKernelVec, withKernelPnt, withKernelDir } from 'brepjs';
1507
+
1508
+ toKernelVec(v): gp_Vec // Caller must delete()
1509
+ fromKernelVec(ocVec): Vec3 // Extract tuple from gp_Vec
1510
+ fromKernelPnt(ocPnt): Vec3 // Extract tuple from gp_Pnt
1511
+ fromKernelDir(ocDir): Vec3 // Extract tuple from gp_Dir
1512
+
1513
+ // Scoped (auto-cleanup)
1514
+ withKernelVec(v, fn): T // fn receives gp_Vec, deleted after
1515
+ withKernelPnt(v, fn): T
1516
+ withKernelDir(v, fn): T
1517
+ ```
1518
+
1519
+ ## Constants
1520
+
1521
+ - `DEG2RAD` — Multiply degrees to get radians
1522
+ - `RAD2DEG` — Multiply radians to get degrees
1523
+ - `HASH_CODE_MAX` — Maximum hash code value (2147483647)
1524
+
1525
+ ## Types Reference
1526
+
1527
+ Key types used across the API:
1528
+ - `Vec3`: `readonly [number, number, number]`
1529
+ - `Vec2`: `readonly [number, number]`
1530
+ - `PointInput`: `Vec3 | Vec2`
1531
+ - `Point2D`: `[number, number]`
1532
+ - `Direction`: `Vec3 | 'X' | 'Y' | 'Z'`
1533
+ - `Plane`: `{ origin: Vec3, xDir: Vec3, yDir: Vec3, zDir: Vec3 }`
1534
+ - `PlaneName`: `'XY' | 'XZ' | 'YZ' | 'ZX' | 'YX' | 'ZY' | 'front' | 'back' | 'left' | 'right' | 'top' | 'bottom'`
1535
+ - `PlaneInput`: `Plane | PlaneName`
1536
+ - `ShapeKind`: `'vertex' | 'edge' | 'wire' | 'face' | 'shell' | 'solid' | 'compsolid' | 'compound'`
1537
+ - `AnyShape`: Union of Vertex, Edge, Wire, Face, Shell, Solid, CompSolid, Compound
1538
+ - `Shape3D`: Shell | Solid | CompSolid | Compound
1539
+ - `Shape1D`: Edge | Wire
1540
+ - `CurveType`: `'LINE' | 'CIRCLE' | 'ELLIPSE' | 'HYPERBOLA' | 'PARABOLA' | 'BEZIER_CURVE' | 'BSPLINE_CURVE' | 'OFFSET_CURVE' | 'OTHER_CURVE'`
1541
+ - `SurfaceType`: `'PLANE' | 'CYLINDRE' | 'CONE' | 'SPHERE' | 'TORUS' | 'BEZIER_SURFACE' | 'BSPLINE_SURFACE' | 'REVOLUTION_SURFACE' | 'EXTRUSION_SURFACE' | 'OFFSET_SURFACE' | 'OTHER_SURFACE'`
1542
+ - `Result<T, E>`: `Ok<T> | Err<E>` (default `E = BrepError`)
1543
+ - `BrepError`: `{ kind: BrepErrorKind, code: string, message: string, cause?, metadata? }`
1544
+ - `ShapeMesh`: `{ triangles: Uint32Array, vertices: Float32Array, normals: Float32Array, uvs: Float32Array, faceGroups }`
1545
+ - `EdgeMesh`: `{ lines: number[], edgeGroups: {start, count, edgeId}[] }`
1546
+ - `MeshOptions`: `{ tolerance?, angularTolerance?, signal? }` (mesh() also accepts `{ skipNormals?, includeUVs?, cache? }`)
1547
+ - `BooleanOptions`: `{ optimisation?, simplify?, strategy?, signal? }`
1548
+ - `Camera`: `{ position: Vec3, direction: Vec3, xAxis: Vec3, yAxis: Vec3 }`
1549
+ - `ProjectionPlane`: `'XY' | 'XZ' | 'YZ' | 'YX' | 'ZX' | 'ZY' | 'front' | 'back' | 'top' | 'bottom' | 'left' | 'right'`
1550
+ - `AssemblyNode`: `{ name, shape?, translate?, rotate?: { angle, axis? }, metadata?, children }`
1551
+ - `ModelHistory`: `{ steps: ReadonlyArray<OperationStep>, shapes: ReadonlyMap<string, AnyShape> }`
1552
+ - `OperationStep`: `{ id, type, parameters, inputIds, outputId, timestamp, metadata? }`
1553
+ - `OperationFn`: `(inputs: AnyShape[], params: Record<string, unknown>) => AnyShape`
1554
+ - `HistoryOperationRegistry`: `{ operations: ReadonlyMap<string, OperationFn> }`
1555
+ - `HealingReport`: `{ isValid, alreadyValid, wiresHealed, facesHealed, solidHealed, steps, diagnostics }`
1556
+ - `InterferenceResult`: `{ hasInterference, minDistance, pointOnShape1, pointOnShape2 }`
1557
+ - `CurvatureResult`: `{ mean, gaussian, maxCurvature, minCurvature, maxDirection, minDirection }`
1558
+ - `ShapeHandle`: `{ wrapped, disposed, [Symbol.dispose]() }`
1559
+ - `Bounds3D`: `{ xMin, xMax, yMin, yMax, zMin, zMax }`
1560
+ - `ShapeDescription`: `{ kind, faceCount, edgeCount, wireCount, vertexCount, valid, bounds }`
1561
+
1562
+ ## Advanced Examples
1563
+
1564
+ ### Flanged pipe fitting with loft, sweep, fillet, shell, and boolean
1565
+
1566
+ ```typescript
1567
+ import {
1568
+ initFromOC, DisposalScope, unwrap,
1569
+ sketchCircle, sketchRectangle, sketchRoundedRectangle,
1570
+ Sketcher, draw, drawCircle, drawRectangle,
1571
+ helix, cylinder, sphere,
1572
+ exportSTEP, mesh, exportGlb,
1573
+ faceFinder, edgeFinder, getFaces,
1574
+ fuse, cut, shell, fillet,
1575
+ measureVolume, checkInterference, rotate,
1576
+ } from 'brepjs';
1577
+
1578
+ // 1. Flanged pipe: main tube + two flanges, hollowed out
1579
+ const pipeFitting = (() => {
1580
+ using _scope = new DisposalScope();
1581
+ // Main tube body
1582
+ const tube = cylinder(15, 100);
1583
+
1584
+ // Flanges at top and bottom
1585
+ const bottomFlange = cylinder(30, 5);
1586
+ const topFlange = cylinder(30, 5, { at: [0, 0, 95] });
1587
+
1588
+ // Fuse tube + flanges
1589
+ const step1 = unwrap(fuse(tube, bottomFlange));
1590
+ const body = unwrap(fuse(step1, topFlange));
1591
+
1592
+ // Hollow out: remove top face, shell to 2mm wall thickness
1593
+ // parallelTo('XY') finds faces with Z-normal; atDistance selects the one at Z=100
1594
+ const shellFaces = faceFinder().parallelTo('XY').atDistance(100, [0, 0, 0]).findAll(body);
1595
+ const hollowed = unwrap(shell(body, shellFaces, 2));
1596
+
1597
+ // Fillet the tube-to-flange transitions
1598
+ const filletEdges = edgeFinder().ofCurveType('CIRCLE').ofLength(2 * Math.PI * 15).findAll(hollowed);
1599
+ const filleted = unwrap(fillet(hollowed, filletEdges, 3));
1600
+
1601
+ // Bolt holes in each flange
1602
+ let result = filleted;
1603
+ for (let i = 0; i < 6; i++) {
1604
+ const angle = (360 / 6) * i;
1605
+ const hole = rotate(cylinder(3, 10, { at: [22, 0, -2] }), angle, { axis: [0, 0, 1] });
1606
+ result = unwrap(cut(result, hole));
1607
+ }
1608
+ // Same holes at top
1609
+ for (let i = 0; i < 6; i++) {
1610
+ const angle = (360 / 6) * i;
1611
+ const hole = rotate(cylinder(3, 10, { at: [22, 0, 90] }), angle, { axis: [0, 0, 1] });
1612
+ result = unwrap(cut(result, hole));
1613
+ }
1614
+ return result;
1615
+ })();
1616
+
1617
+ console.log('Volume:', measureVolume(pipeFitting));
1618
+ ```
1619
+
1620
+ ### Enclosure with drafted walls, snap-fit features, and embossed text
1621
+
1622
+ ```typescript
1623
+ import {
1624
+ DisposalScope, unwrap,
1625
+ sketchRoundedRectangle, sketchCircle, sketchRectangle,
1626
+ draw, drawText, loadFont, sketchText,
1627
+ cylinder, compoundSketchExtrude,
1628
+ fuse, cut, shell, fillet,
1629
+ faceFinder, edgeFinder, translate,
1630
+ } from 'brepjs';
1631
+
1632
+ await loadFont('/fonts/Roboto-Regular.ttf', 'Roboto');
1633
+
1634
+ const enclosure = (() => {
1635
+ using _scope = new DisposalScope();
1636
+ // Base box with rounded corners — sketch.extrude() returns Shape3D directly (not Result)
1637
+ const b = sketchRoundedRectangle(80, 50, 5).extrude(30);
1638
+
1639
+ // Shell: remove top face
1640
+ // parallelTo accepts StandardPlane strings ('XY', 'XZ', 'YZ') or Plane objects
1641
+ const shellFaces = faceFinder().parallelTo('XY').findAll(b);
1642
+ const shelled = unwrap(shell(b, shellFaces, 2));
1643
+
1644
+ // Fillet all vertical edges
1645
+ const vertEdges = edgeFinder().inDirection([0, 0, 1]).findAll(shelled);
1646
+ const filleted = unwrap(fillet(shelled, vertEdges, 1));
1647
+
1648
+ // Mounting bosses: 4 cylinders at corners inside the box
1649
+ const bossPositions: [number, number][] = [[30, 18], [-30, 18], [30, -18], [-30, -18]];
1650
+ let result = filleted;
1651
+ for (const [x, y] of bossPositions) {
1652
+ const bossShape = cylinder(3, 25, { at: [x, y, 2] });
1653
+ const hole = cylinder(1.2, 25, { at: [x, y, 2] });
1654
+ result = unwrap(cut(unwrap(fuse(result, bossShape)), hole));
1655
+ }
1656
+
1657
+ // Ventilation slots on side face
1658
+ for (let i = -2; i <= 2; i++) {
1659
+ const slot = translate(
1660
+ sketchRoundedRectangle(1.5, 10, 0.5, { plane: 'XZ' }).extrude(5),
1661
+ [0, -27, 15 + i * 3]
1662
+ );
1663
+ result = unwrap(cut(result, slot));
1664
+ }
1665
+
1666
+ // Embossed text on top — text produces CompoundSketch, use compoundSketchExtrude
1667
+ const textSketch = sketchText('brepjs', { fontSize: 8, fontFamily: 'Roboto' }, { plane: 'XY', origin: 30 });
1668
+ const textSolid = compoundSketchExtrude(textSketch, 1);
1669
+ result = unwrap(fuse(result, textSolid));
1670
+
1671
+ return result;
1672
+ })();
1673
+ ```
1674
+
1675
+ ### Parametric spring with sweep and interference check
1676
+
1677
+ ```typescript
1678
+ import {
1679
+ DisposalScope, unwrap,
1680
+ sketchCircle, sketchHelix,
1681
+ Sketcher, helix, cylinder,
1682
+ fuse, checkInterference,
1683
+ measureLength, describe,
1684
+ translate, rotate,
1685
+ } from 'brepjs';
1686
+
1687
+ const spring = (() => {
1688
+ using _scope = new DisposalScope();
1689
+ // Helix spine: pitch=8, height=60, radius=20
1690
+ const helixSketch = sketchHelix(8, 60, 20);
1691
+
1692
+ // Sweep a circular cross-section along the helix
1693
+ const coil = helixSketch.sweepSketch((plane, origin) =>
1694
+ new Sketcher(plane).movePointerTo([-1.5, 0]).sagittaArc(3, 0, 1.5).sagittaArc(-3, 0, 1.5).close()
1695
+ );
1696
+
1697
+ // Flat ends: cylinders at top and bottom
1698
+ const bottomEnd = cylinder(20, 2, { at: [0, 0, -2] });
1699
+ const topEnd = cylinder(20, 2, { at: [0, 0, 60] });
1700
+
1701
+ return unwrap(fuse(unwrap(fuse(coil, bottomEnd)), topEnd));
1702
+ })();
1703
+
1704
+ // Check if spring fits inside a housing cylinder
1705
+ const housing = cylinder(25, 70, { at: [0, 0, -5] });
1706
+ const interference = unwrap(checkInterference(spring, housing));
1707
+ console.log('Fits:', !interference.hasInterference, 'Gap:', interference.minDistance);
1708
+
1709
+ // Chain transforms
1710
+ const movedSpring = rotate(translate(spring, [100, 0, 0]), 90, { axis: [0, 1, 0] });
1711
+ ```
1712
+
1713
+ ### Multi-format export with materials
1714
+
1715
+ ```typescript
1716
+ import {
1717
+ mesh as meshFn, exportGlb, exportOBJ, exportThreeMF, exportDXF,
1718
+ drawProjection, exportSTEP, exportAssemblySTEP,
1719
+ toBufferGeometryData, toGroupedBufferGeometryData,
1720
+ } from 'brepjs';
1721
+
1722
+ // Mesh the shape for rendering
1723
+ const meshData = meshFn(part, { tolerance: 0.1, angularTolerance: 15, includeUVs: true });
1724
+
1725
+ // glTF with per-face PBR materials
1726
+ const materials = new Map();
1727
+ for (const group of meshData.faceGroups) {
1728
+ materials.set(group.faceId, {
1729
+ name: `face_${group.faceId}`,
1730
+ baseColor: [0.7, 0.7, 0.8, 1.0],
1731
+ metallic: 0.8,
1732
+ roughness: 0.3,
1733
+ });
1734
+ }
1735
+ const glb = exportGlb(meshData, { materials });
1736
+
1737
+ // 3MF for 3D printing
1738
+ const threemf = exportThreeMF(meshData, { name: 'MyPart', unit: 'millimeter' });
1739
+
1740
+ // OBJ for compatibility
1741
+ const obj = exportOBJ(meshData);
1742
+
1743
+ // STEP with assembly structure and colors
1744
+ const step = exportAssemblySTEP(
1745
+ [
1746
+ { shape: body, color: '#336699', name: 'Body' },
1747
+ { shape: lid, color: '#996633', name: 'Lid', alpha: 0.8 },
1748
+ ],
1749
+ { unit: 'millimeter' }
1750
+ );
1751
+
1752
+ // 2D projection → DXF for laser cutting
1753
+ const { visible } = drawProjection(part, 'top');
1754
+ const dxf = exportDXF([
1755
+ { type: 'POLYLINE', points: [[0,0], [100,0], [100,50], [0,50]], closed: true, layer: 'outline' },
1756
+ ], { layer: '0', curveSegments: 64 });
1757
+
1758
+ // Three.js integration
1759
+ const bufferData = toGroupedBufferGeometryData(meshData);
1760
+ // Use bufferData.position, .normal, .index, .groups with THREE.BufferGeometry
1761
+ ```
1762
+
1763
+ ### Functional API: immutable pipeline with healing
1764
+
1765
+ ```typescript
1766
+ import {
1767
+ unwrap,
1768
+ extrude, face, wire, line, circle,
1769
+ fuse, cut, fillet, shell,
1770
+ getEdges, getFaces, edgeFinder, faceFinder,
1771
+ autoHeal, isValid, describe,
1772
+ translate, rotate, linearPattern,
1773
+ mesh, exportGlb,
1774
+ } from 'brepjs';
1775
+
1776
+ // Build with functional API (no classes, all immutable operations)
1777
+ const base = extrude(
1778
+ unwrap(face(unwrap(wire([
1779
+ line([0,0,0], [40,0,0]),
1780
+ line([40,0,0], [40,30,0]),
1781
+ line([40,30,0], [0,30,0]),
1782
+ line([0,30,0], [0,0,0]),
1783
+ ])))),
1784
+ [0, 0, 20]
1785
+ );
1786
+
1787
+ // Find top face and cut mounting holes
1788
+ const topFaces = faceFinder().inDirection('Z').findAll(base);
1789
+ const holeFace = unwrap(face(unwrap(wire([circle(3, { at: [10, 15, 20] })]))));
1790
+ const hole1 = extrude(holeFace, [0, 0, -25]);
1791
+ const withHoles = unwrap(cut(base, hole1));
1792
+
1793
+ // Fillet just the top edges
1794
+ const topEdges = edgeFinder().atDistance(20, [20, 15, 0]).findAll(withHoles);
1795
+ const filleted = unwrap(fillet(withHoles, topEdges, 2));
1796
+
1797
+ // Shell it out
1798
+ const shellFaces = faceFinder().inDirection([0, 0, 1]).findAll(filleted);
1799
+ const shelled = unwrap(shell(filleted, shellFaces, 1.5));
1800
+
1801
+ // Validate and heal
1802
+ if (!isValid(shelled)) {
1803
+ const { shape: healed, report } = unwrap(autoHeal(shelled));
1804
+ console.log('Healed:', report.steps);
1805
+ }
1806
+
1807
+ // Describe the result
1808
+ const desc = describe(shelled);
1809
+ console.log(`${desc.kind}: ${desc.faceCount} faces, ${desc.edgeCount} edges, valid: ${desc.valid}`);
1810
+ ```