ParaShape

Model

A compiled parametric model: value nodes (the parameters header) plus one flat body fold (the operations list) that evaluates to a scene tree.

Root = container

The root IS a container: its top-level body nodes fold exactly like any nested operations list (producers/containers concat into the root stream, root-level transforms process it, in listed order — rootFoldSlices). It is a DEFINITION — it has no instances lane, because a model carries no transform of its own. Placement is the consumer's job: placeModel/clone supply the instances when this definition is placed inside another model.

Materials and layers

Declared as value nodes in parameters with method:"createMaterial" or method:"createLayer". They register named resources in the Store before evaluation; resolved values are returned in evaluate().materials / evaluate().layers. The toScene() packet references them by name only (bodies resolved from the Store at render time).

{ "method": "createMaterial", "key": "material",
  "args": [{ "key": "name", "input": "'oak'" }, { "key": "baseColor", "input": "'#c8a96e'" }] }

Source: packages/parametric/src/nodes/Model.ts

Properties

NameTypeDescription
idstringUnique model ID (empty string for unsaved models).
namestringDisplay name of the model.
nodesNodeCollection<ChildNode>Flat ordered collection of every node in the model (header value nodes first, then the body fold list — see modelSections).
unitUnitConfigMeasurement unit config (default: { length: "mm" }).
scopeModelScopeExpression evaluation scope shared across all nodes in this model.

Methods

SignatureDescription
convertUnit(unitType: string, newUnit: string): void

Convert all unit-carrying literals in the model from currentUnit to newUnit.

Applies to:

  • Parameter inputs with declared unit types (length/area/volume/mass)
  • Operation arg inputs with any of the above types, including composite arg types that contain length components (point/axis/plane)

Uses dimensional inference to only convert literals that carry physical units (not dimensionless counts or ratios). Cross-param references resolve their own inputs independently, so expressions like "width - 18" correctly convert only the literal 18.

toJSON(): ModelJSONSerialize the model back to a ModelJSON document (v21: the flat runtime collection partitions back into parameters[] / operations[] by lane — value nodes and header containers are parameters, the rest is the body).
evaluate(params: Record<string, unknown>): EvaluateResultEvaluate the model with optional parameter overrides; returns the full render result. Every asset (fonts/images/sub-models) is pre-loaded in the Store, so the graph itself never does async I/O — this call always returns synchronously. A node's OWN registry-supplied compute() may still be maybe-async (see registry/contract.ts): ParaShape's bundled node registry stays 100% synchronous by policy, so this stays true in practice, but the return value can reflect a still-pending node if some OTHER registry's compute hasn't settled yet (it settles later via that node's own notify cascade).
bakeValue(value: unknown[] | undefined): EntityJSON[]Bake a node's value (an EntityJSON[] that may mix leaf entities with nested groupEntity placements) into flat leaf entities, resolving this model's group definitions. A host or test that reads node.value directly uses this to get concrete geometry. Delegates to the scope so a node-tree consumer reaching the same scope via its own .model getter (e.g. a hover preview) shares the identical bake logic.
toScene(params: Record<string, unknown>): SceneJSONProduce a JSON-safe SceneJSON packet for a host renderer (Three.js / SketchUp). Runs the full evaluation first (evaluate() — one shared compute path), then formats the already-computed node values into the pure-geometry SceneJSON tree (materials / layers / fonts referenced by name, bodies resolved from the Store).