Docs/CAD

Authoring model

This page is the developer contract behind both workbenches. Read it before changing anything that writes or reads CAD source.

Source is the only truth

A CAD page payload is:

  • source — the FeatureScript-compatible program (a string in the page map)
  • imports — pinned STEP asset references ({ id, name, hash, size })
  • view state (camera, visibility) — UI state, not document state

Everything else is derived by evaluating (source, imports, kernelVersion) in a worker: exact B-rep, tessellation, per-body metrics, the feature history, pipes and terrains, CFD readiness, and the BOM of an assembly. Derived values are cached by content hash and never persisted.

Tool blocks

Every gesture writes a marked block:

featurescript
// cad-tool:hole id=hole1 bodies=all plane=xy offset=6 cx=60 cy=40 radius=6 depth=16 through=true direction=-1
var cadHoleTargets_hole1 = qAllModifiableSolidBodies();
… sketch + opExtrude + opBoolean(subtract) …
// cad-end

The marker line carries the feature's values, so re-opening a feature reads what the program says; editing rewrites the block in place; deleting removes it. Appending and removing are whitespace-symmetric: adding a feature and undoing it is a byte-for-byte no-op. Keep it that way — the undo stack and collaboration rely on it.

The Source layer is pure and unit-tested:

ModuleResponsibility
source-edit.tsTool-block parser, inserter, in-place rewriter, remover.
tooling.tsPart statements + quaternion/Euler maths for every part tool.
assembly-tooling.tsInstances, imported-instance blocks, materials, terrain, pipes.
feature-map.tsHistory row ↔ tool block mapping; collapses a tool's several interpreter ops into one labelled row; edit-context stop points.
parameters.tsNumeric parameter scanning and literal rewriting.

History from execution, not the AST

One statement can produce many features (a loop, or a helper called twice), and feature ids are computed at runtime. So the engine records an execution trace (runFeature(..., { trace: true })) and projects it into the feature tree (projectFeatureList). Rows carry source spans, editability, whether they are structural/reorderable, and honest provenance (loop, repeat, helper, or authored). Queries, math and vector helpers are deliberately not recorded.

Clicking a row sets an editing context: evaluation stops after that feature's last operation and later features are hidden. Closing returns to the current model. This is not rollback — version history and branching are not implemented.

Templates

Documents can derive from shared template documents:

featurescript
// @template Biological tanktainer template

The editor resolves the named project document, passes its source as a kernel include, and selects the document's own exported feature. This keeps a stage document ~25–28 lines instead of copying the base geometry, and lets one change update every derived stage. Module resolution and source maps for includes are not implemented; the chain is name-based and resolved by the editor/renderer.

CFD study

A cad-part can carry a reserved CFD_STUDY map inside a marked block. The payload is strict JSON — also valid FeatureScript map syntax — so the program still parses and runs. Bodies are referenced by name-then-index and faces by geometric anchors (centroid/normal/area), never by B-rep face ids, because ids are only valid within one evaluation. Every evaluation re-resolves anchors and emits CAD-CFD-* readiness diagnostics.

The same anchor convention is a modelling primitive now: an Extrude profile can be a planar face of an existing body, written as qPlanarFaceAt(qNthElement(qAllModifiableSolidBodies(), i), vector(x, y, z) * millimeter, vector(nx, ny, nz)) — a host-query extension (like qAllModifiableSolidBodies) whose anchor is resolved by the exact kernel on every rebuild, with a stale anchor refused rather than re-targeted. A program therefore never carries a B-rep face id, and re-running it from source is enough to rebuild the model.

Kernel and worker

Entry pointHost
@indispec/cad-kernelNode host (OCCT wasm) used by app-server, tests and render tooling.
@indispec/cad-kernel/browserModule Worker host used by the editor: same kernel package, identical meshes.

The editor never imports OCCT on the main thread. The worker owns the wasm, evaluates, tessellates and returns typed arrays (transferable, not copied) plus the derived records. The viewer builds one Babylon mesh per body and rebuilds everything after each evaluation — meshes are never edited in place.

The browser engine is a development capability: shipping the OCCT wasm to users is distribution and is gated on a licensing route. Keep it behind the existing worker boundary so the host can change without touching the language or the UI.

Exports

STEP and STL are produced by the kernel (not by the sheet PDF pipeline). CAD pages are not printable sheets: they export through their own menu. A PNG screenshot captures the viewport as seen. The CFD analysis package (STEP, patch-segmented STL, boundary map, manifest, blockMesh scaffold, README) is a separate ZIP export from the CFD pane.

Persistence and collaboration

CAD pages use the same Yjs page document as every other page. source and imports are last-write-wins strings today, so editing is single-writer and the workbench says so in the status bar. The Y.Text migration (shared with the PCB page) is planned but not implemented. Undo/redo is per-editor and source-based.