Parametron

@parametron/parametric/compute is the standalone compute substrate: pure geometry/value functions with no engine coupling — value in, value out, no registry, no Store, no node classes. It's the surface a node registry wires into NodeDefinition computes (/nodes does exactly that) and the surface another project reuses without adopting the default node catalog. Everything here is namespaced per domain module — method names are only unique within a domain (e.g. entity.reverse vs a different reverse elsewhere), so there is deliberately no flat re-export.

Import

import * as compute from "@parametron/parametric/compute"

compute.vector.add([1, 2, 3], [4, 5, 6])          // [5, 7, 9]
compute.curve.polyline([[0, 0, 0], [10, 0, 0]])   // CurveJSON

or destructure just the domain namespaces you need from the same entry point (there's only one ./compute subpath — no per-domain subpaths):

import { curve, entity, solid } from "@parametron/parametric/compute"

curve.polyline([[0, 0, 0], [10, 0, 0]])

This is exactly how packages/parametric/src/nodesRegistry/wiring.ts wires the stock catalog: import * as compute from "../compute.js", then tables like { curveEntity: compute.curve, faceEntity: { ...compute.face, ...compute.solid, ...compute.surface }, pointEntity: compute.point, vector: compute.vector } map registry entity kinds to the domain that implements them.

Domain catalog

domainrole
axisAxis.* expression namespace — a directed half-line { origin, direction }, direction always unit.
curvecurveEntity producers (polyline, spline, arcs, bezier, rectangle, circle, polygon, textCurve, nurbsCurve), curve modifiers (split/fillet/chamfer), 2D boolean modifiers (curveUnion/Subtract/Intersect, curveSelfUnionImpl), and the Curve.* computation namespace (length, pointAt, tangentAt, divide, …).
entitiesWhole-stream generators/modifiers: clone, entities (literal → renderable), placeModel, and the array/mirror family (arrayLinear, arrayDistances, arrayRadial, arrayCurve, mirror).
entityPer-entity modifiers (1 EntityJSON → 1 EntityJSON): move/rotate/flip/reverse, immutable property setters (material/layer/visible/name), plus whole-array resize/solidSelfUnion/curveSelfUnion/flatten.
faceThe face geometry model ({ loops, uvMatrix }), the 2D boolean/offset kernel bridge (PolyShape), fill, faceOffset, coplanarGroups.
faceValueFace.* computation namespace — pure value math over a face's geometry: area, normal, centroid, contains.
fontopentype.js wrapper: parse, textClean (text → boolean-unioned Shape2d[]), fallback-font registry (setFallbackFont/getFallbackFont).
groupbakeGroup only — explode a groupEntity placement into flat, independently-transformed leaf entities.
parameterParameter (value-node) converters: number/length/area/volume/mass/currency/angle/boolean/string/text/color/fn/font/group/model/object — raw evaluated value → typed JSON.
planePlane.* expression namespace — { point, normal } plus signed distance.
pointPoint.* computation namespace (distance, midpoint, interpolate, translate) plus the pointEntity generator (Point3D[] → PointJSON[] markers).
renderrender only — EntityJSON → SceneEntity (curve polyline / triangulated mesh / point); throws if it's handed an un-baked groupEntity.
solidFace[] topology: the mesh bridge (facesToPolygonMesh/facesToMesh/meshToFaces/nurbsToFaces), extrude/curveFill, 3D CSG (solidUnion/solidSubtract/solidIntersect), faceExtrude/edgeFillet/edgeChamfer/faceLattice, and markWholeArray.
surfaceCurved generators — loft/revolve/patchFromCurve/patchFromCorners build a kernel NurbsSurface, then tessellate to Face[] (a NURBS surface is not a scene entity).
surfaceValueSurface.* computation namespace over a NurbsSurfaceJSON value: pointAt, normalAt, curvature, area, isocurve.
vectorVector.* expression namespace: length, normalize, dot, cross, angle, add, subtract, scale, between.

Every scene entity the graph ever holds is a Point, Curve, or Face (a solid is Face[]) — see Model & pull graph for the entity JSON shapes and Entity API for the full per-entity method table these domains implement.

Value math vs entity generators

Two shapes recur across the domain table:

  • Value namespaces (Point.*, Vector.*, Curve.*, Plane.*, Axis.*, Face.*, Surface.*) compute a VALUE from a value — axis, plane, vector, faceValue, and surfaceValue are pure math with no entity wrapper at all. These are what expression strings call (Vector.cross(a, b)).
  • Entity producers/modifiers (curve, face, solid, surface, point's pointEntity) build or transform renderable { id, geometry, … } JSON. curve/face/surface overlap: curve and faceValue both read geometry a producer in curve/face built.
import * as compute from "@parametron/parametric/compute"

const curve = compute.curve.polyline([[0, 0, 0], [10, 0, 0], [10, 10, 0]])
compute.curve.length(curve)               // > 0
compute.curve.pointAt(curve, 0)           // === startPoint(curve)
compute.curve.divide(curve, 4)            // 5 points, evenly spaced by parameter

Whole-array modifiers

Some ops need the parent's entire output array at once — hole detection (extrude/curveFill's groupLoopsByArea), coplanar grouping (curveOffset/curveThicken/arrayLinear), or n→1 reduction (solidSelfUnion/curveSelfUnion). These are branded with solid.ts's markWholeArray(fn), which stamps fn.wholeArray = true so the engine passes the whole array in one call instead of mapping per-entity. Whole-array exports across the domains: entities.arrayLinear/arrayDistances/arrayRadial/ arrayCurve/mirror, entity.resize/solidSelfUnion/curveSelfUnion/ flatten, curve.curveOffset/curveThicken, solid.extrude/curveFill/ solidUnion/solidSubtract/solidIntersect/faceExtrude/edgeFillet/ edgeChamfer/faceLattice.

Immutability

Compute never mutates its input — per-step caches key on identity. Every modifier returns a new object ({ ...entity, geometry: … }, a fresh FaceJSON/CurveJSON from wrap()) rather than editing the argument in place; entity.transformEntity and friends always build a new geometry array. This is what lets the graph's per-node cache key a step's cached result on its input's identity — mutating an entity in place would silently invalidate every downstream cache keyed on that identity without the graph ever seeing a change. bakeGroup/clone/array modifiers additionally give every emitted copy a fresh id (entityId() / refreshIds) so instanced/cloned geometry never collides in the scene.

The kernel boundary

Curve, face-boolean, and solid-topology domains are thin JSON ⇄ @huukhanhnguyen/shapemetry adapters: curve.ts wraps NurbsCurve, face.ts bridges a face's { loops, uvMatrix } to a 2D PolyShape for booleans/offsets, solid.ts bridges Face[] to a PolygonMesh/half-edge Mesh for CSG and edge ops (facesToPolygonMesh is the cheap render-tessellation tier; facesToMesh/toMesh() is the expensive weld+half-edge promotion topology ops need), and surface.ts builds a kernel NurbsSurface then tessellates to Face[] — NURBS/half-edge mesh are internal kernel concepts, never a scene entity. The graph only ever holds Point/Curve/Face.

See also

  • Node catalog — wires these functions to OperationDefinition.compute/create.
  • Entity API — per-entity method reference (SSOT).
  • Model authoring reference — the authoring guide for the methods these domains implement.
  • Render contract — how render()'s output and bakeGroup's flattened leaves reach a renderer.
  • Model & pull graph — the EntityJSON/CurveJSON/ FaceJSON/PointJSON shapes these functions consume and produce.
📖 5 min read