Architecture
Shapemetry splits into three package tiers with one-way dependency arrows. Domain logic lives in one layer; hosts only orchestrate.
geometry = COMPUTE (geometry kernel — Rust/wasm, numbers in/out)
io = FORMATS (file readers/writers — Rust/wasm, lazy-loaded)
model = RUNTIME (parametric kernel: ModelJSON, evaluate → scene data)
types = shared type contracts (declarations only — sits below all packages)| Package | npm name | Role |
|---|---|---|
packages/geometry | @huukhanhnguyen/geometry | COMPUTE |
packages/io | @huukhanhnguyen/io | FORMATS |
packages/model | @huukhanhnguyen/model | RUNTIME |
packages/types | @huukhanhnguyen/types | shared cross-package type contracts (declarations only) |
model ──► io ──► geometryProduct hosts (e.g. ParaShape) consume geometry + io + model. Model may depend on io + geometry; never on product packages. @huukhanhnguyen/io loads its wasm lazily — a viewer that never imports the package never fetches it.
Geometry public surface
@huukhanhnguyen/geometry is the TypeScript face of crates/geometry:
- Namespace objects —
Point,Vector,Mesh,NurbsCurve,Surface,Shell, …
Grouped over flat snake_case wasm exports byscripts/generate.mjs.
The generated Geometry pages are the live inventory of which namespace owns which function;GEOMETRY_CATALOG.md(repo root) is a historical port-plan document, kept for reference only. - B-Rep documents — JSON strings. Operations return a fresh document string; there is no free registry. Solid-ness is a property of the root, never a type or namespace name.
There is no deep OOP class tree (new Point(...), Face/Edge entity graph as the default API). That was the deleted legacy TS kernel. Callers use namespaces only.
Examples of the live style
import { Brep, Point, Mesh } from '@huukhanhnguyen/geometry'
const box = Brep.box(2, 3, 4)
Brep.volume(box, Brep.measurementDefaultEps())
Point.distanceTo(0, 0, 0, 1, 0, 0)
// Mesh.* takes/returns arena handles — free when done
const m = Mesh.box(1, 1, 1)
// Catmull-Clark in place (each n-gon → n quads per level)
Mesh.subdivide(m, JSON.stringify({ levels: 2 }))
Mesh.free(m)Io and model
Live inventories (generated from package .d.ts / registries — not hand-maintained member lists). Top nav is package-scoped; each package has its own API sidebar:
- Geometry — COMPUTE namespaces (
Shell,Point,Mesh, …) over Rust/wasm. - Io — file readers/writers (STEP, IGES, USD, glTF, STL, …) under
@huukhanhnguyen/io. Wire format between layers is JSON (Shell, triangle meshes), not kernel class instances. Evaluated document vocabulary (Entity, materials, views, sheets) lives on Model. - Model — parametric runtime: parse ModelJSON, build graph,
evaluate()→ scene data. Headless, deterministic, no UI deps. Registry tables: packageREFERENCE.md/ENTITIES.md(AUTOGEN viapnpm gen:docs). - Types — shared cross-package type contracts (
Point,Curve,Surface, …). Declarations only; imports nothing, so every package may depend on it.
Import contract
- Primary compute entry:
@huukhanhnguyen/geometry - File formats:
@huukhanhnguyen/io; evaluated document vocabulary:@huukhanhnguyen/model - Parametric host logic:
@huukhanhnguyen/model(+ optional./nodes,./compute) - Shared cross-package types:
@huukhanhnguyen/types(import typeonly — no runtime) - No deep imports under
dist/or package internals - Coordinates: Z-up
- Renderer-agnostic — tessellate/export, then render with Three.js or anything else
Related
- Getting Started
- Packages
- Geometry · Io · Model · Types
- Examples app:
pnpm dev:examples(http://127.0.0.1:5746)