A NURBS-based CAD kernel in Rust targeting WebAssembly. Provides boundary representation (BRep) modeling with exact topology, boolean operations, feature operations, and multi-format I/O. Runs in browsers, edge workers, and native.
- BRep Modeling -- Radial-edge topology with NURBS and analytical geometry (planes, spheres, cylinders, cones, tori)
- Boolean Operations -- Union, intersection, subtraction with shared-edge topology and exact rational predicates
- Feature Operations -- Linear extrude, revolve, constant-radius fillet, constant-distance chamfer
- Primitives -- Box, sphere, cylinder with configurable resolution
- Rigid Transforms -- Translate, rotate, scale
- I/O -- STEP AP203 import/export, binary/ASCII STL export, glTF 2.0 (GLB) export
- Tessellation -- Ear-clipping triangulation with per-vertex normals and face ID mapping
- WASM Bindings -- Full API via
wasm-bindgenfor browser and edge deployment - TypeScript/React SDK --
knot-cadnpm package with hooks and Three.js integration
cargo build
cargo test --workspacewasm-pack build --target web --out-dir pkg
python3 -m http.server 8080 # open http://localhost:8080/web/pnpm install
pnpm build # builds WASM + JS SDKimport { createKnot } from 'knot-cad';
const knot = await createKnot();
const box = knot.box(2, 3, 4);
const cylinder = knot.cylinder(0, 0, 0, 1, 5, 32);
const result = knot.subtraction(box, cylinder);
const mesh = result.tessellate();
console.log(mesh.triangleCount); // triangle mesh ready for rendering
const step = result.toSTEP(); // STEP AP203 string
const stl = result.toSTL(); // binary STL Uint8Array
const glb = result.toGLB(); // binary glTF Uint8Array
result.free();import { useKnot, useBrep, KnotMesh } from 'knot-cad/react';
function Part() {
const knot = useKnot();
const brep = useBrep(() => {
if (!knot) return null;
return knot.box(2, 2, 2);
}, [knot]);
return brep ? <KnotMesh brep={brep} color="steelblue" /> : null;
}Cargo workspace with a facade crate (knot) re-exporting 8 sub-crates. The dependency DAG is enforced at compile time:
knot (facade)
|
+-- knot-bindings --> knot-ops, knot-tessellate, knot-io
+-- knot-ops --> knot-intersect, knot-topo, knot-geom, knot-core
+-- knot-intersect --> knot-geom, knot-core
+-- knot-topo --> knot-geom, knot-core
+-- knot-tessellate--> knot-topo, knot-geom
+-- knot-io --> knot-topo, knot-geom, knot-core
+-- knot-geom --> knot-core
+-- knot-core (no internal deps)
| Crate | Purpose |
|---|---|
| knot-core | Exact rational arithmetic (ExactRational via malachite), orientation predicates (orient3d, orient2d), snap-rounding grid with LatticeIndex vertex keys, interval arithmetic, AABB, error types |
| knot-geom | Curve and Surface enums for pattern-matching analytical fast-paths. Curves: Line, CircularArc, EllipticalArc, Nurbs. Surfaces: Plane, Sphere, Cylinder, Cone, Torus, Nurbs. All immutable, stored behind Arc<[T]> |
| knot-topo | Radial-edge BRep: Vertex > Edge > HalfEdge > Loop > Face > Shell > Solid > BRep. Faces carry Arc<Surface>, edges carry Arc<Curve>. All topology is immutable and Arc-shared |
| knot-intersect | Curve-curve, curve-surface, and surface-surface intersection. Analytical fast-paths for primitive pairs (plane-plane, plane-sphere, plane-cylinder, sphere-sphere, cylinder-cylinder). General SSI via hierarchical seed-finding + curvature-adaptive marching |
| knot-ops | Boolean operations, fillet/chamfer, extrude/revolve, primitives, rigid transforms. TopologyBuilder for snap-grid vertex/edge deduplication. Operation history tree (OpNode) |
| knot-tessellate | Ear-clipping triangulation of BRep face polygons with Newell's method normals and face ID mapping |
| knot-io | STEP AP203/AP214 import and export (hand-written parser + entity mapper). STL binary/ASCII export. glTF 2.0 (GLB) export |
| knot-bindings | wasm-bindgen API: JsBrep, JsCurve, JsSurface, JsSurfaceMesh, plus free functions for primitives, booleans, feature ops, transforms, and I/O |
- Topology-first: Topological decisions use exact predicates (
ExactRational/orient3d) before geometry is approximated. Geometry never drives topology. - Snap-rounded coincidence: All vertex identity uses
LatticeIndex(integer lattice keys fromSnapGrid), never f64 distance thresholds. One model-level grid, no per-entity tolerances. - Fail-or-correct: Every operation returns
KResult<BRep>-- valid result or structured error. The boolean pipeline runsvalidate()on output. No silent garbage. - Immutable + Arc-shared: All geometry and topology is immutable. Operations return new values.
Arc<[T]>for structural sharing. - Enum geometry:
CurveandSurfaceare closed enums enabling exhaustive match for analytical fast-paths in intersection.
# Build (native)
cargo build
# Build (WASM)
wasm-pack build --target web --out-dir pkg
# Run all tests
cargo test --workspace
# Run a single crate's tests
cargo test -p knot-ops --test boolean
# Run boolean reliability harness (~2 min)
cargo test -p knot-ops --test reliability -- boolean_reliability_100 --nocapture
# Run ABC dataset harness (~13 min, requires downloaded data)
cargo test -p knot-io --test abc_harness -- --nocapture --ignored
# Build JS SDK
pnpm build
# Serve web demo
python3 -m http.server 8080use knot_io::from_step;
let brep = from_step(step_file_contents)?;
let solid = brep.single_solid().unwrap();
println!("{} faces", solid.outer_shell().face_count());Supported STEP entities: MANIFOLD_SOLID_BREP, CLOSED_SHELL, ADVANCED_FACE, PLANE, CYLINDRICAL_SURFACE, SPHERICAL_SURFACE, CONICAL_SURFACE, TOROIDAL_SURFACE, B_SPLINE_SURFACE_WITH_KNOTS, LINE, CIRCLE, ELLIPSE, B_SPLINE_CURVE_WITH_KNOTS, SEAM_CURVE, SURFACE_CURVE, INTERSECTION_CURVE. Complex entities (#ID = (TYPE1(...) TYPE2(...))) are handled.
use knot_io::to_step;
let step_string = to_step(&brep)?;
std::fs::write("output.stp", step_string)?;Exports all analytical surface/curve types and NURBS with proper knot compression.
The ABC Dataset provides ~1 million real-world CAD models in STEP format, sourced from Onshape. Knot uses it for integration testing against production CAD data.
One chunk (~10,000 STEP files) is stored in data/abc/0000/ (gitignored). Download with:
./scripts/fetch_abc_chunk.sh 0This downloads chunk 0000 (~635 MB compressed) from the NYU archive, extracts with 7zip, and places files in data/abc/0000/. The archive is kept for re-extraction; delete it manually if disk space is a concern.
ABC tests are #[ignore]-gated so they don't run in normal CI. Run them explicitly:
# Import reliability: loads 200 STEP files, reports success rate
cargo test -p knot-io --test abc_harness -- abc_import_report --nocapture --ignored
# Boolean reliability: imports up to 50 models, runs pairwise booleans
cargo test -p knot-io --test abc_harness -- abc_boolean_reliability --nocapture --ignoredImport harness (abc_import_report):
- Loads 200 STEP files from the ABC chunk
- Reports import success rate, parse failures, and topology failures
- Tracks total face count and average import time per file
- Fault-tolerant: individual face/edge parse failures are skipped with warnings
Boolean harness (abc_boolean_reliability):
- Imports up to 50 models that parse successfully
- Runs pairwise boolean operations (union, intersection, subtraction) on 30 random model pairs
- Each operation has a 10-second timeout and panic-catching wrapper
- Categorizes outcomes: valid, empty (correct), topology failure, tessellation failure, crash, timeout
- Reports overall success rate
The harness reports are the ground truth for whether changes improve or regress reliability. Current baselines:
| Metric | Synthetic Primitives | ABC Dataset |
|---|---|---|
| Success rate | 98% (300 ops) | ~60% (90 ops) |
| Crashes | 0 | 0 |
| Primary gaps | SSI on curved shapes | SSI timeouts, STEP entity coverage |
The full kernel is available from JavaScript/TypeScript via wasm-bindgen:
create_box(sx, sy, sz)-- axis-aligned box centered at origincreate_sphere_brep(cx, cy, cz, r, n_lon, n_lat)-- UV-spherecreate_cylinder_brep(cx, cy, cz, r, h, n_sides)-- z-axis cylindercreate_profile(points, stride)-- planar face from 2D/3D point array
extrude(profile, dx, dy, dz, distance)-- linear extrusionrevolve_brep(profile, ox, oy, oz, ax, ay, az, angle)-- revolutionfillet_edges(brep, edge_points, radius)-- constant-radius filletchamfer_edges(brep, edge_points, distance)-- constant-distance chamfer
boolean_union(a, b),boolean_intersection(a, b),boolean_subtraction(a, b)
.translate(dx, dy, dz),.rotate(ax, ay, az, angle),.scale(sx, sy, sz)
import_step(string)/export_step(brep)-- STEP round-trip.to_stl()-- binary STL bytes.to_glb()-- binary glTF 2.0 bytes.tessellate()-- triangle mesh (positions, normals, indices)
The web/ directory contains an interactive demo with three panels:
- NURBS Curve -- draggable control point weight and position, live curve evaluation
- BRep Primitives -- box/cylinder/sphere with adjustable size, face and triangle counts
- Boolean Operations -- union/intersection/subtraction of box and cylinder with timing
Serve locally:
wasm-pack build --target web --out-dir pkg
python3 -m http.server 8080
# open http://localhost:8080/web/A richer React + react-three-fiber demo lives in examples/react-demo/:
pnpm install
pnpm run demo # builds wasm + js, then runs vite dev serverTwo GitHub Actions workflows live in .github/workflows/:
-
ci.ymlruns on every push tomainand on every PR. Builds the workspace, runs all non-#[ignore]'d tests (synthetic primitives included), and exercises the WASM build viawasm-pack. ABC dataset diagnostics are skipped — they need ~10K STEP files not in the repo. -
deploy-demo.ymlruns on every push tomainand publishesexamples/react-demo/to GitHub Pages.
One-time setup to enable Pages deployment (manual UI step,
required after the first deploy-demo.yml run lands):
- Go to the repository's Settings → Pages.
- Under "Build and deployment", set "Source" to "GitHub Actions".
- Push to
main(or click "Run workflow" on the deploy action). The deployment URL appears in the workflow run summary — typicallyhttps://<user>.github.io/knot/.
The Vite config (examples/react-demo/vite.config.ts) reads
BASE_URL from the environment so asset paths match the GitHub
Pages subpath. Local pnpm dev runs unaffected; the env var only
needs to be set during the CI build.
knot/
Cargo.toml # workspace root
CLAUDE.md # AI assistant instructions
src/ # facade crate (re-exports sub-crates)
crates/
knot-core/ # exact arithmetic, snap grid, errors
knot-geom/ # curves, surfaces, transforms
knot-topo/ # BRep topology, validation
knot-intersect/ # curve/surface intersection
knot-ops/ # booleans, fillet, extrude, primitives
knot-tessellate/ # triangulation
knot-io/ # STEP, STL, glTF I/O
knot-bindings/ # wasm-bindgen API
js/ # TypeScript/React SDK (knot-cad npm package)
web/ # interactive demo
scripts/ # ABC dataset download
data/ # ABC dataset files (gitignored)
TBD