@tweakcad/occt-wasm 0.0.0-stage → 5.17.0-tweakcad.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,533 @@
1
- # Temporary Holding Version
1
+ # @tweakcad/occt-wasm
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ TweakCAD's build of [occt-wasm](https://github.com/andymai/occt-wasm), made from
4
+ the `tweakcad` branch of [TweakCAD/occt-wasm](https://github.com/TweakCAD/occt-wasm).
5
+ Version `<upstream>-tweakcad.<n>` is upstream release `<upstream>` plus the commits
6
+ on that branch; the API is upstream's. To keep `import ... from 'occt-wasm'`:
7
+
8
+ ```bash
9
+ npm install occt-wasm@npm:@tweakcad/occt-wasm
10
+ ```
11
+
12
+ The rest of this file is upstream's README.
13
+
14
+ ---
15
+
16
+ <div align="center">
17
+
18
+ # occt-wasm
19
+
20
+ [![npm](https://img.shields.io/npm/v/occt-wasm)](https://www.npmjs.com/package/occt-wasm)
21
+ [![Crates.io](https://img.shields.io/crates/v/occt-wasm.svg)](https://crates.io/crates/occt-wasm)
22
+ [![CI](https://github.com/andymai/occt-wasm/actions/workflows/ci.yml/badge.svg)](https://github.com/andymai/occt-wasm/actions/workflows/ci.yml)
23
+ [![Last release](https://img.shields.io/github/release-date/andymai/occt-wasm?label=last%20release)](https://github.com/andymai/occt-wasm/releases)
24
+ [![Commit activity](https://img.shields.io/github/commit-activity/m/andymai/occt-wasm?label=commits%2Fmonth)](https://github.com/andymai/occt-wasm/commits/main)
25
+ [![License](https://img.shields.io/badge/tooling-MIT%20OR%20Apache--2.0-blue.svg)](#license) [![WASM License](https://img.shields.io/badge/wasm%20output-LGPL--2.1--only-blue.svg)](#license)
26
+
27
+ [OpenCascade](https://github.com/Open-Cascade-SAS/OCCT) V8 compiled to WebAssembly with a clean TypeScript API.
28
+
29
+ Smaller bundles, branded types, arena-based memory, and modern tooling.
30
+
31
+ </div>
32
+
33
+ > **Looking for a higher-level CAD library?** [brepjs](https://github.com/andymai/brepjs) builds on occt-wasm with a friendlier API for parametric modeling, sketching, and production CAD applications. Use occt-wasm directly when you need full control over OCCT operations.
34
+
35
+ ## Highlights
36
+
37
+ - **~4.5 MB brotli** -- roughly 2x smaller than opencascade.js
38
+ - **Comprehensive API** -- primitives, booleans, sweeps, XCAF assemblies, curves, surfaces, STEP/STL/glTF/BREP I/O, topology, shape evolution tracking
39
+ - **Arena-based API** -- u32 shape handles, no manual `.delete()`, `Symbol.dispose` support
40
+ - **TypeScript-first** -- branded `ShapeHandle`, union types for shapes/surfaces/curves, structured returns
41
+ - **Structured error handling** -- `OcctErrorCode` enum for programmatic `switch/case` instead of string parsing
42
+ - **Web Worker support** -- `OcctWorker` class for off-main-thread CAD operations via [Comlink](https://github.com/GoogleChromeLabs/comlink)
43
+ - **Modern browser targets** -- WASM SIMD, tail calls, wasm-exceptions
44
+
45
+ ## Scope
46
+
47
+ To set expectations, this library deliberately does not:
48
+
49
+ - **Provide a higher-level CAD modeling API** — parametric sketching, constraints, feature trees, and ergonomic modeling belong in [brepjs](https://github.com/andymai/brepjs), which wraps occt-wasm for that purpose
50
+ - **Manage memory automatically beyond arena handles** — shapes are freed when the kernel is disposed or you call `release()`; there is no per-shape garbage collection
51
+ - **Support non-WASM-SIMD browsers** — the build requires WASM SIMD (baseline `-msimd128`), tail calls, and wasm exceptions, so it needs a recent engine (see [Browser Compatibility](#browser-compatibility)). Relaxed-SIMD is intentionally not used: some Safari/iOS WebKit builds fail to compile relaxed-SIMD modules, and it made geometry non-reproducible across CPUs
52
+ - **Include OCCT visualization or display modules** — TKV3d, TKHLR (except the HLR facade), and the AIS interactive context are excluded; bring your own renderer (Three.js, Babylon.js, etc.)
53
+ - **Support IGES import/export** -- TKDEIGES is excluded from the link; use STEP for interchange
54
+
55
+ ## Install
56
+
57
+ ```bash
58
+ npm install occt-wasm
59
+ ```
60
+
61
+ ## Quick Start
62
+
63
+ ```typescript check
64
+ import { OcctKernel } from "occt-wasm";
65
+
66
+ // Recommended: deterministic cleanup via Symbol.dispose
67
+ {
68
+ using kernel = await OcctKernel.init();
69
+
70
+ // Primitives
71
+ const box = kernel.makeBox(20, 20, 20);
72
+ const cyl = kernel.makeCylinder(8, 30);
73
+
74
+ // Modeling -- fillet takes a solid, so round the box before combining
75
+ const edges = kernel.getSubShapes(box, "edge");
76
+ const filleted = kernel.fillet(box, edges.slice(0, 4), 2.0);
77
+
78
+ // Booleans
79
+ const fused = kernel.fuse(filleted, cyl);
80
+
81
+ // Tessellation -> Three.js / Babylon.js
82
+ const mesh = kernel.tessellate(fused);
83
+ // mesh.positions (Float32Array), mesh.normals, mesh.indices
84
+
85
+ // Edge overlay that shares vertices with that mesh (falls back to
86
+ // curve sampling for edges that were never meshed)
87
+ const outline = kernel.wireframe(fused, { source: "triangulation" });
88
+
89
+ // STEP I/O
90
+ const step = kernel.exportStep(fused);
91
+ const reimported = kernel.importStep(step);
92
+
93
+ // Query
94
+ const vol = kernel.getVolume(fused);
95
+ const bbox = kernel.getBoundingBox(fused);
96
+ const com = kernel.getCenterOfMass(fused);
97
+
98
+ // kernel is disposed at end of block
99
+ }
100
+ ```
101
+
102
+ > **Boolean results are compounds.** OCCT's `BRepAlgoAPI` operations wrap their
103
+ > output in a `TopoDS_Compound` — a boolean can produce several disjoint solids.
104
+ > The operations that downcast to a solid (`fillet`, `chamfer`, `filletVariable`,
105
+ > `filletBatch`, `healSolid`) reject one, so unwrap first:
106
+ >
107
+ > ```typescript check kernel,fused
108
+ > const [solid] = kernel.getSubShapes(fused, "solid");
109
+ > if (!solid) throw new Error("boolean produced no solid");
110
+ > ```
111
+ >
112
+ > **Not every edge is filletable.** Seam and degenerate edges are not, so on a
113
+ > shape you didn't build yourself, select edges by geometry rather than by index.
114
+ > The `slice(0, 4)` above is safe only because all 12 edges of a plain box round
115
+ > cleanly — on the box-plus-cylinder fusion, only 13 of 20 edges do.
116
+
117
+ ## Rust Crate
118
+
119
+ The same OCCT WASM is available as a [Rust crate](https://crates.io/crates/occt-wasm) for native targets (servers, CLIs, build scripts) — no C++ toolchain required:
120
+
121
+ ```toml
122
+ [dependencies]
123
+ occt-wasm = "7"
124
+ ```
125
+
126
+ ```rust
127
+ use occt_wasm::OcctKernel;
128
+
129
+ let mut kernel = OcctKernel::new()?;
130
+ let box_shape = kernel.make_box(10.0, 20.0, 30.0)?;
131
+ let sphere = kernel.make_sphere(8.0)?;
132
+ let fused = kernel.fuse(box_shape, sphere)?;
133
+ let mesh = kernel.tessellate(fused, 0.1, 0.5)?;
134
+ let step = kernel.export_step(fused)?;
135
+ ```
136
+
137
+ The crate embeds a brotli-compressed WASM binary (~4.7 MB) and runs it via [wasmtime](https://wasmtime.dev/). Same 170+ facade methods as the TS API. See [`crate/README.md`](./crate/README.md) and [docs.rs/occt-wasm](https://docs.rs/occt-wasm) for full details.
138
+
139
+ ## Initialization
140
+
141
+ By default, `OcctKernel.init()` auto-locates the `.wasm` file next to the JS module. You can also provide explicit paths or pre-loaded binaries:
142
+
143
+ ```typescript check
144
+ import { OcctKernel } from "occt-wasm";
145
+
146
+ // Auto-detect (browser, Node.js, or Worker):
147
+ const kernel = await OcctKernel.init();
148
+
149
+ // ...or point at an explicit URL / path:
150
+ await OcctKernel.init({ wasm: "/assets/occt-wasm.wasm" });
151
+
152
+ // ...or hand over a pre-fetched binary, skipping the fetch:
153
+ const binary = await fetch("/occt-wasm.wasm").then((r) => r.arrayBuffer());
154
+ await OcctKernel.init({ wasm: binary });
155
+ await OcctKernel.init({ wasm: new Uint8Array(binary) });
156
+
157
+ // ...or pass the Emscripten glue you loaded yourself, for hosts where init()'s
158
+ // own import of ./occt-wasm.js cannot resolve (e.g. a bundled Web Worker):
159
+ const glueUrl: string = "/occt/occt-wasm.js";
160
+ const { default: createModule } = await import(/* webpackIgnore: true */ glueUrl);
161
+ await OcctKernel.init({ createModule, wasm: "/occt/occt-wasm.wasm" });
162
+ ```
163
+
164
+ ## Error Handling
165
+
166
+ All errors are instances of `OcctError` with a structured `code` field for programmatic handling:
167
+
168
+ ```typescript check kernel,a,b
169
+ import { OcctError, OcctErrorCode } from "occt-wasm";
170
+
171
+ try {
172
+ kernel.fuse(a, b);
173
+ } catch (e) {
174
+ if (e instanceof OcctError) {
175
+ switch (e.code) {
176
+ case OcctErrorCode.BooleanFailed:
177
+ // retry with simpler geometry
178
+ break;
179
+ case OcctErrorCode.InvalidShapeId:
180
+ // shape was already released
181
+ break;
182
+ case OcctErrorCode.KernelError:
183
+ // OCCT internal error (Standard_Failure)
184
+ console.error(e.message);
185
+ break;
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ Available error codes:
192
+
193
+ | Code | When |
194
+ | -------------------- | ----------------------------------------------- |
195
+ | `ConstructionFailed` | `Build()`/`IsDone()` returned false |
196
+ | `BooleanFailed` | Boolean operation (fuse/cut/common/etc.) failed |
197
+ | `InvalidShapeId` | Shape ID not found in the arena |
198
+ | `InvalidLabelId` | XCAF label ID not found |
199
+ | `TessellationFailed` | Meshing operation failed |
200
+ | `ImportExportFailed` | STEP/STL/BREP I/O error |
201
+ | `HealingFailed` | Shape repair failed |
202
+ | `DocumentClosed` | Operation on a closed XCAF document |
203
+ | `KernelError` | OCCT `Standard_Failure` (unclassified) |
204
+ | `Unknown` | Error from outside the kernel |
205
+
206
+ A fillet or chamfer that OCCT cannot build also sets `faultyEdges` to the edges, as passed in, whose contour failed. The raw message carries the same list as a ` [faulty edges: 3,7]` suffix.
207
+
208
+ ```typescript check kernel,a
209
+ import { OcctError } from "occt-wasm";
210
+
211
+ const edges = kernel.getSubShapes(a, "edge");
212
+ try {
213
+ kernel.fillet(a, edges, 3);
214
+ } catch (e) {
215
+ if (e instanceof OcctError && e.faultyEdges) {
216
+ // retry without e.faultyEdges, or with a smaller radius on them
217
+ }
218
+ }
219
+ ```
220
+
221
+ > **`OcctWorker` is the exception.** Comlink serializes a thrown error down to
222
+ > `{ message, name, stack }`, so an error crossing the worker boundary arrives
223
+ > as a plain `Error`: the message survives intact, but `code`, `operation`, and
224
+ > `instanceof OcctError` do not. Match on `e.message` there, or do the
225
+ > `switch (e.code)` inside the worker.
226
+
227
+ ## Named Enums
228
+
229
+ Sweep, offset, and boolean operations use self-documenting enums instead of opaque numbers:
230
+
231
+ ```typescript check kernel,profile,spine,wire,shape,face,base,tool1,tool2
232
+ import { TransitionMode, JoinType, BooleanOp, BooleanGlue } from "occt-wasm";
233
+
234
+ // Sweep with round-corner transitions
235
+ kernel.sweep(profile, spine, TransitionMode.RoundCorner);
236
+
237
+ // Offset wire with arc joins
238
+ kernel.offsetWire2D(wire, 2.0, JoinType.Arc);
239
+
240
+ // Hollow a solid through one face; Intersection keeps the inner corners
241
+ // at concave edges sharp where Arc would round them
242
+ kernel.shell(shape, [face], 2.0, 1e-6, JoinType.Intersection);
243
+
244
+ // Boolean pipeline
245
+ kernel.booleanPipeline(base, [BooleanOp.Cut, BooleanOp.Fuse], [tool1, tool2]);
246
+
247
+ // One boolean against several tools, glued where the operands share faces
248
+ kernel.booleanOp(BooleanOp.Fuse, [base], [tool1, tool2], { glue: BooleanGlue.Shift });
249
+ ```
250
+
251
+ Numeric values (0, 1, 2) are still accepted for backwards compatibility.
252
+
253
+ ## Type Predicates
254
+
255
+ Convenience methods for checking shape topology:
256
+
257
+ ```typescript check kernel,shape
258
+ if (kernel.isSolid(shape)) {
259
+ /* ... */
260
+ }
261
+ if (kernel.isFace(shape)) {
262
+ /* ... */
263
+ }
264
+ if (kernel.isEdge(shape)) {
265
+ /* ... */
266
+ }
267
+ if (kernel.isWire(shape)) {
268
+ /* ... */
269
+ }
270
+ if (kernel.isVertex(shape)) {
271
+ /* ... */
272
+ }
273
+ if (kernel.isShell(shape)) {
274
+ /* ... */
275
+ }
276
+ if (kernel.isCompound(shape)) {
277
+ /* ... */
278
+ }
279
+ ```
280
+
281
+ ## Web Workers
282
+
283
+ For browser apps, heavy CAD operations can block the main thread. `OcctWorker` runs a full kernel in a Web Worker with the same API:
284
+
285
+ ```typescript check edge
286
+ import { OcctWorker } from "occt-wasm/worker";
287
+
288
+ // Spawn a worker with its own kernel
289
+ const worker = await OcctWorker.spawn({ wasm: "/occt-wasm.wasm" });
290
+
291
+ // Same API, every call returns a Promise
292
+ const box = await worker.makeBox(10, 20, 30);
293
+ const cyl = await worker.makeCylinder(5, 40);
294
+ const fused = await worker.fuse(box, cyl);
295
+ const mesh = await worker.tessellate(fused);
296
+ console.log(`${mesh.triangleCount} triangles`);
297
+
298
+ // Access the full kernel via .kernel for less common methods
299
+ const nurbs = await worker.kernel.getNurbsCurveData(edge);
300
+
301
+ // Clean up
302
+ worker.terminate();
303
+ ```
304
+
305
+ The worker helper uses [Comlink](https://github.com/GoogleChromeLabs/comlink) (~1.2 KB gzipped) for transparent RPC. Each worker has its own WASM instance and arena -- shape handles are local to the worker.
306
+
307
+ ## XCAF Assemblies
308
+
309
+ Create assembly documents with colors, names, and component hierarchies. Colors are sRGB components in [0, 1], as in CSS (`#808080` is `128 / 255`), and names may be any Unicode text:
310
+
311
+ ```typescript check kernel,box,gear,stepData
312
+ // Factory method auto-injects Emscripten FS for glTF export
313
+ const doc = kernel.createXCAFDocument();
314
+
315
+ const housing = doc.addShape(box, { name: "housing", color: [0.8, 0.2, 0.1] });
316
+ doc.addChild(housing, gear, {
317
+ name: "gear-1",
318
+ location: { tx: 10, tz: 5 },
319
+ color: [0.5, 0.5, 0.5],
320
+ });
321
+
322
+ // Export
323
+ const step = doc.exportSTEP(); // preserves colors/names
324
+ const stepInches = doc.exportSTEP({ unit: "inch" }); // same geometry, written in inches
325
+ const stepAp242 = doc.exportSTEP({ schema: "AP242" }); // AP203, AP214 (default) or AP242
326
+ const glb = doc.exportGLTF(); // binary glTF (.glb) bytes
327
+ doc.close();
328
+
329
+ // Import with preserved metadata
330
+ const imported = kernel.importXCAFFromSTEP(stepData);
331
+ ```
332
+
333
+ Walking an imported assembly: each component is a placed reference to a prototype label (a part or sub-assembly). Resolve it with `getReferredLabel` to reach the prototype's name, named sub-shapes and children. To author the same structure, add a compound with `doc.addShape(compound, { assembly: true })`.
334
+
335
+ ```typescript check doc
336
+ import type { LabelTag } from "occt-wasm";
337
+
338
+ function walk(label: LabelTag, depth = 0): void {
339
+ const info = doc.getLabelInfo(label);
340
+ console.log(`${" ".repeat(depth)}${info.name}`);
341
+ const proto = doc.getReferredLabel(label) ?? label;
342
+ for (const sub of doc.getSubShapes(proto)) {
343
+ console.log(`${" ".repeat(depth + 1)}(${doc.getLabelInfo(sub).name})`);
344
+ }
345
+ for (const child of doc.getChildren(proto)) walk(child, depth + 1);
346
+ }
347
+ for (const root of doc.getRoots()) walk(root);
348
+ ```
349
+
350
+ `getLocation(label)` returns a component's placement as a 3x4 matrix in the layout `kernel.transform` accepts, so a prototype's geometry can be tessellated once and instanced per component.
351
+
352
+ ## Bundler Configuration
353
+
354
+ ### Vite
355
+
356
+ ```typescript
357
+ // vite.config.ts
358
+ export default defineConfig({
359
+ optimizeDeps: {
360
+ exclude: ["occt-wasm"], // Don't pre-bundle WASM
361
+ },
362
+ build: {
363
+ target: "esnext", // Required for WASM features
364
+ },
365
+ });
366
+ ```
367
+
368
+ ### Webpack 5
369
+
370
+ ```javascript
371
+ // webpack.config.js
372
+ module.exports = {
373
+ experiments: { asyncWebAssembly: true },
374
+ module: {
375
+ rules: [{ test: /\.wasm$/, type: "asset/resource" }],
376
+ },
377
+ };
378
+ ```
379
+
380
+ Webpack bundles the Emscripten glue and emits `occt-wasm.wasm` as a hashed
381
+ asset, rewriting the URL it is loaded from. No manual copy step is needed, and
382
+ `OcctKernel.init()` finds the binary without a `wasm` option.
383
+
384
+ ### Next.js
385
+
386
+ No configuration needed, under either Turbopack or webpack. Both emit the
387
+ `.wasm` into `_next/static` and rewrite the URL. Initialize from a client
388
+ component (`"use client"`) so the kernel loads in the browser rather than
389
+ during SSR:
390
+
391
+ ```javascript
392
+ "use client";
393
+ import { useEffect } from "react";
394
+
395
+ export default function Viewer() {
396
+ useEffect(() => {
397
+ (async () => {
398
+ const { OcctKernel } = await import("occt-wasm");
399
+ const kernel = await OcctKernel.init();
400
+ // ...
401
+ })();
402
+ }, []);
403
+ }
404
+ ```
405
+
406
+ ### Node.js
407
+
408
+ ```typescript check
409
+ // Works out of the box with Node.js 18+
410
+ import { OcctKernel } from "occt-wasm";
411
+ const kernel = await OcctKernel.init();
412
+ ```
413
+
414
+ ## API Reference
415
+
416
+ Generate full docs locally: `cd ts && npm run docs` (TypeDoc output).
417
+
418
+ | Category | What's covered |
419
+ | ---------------- | -------------------------------------------------------------------------------------------------------------- |
420
+ | **Primitives** | Box, cylinder, sphere, cone, torus, ellipsoid, rectangle, half-space |
421
+ | **Booleans** | Fuse, cut, common, intersect, section (by shape or plane) + multi-shape variants, intersection cells |
422
+ | **Modeling** | Extrude, revolve, fillet and chamfer (per-edge radii/distances, tapered fillets), shell, offset, multi-face draft |
423
+ | **Sweeps** | Pipe, loft, sweep, oriented sweep (fixed/Frenet/up-axis/auxiliary), draft prism, extrusion laws |
424
+ | **Construction** | Vertices, edges (line/arc/circle/ellipse/bezier/helix), wires, faces, solids, compounds, sewing |
425
+ | **Transforms** | Translate, rotate, scale, mirror, align to bounding box, 3x4 matrix, linear/circular patterns |
426
+ | **Topology** | Shape type queries, type predicates, sub-shape extraction, adjacency, hash codes |
427
+ | **Tessellation** | Triangle meshes (absolute or relative deflection), wireframe polylines (curve-sampled or mesh-aligned), per-face groups, batched meshing |
428
+ | **I/O** | STEP (choice of length unit and AP203/AP214/AP242 protocol), STL (ASCII + binary), BREP (text + binary) import/export |
429
+ | **Query** | Bounding box, volume, surface area, length, center of mass, inertia tensor, point-in-solid, curvature |
430
+ | **Surfaces** | Type, exact parameters, normal, UV bounds, point classification, B-spline construction |
431
+ | **Curves** | Type, exact parameters, point/tangent eval, parameters, NURBS data, interpolation (incl. clamped tangents), project point |
432
+ | **Projection** | Hidden line removal (HLR), multiview SVG render (Front/Top/Right/Iso) |
433
+ | **Modifiers** | Thicken, defeature, reverse, simplify, variable fillet, 2D wire offset |
434
+ | **Evolution** | Face and edge history for extrude, revolve, pipe, sweep, loft, fuse, cut, fillet, chamfer, draft, shell, offset, thicken, unify, and the transforms |
435
+ | **XCAF** | Assembly documents with colors, names, component hierarchies, STEP/glTF export |
436
+ | **Healing** | Fix shape, unify domain, heal solid/face/wire, fix orientations, remove degenerate edges |
437
+ | **Batch** | Multi-shape translate, chained boolean pipeline |
438
+
439
+ ## Architecture
440
+
441
+ ```
442
+ OCCT V8.0.1 C++ (git submodule)
443
+ -> emcmake cmake (static libs)
444
+ -> C++ facade (OcctKernel class, arena-based u32 IDs)
445
+ -> Embind bindings
446
+ -> emcc link (-O3, -flto, -fwasm-exceptions, SIMD) -> .wasm
447
+ -> wasm-opt -O4 --converge --gufa -> dist/
448
+ ```
449
+
450
+ Built with Rust xtask (`cargo xtask build`), tested with Vitest.
451
+
452
+ ## Size & Performance
453
+
454
+ Compared against other OCCT-to-WASM builds (all include STEP, XCAF, glTF):
455
+
456
+ | Build | brotli |
457
+ | ------------------ | ------- |
458
+ | **occt-wasm** | ~4.5 MB |
459
+ | opencascade.js | ~9 MB |
460
+ | brepjs-opencascade | ~5 MB |
461
+
462
+ Run benchmarks locally: `npx tsx test/benchmark.ts`
463
+
464
+ ## Development
465
+
466
+ ### Building from Source
467
+
468
+ ```bash
469
+ # Prerequisites: Rust 1.95+, emsdk 5.0.3
470
+ git clone --recurse-submodules https://github.com/andymai/occt-wasm
471
+ cd occt-wasm
472
+ npm install && cd ts && npm install && cd ..
473
+
474
+ cargo xtask build # Build OCCT + facade -> WASM
475
+ cargo xtask test # Run tests
476
+
477
+ # View the Three.js example
478
+ node scripts/static-server.mjs
479
+ # Open http://localhost:3000/examples/three-js/
480
+ ```
481
+
482
+ ### Docker Build
483
+
484
+ No local emsdk or Rust needed -- everything runs in the container.
485
+
486
+ ```bash
487
+ npm run docker:build # Build image (OCCT layer cached after first run)
488
+ npm run docker:dist # Build + copy dist/ artifacts to host
489
+ ```
490
+
491
+ ## Browser Compatibility
492
+
493
+ occt-wasm requires modern browsers with WASM SIMD, tail calls, and exception
494
+ handling. WASM **tail calls** are the newest and therefore binding requirement —
495
+ they gate the minimum versions below. The versions listed are the lowest
496
+ combinations verified to load the kernel:
497
+
498
+ | Browser | Minimum (verified) | Notes |
499
+ | ------- | ------------------ | ------------------------------------------- |
500
+ | Chrome | 114+ | Tail calls landed in 112; 114 is verified |
501
+ | Edge | 114+ | Same engine as Chrome |
502
+ | Safari | 17.2+ | Earliest WebKit verified to load the kernel |
503
+ | Firefox | 121+ | Tail calls shipped in 121; 146 is verified |
504
+
505
+ Node.js 22+ is recommended (tail calls via V8). Node.js 18+ works if your V8 version supports the required WASM features.
506
+
507
+ ## Known Limitations
508
+
509
+ These are upstream OCCT V8.0.1 issues, not occt-wasm bugs:
510
+
511
+ - **IGES** -- TKDEIGES excluded from link; no IGES import/export
512
+ - **Zero-length extrusion** -- WASM exception escapes JS catch boundary (1 test skip)
513
+ - **Single WASM thread** -- each kernel instance is single-threaded; use `OcctWorker` (see above) to move work off the main thread
514
+
515
+ These will be addressed as upstream OCCT and browser support improve.
516
+
517
+ OCCT V8.0.1 (2026-07) resolved several upstream hangs and crashes that
518
+ affected modeling here: an infinite-loop guard in the boolean section
519
+ algorithm, crash guards in chamfer construction (`ChFi3d_Builder`) and
520
+ `BRep_Tool::CurveOnPlane`, null-pcurve guards in `UnifySameDomain`, and
521
+ an infinite-loop guard in the STEP writer.
522
+
523
+ ## Contributing
524
+
525
+ This project is open source. Bug reports and feature requests are welcome via GitHub Issues. For pull requests, please open an issue first to discuss the change.
526
+
527
+ ## License
528
+
529
+ **Build tooling** (xtask, scripts, TypeScript wrapper): MIT OR Apache-2.0
530
+
531
+ **Compiled WASM output**: LGPL-2.1-only (inherits from [OCCT](https://dev.opencascade.org/resources/download))
532
+
533
+ The LGPL requires that end users can replace the LGPL component. For web applications, this is satisfied by loading the `.wasm` file from a URL (which users can override via `OcctKernel.init({ wasm: '...' })`). If you ship a desktop app with the WASM embedded, consult the [LGPL-2.1 FAQ](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html).
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A 5x7 bitmap face, enough for the labels the renderers draw: view names,
3
+ * the XYZ gnomon and the "100 x 60 x 40 (XxYxZ)" footer.
4
+ *
5
+ * Shipping a face as data keeps the PNG renderer free of a font file and of
6
+ * any system font lookup, so a caller who embeds this in a single-file binary
7
+ * gets the same picture everywhere. Characters with no glyph render as a gap.
8
+ *
9
+ * Each row is five bits, most significant bit leftmost.
10
+ *
11
+ * @module
12
+ */
13
+ export declare const GLYPH_W = 5;
14
+ export declare const GLYPH_H = 7;
15
+ export declare const FONT_5X7: Record<string, number[]>;
16
+ //# sourceMappingURL=font5x7.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font5x7.d.ts","sourceRoot":"","sources":["../src/font5x7.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,OAAO,IAAI,CAAC;AAEzB,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAiD7C,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * A 5x7 bitmap face, enough for the labels the renderers draw: view names,
3
+ * the XYZ gnomon and the "100 x 60 x 40 (XxYxZ)" footer.
4
+ *
5
+ * Shipping a face as data keeps the PNG renderer free of a font file and of
6
+ * any system font lookup, so a caller who embeds this in a single-file binary
7
+ * gets the same picture everywhere. Characters with no glyph render as a gap.
8
+ *
9
+ * Each row is five bits, most significant bit leftmost.
10
+ *
11
+ * @module
12
+ */
13
+ export const GLYPH_W = 5;
14
+ export const GLYPH_H = 7;
15
+ export const FONT_5X7 = {
16
+ ' ': [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000],
17
+ '(': [0b00110, 0b01000, 0b10000, 0b10000, 0b10000, 0b01000, 0b00110],
18
+ ')': [0b01100, 0b00010, 0b00001, 0b00001, 0b00001, 0b00010, 0b01100],
19
+ '.': [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b01100],
20
+ '0': [0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110],
21
+ '1': [0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110],
22
+ '2': [0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111],
23
+ '3': [0b11111, 0b00010, 0b00100, 0b00010, 0b00001, 0b10001, 0b01110],
24
+ '4': [0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010],
25
+ '5': [0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110],
26
+ '6': [0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110],
27
+ '7': [0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000],
28
+ '8': [0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110],
29
+ '9': [0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100],
30
+ 'A': [0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001],
31
+ 'B': [0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110],
32
+ 'F': [0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000],
33
+ 'I': [0b01110, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110],
34
+ 'L': [0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111],
35
+ 'R': [0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001],
36
+ 'T': [0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100],
37
+ 'X': [0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001],
38
+ 'Y': [0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100, 0b00100],
39
+ 'Z': [0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111],
40
+ 'a': [0b00000, 0b00000, 0b01110, 0b00001, 0b01111, 0b10001, 0b01111],
41
+ 'c': [0b00000, 0b00000, 0b01110, 0b10000, 0b10000, 0b10001, 0b01110],
42
+ 'e': [0b00000, 0b00000, 0b01110, 0b10001, 0b11111, 0b10000, 0b01110],
43
+ 'f': [0b00110, 0b01001, 0b01000, 0b11100, 0b01000, 0b01000, 0b01000],
44
+ 'g': [0b00000, 0b00000, 0b01111, 0b10001, 0b01111, 0b00001, 0b01110],
45
+ 'h': [0b10000, 0b10000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001],
46
+ 'i': [0b00100, 0b00000, 0b01100, 0b00100, 0b00100, 0b00100, 0b01110],
47
+ 'k': [0b10000, 0b10000, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010],
48
+ 'm': [0b00000, 0b00000, 0b11010, 0b10101, 0b10101, 0b10101, 0b10101],
49
+ 'n': [0b00000, 0b00000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001],
50
+ 'o': [0b00000, 0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b01110],
51
+ 'p': [0b00000, 0b00000, 0b11110, 0b10001, 0b11110, 0b10000, 0b10000],
52
+ 'r': [0b00000, 0b00000, 0b10110, 0b11001, 0b10000, 0b10000, 0b10000],
53
+ 's': [0b00000, 0b00000, 0b01111, 0b10000, 0b01110, 0b00001, 0b11110],
54
+ 't': [0b01000, 0b01000, 0b11100, 0b01000, 0b01000, 0b01001, 0b00110],
55
+ 'u': [0b00000, 0b00000, 0b10001, 0b10001, 0b10001, 0b10011, 0b01101],
56
+ 'v': [0b00000, 0b00000, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100],
57
+ 'w': [0b00000, 0b00000, 0b10101, 0b10101, 0b10101, 0b10101, 0b01010],
58
+ 'x': [0b00000, 0b00000, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001],
59
+ 'y': [0b00000, 0b00000, 0b10001, 0b10001, 0b01111, 0b00001, 0b01110],
60
+ '×': [0b00000, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b00000],
61
+ '-': [0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000],
62
+ ',': [0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b01100, 0b01000],
63
+ ':': [0b00000, 0b01100, 0b01100, 0b00000, 0b01100, 0b01100, 0b00000],
64
+ };
65
+ //# sourceMappingURL=font5x7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font5x7.js","sourceRoot":"","sources":["../src/font5x7.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC;AACzB,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC;AAEzB,MAAM,CAAC,MAAM,QAAQ,GAA6B;IAC9C,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;IACpE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;CACvE,CAAC"}