Skip to content
Shapemetry

B-Rep Documents

The central design decision of the geometry kernel: a B-Rep solid is a JSON document — a string — not an object graph.

Why documents, not objects

  • No aliasing, no invalidation. Every Brep.* door parses the document on entry and returns a fresh document. The input is never mutated, so there is no stale-reference class of bugs and nothing to free.
  • Boundaries are explicit. Documents cross JS↔wasm, main↔worker, and disk unchanged. JSON.stringify/parse is the whole serialization story.
  • Diffable and debuggable. A solid in your debugger or a test fixture is readable JSON, not a pointer.

The cost — parse per call — is deliberately paid at door time; the kernel works on its native structures inside.

Shell v2: four tables

The scene vocabulary type (@huukhanhnguyen/types) is four entity tables and nothing else:

type Shell = {
  vertices: Vertex[]
  edges: Edge[]
  loops: Loop[]
  faces: Face[]
}

One Shell = one shell = one set of connected faces. There is no per-document version field and no root declaring "this is a solid" — solid-ness is derived (Brep.isClosed), never stored.

Version 1 spelled face boundaries with OCCT's words (wires / outerWire / innerWires) and carried a root. Nothing writes v1 anymore, and it is rejected at the door by assertBrepVersion2 — old documents must be regenerated, not upgraded.

One shell vs solid-root Brep

At the wasm boundary the document is a solid-root Brep: four tables plus version: 2 and a shells partition ({ faces: number[] }[]) grouping faces into connected shells. Scene entities stay one-shell Shell. Converters in @huukhanhnguyen/geometry move between the forms:

type Brep = {
  version: 2
  vertices: Vertex[]
  edges: Edge[]
  loops: Loop[]
  faces: Face[]
  shells: { faces: number[] }[]
}
  • toKernelDocument(shell) — one scene shell → Brep JSON string (one shell spanning all faces).
  • fromKernelDocument(document)BrepShell[]. Multi-shell cavity solids come back as several Shell values.

Multi-body files

A STEP file can hold several solid bodies — that is a file-level fact, not a document-level one. Each body is its own document; readStepSolids returns string[], and readStep throws rather than picking one for you. See Import STEP and IGES.

What you do with documents

GoalDoor
Build / editBrep.box, Brep.subtract, Brep.fillet, …
MeasureBrep.volume, Brep.surfaceArea
InspectBrep.isClosed, Brep.freeEdgeCount, Brep.entityCounts
DisplayBrep.tessellateTriangleMesh
ExchangereadStep/writeStep, writeStl, … via @huukhanhnguyen/io

Where next

Last updated: 📖 2 min readEdit on GitHub