Skip to content
Shapemetry

@huukhanhnguyen/model / schema/sheets

schema/sheets

ComposeArgs

ComposeArgs = { facts: Record<string, string>; operations: (Operation | Group)[]; options: SheetResolveOptions; parameters?: (Operation | Group)[]; sheetKey: string; sheetTitle: string; size: { height: number; width: number; }; template: Template; }

What composeSheet takes — one call's argument list (resolveSheet is its only caller). It stays a declared type rather than a module-local one because every type in this package is declared under schema/ (the declaration-path law, enforced by types/tests/typeDeclarationLaw.test.ts).

facts

facts: Record<string, string>

operations

operations: (Operation | Group)[]

options

options: SheetResolveOptions

parameters?

optional parameters?: (Operation | Group)[]

The sheet's own signature — forwarded to resolveFlatOperations so the isolated scope can bind it (see the hook's doc).

sheetKey

sheetKey: string

sheetTitle

sheetTitle: string

size

size: { height: number; width: number; }

height

height: number

width

width: number

template

template: Template


CropFractions

CropFractions = { bottom: number; left: number; right: number; top: number; }

Viewport crop — fractions 0–1 of the uncropped drawing.

bottom

bottom: number

left

left: number

right: number

top

top: number


CropRegion

CropRegion = { curve: Curve2d; json: string; rings: Point2d[][]; }

The crop AREA, as the region it is — never a box. crop.ts builds one from the fractions above; every clipper reads only this, so a non-rectangular crop is a different argument rather than a different code path.

json and rings are curve in the two forms the kernel doors want. They ride along because the clippers run per segment and per point, and deriving them there would be one wasm call apiece.

curve

curve: Curve2d

The crop boundary. Closed rings; nested rings punch holes (even-odd).

json

json: string

curve as the Curve2d kernel payload, for the boolean door.

rings

rings: Point2d[][]

curve sampled to polygon rings, for the segment / point clippers.


SheetEvaluation

SheetEvaluation = { sheet: Resolved<Page>; status: "complete"; } | { sheet: Resolved<Page>; status: "partial"; warnings: StepFailure[]; }

What Model.evaluateSheet answers with.

A UNION, not a warnings field beside the sheet, because the failure mode being fixed is a consumer that reads .value.data and renders a page that LOOKS finished. A field is ignorable by doing nothing; this shape is not reachable at all without naming which case you are in, so "I forgot to check" becomes a compile error rather than a drawing with a view missing.

Measured (parashape sheetSurface fixture): a literal window drew 80 documents, 6 of them from the group1 step; the same window with an undefined variable drew 74. Those 6 documents vanish, the host then skips a placement that drew nothing, and the placed view is gone from the sheet with a console line as its only trace.

partial still carries the sheet: the drawing that DID compose is worth showing beside "this view could not be placed because: …". What it must not do is arrive looking whole.


SheetResolveOptions

SheetResolveOptions = { evaluateExpression?: (expr) => unknown; materials?: Material[]; resolveComponent?: (key) => Component | undefined; resolveDetail?: (source) => ObjectData[]; resolveFlatOperations?: (operations, parameters?) => ObjectData[]; templates?: Template[]; titleBlock?: Record<string, string>; views?: View[]; }

evaluateExpression?

optional evaluateExpression?: (expr) => unknown

Evaluates sheet.title — an expression like every other authored string (titleValue law: eval, else the literal). Absent (a bare-engine caller with no registry) leaves the title literal.

Parameters

expr

string

Returns

unknown

materials?

optional materials?: Material[]

resolveComponent?

optional resolveComponent?: (key) => Component | undefined

Looks up a components[] entry by key for the template's furniture placeComponent (the same hook sheet operations already use). Absent, only the kernel default title-block component is visible.

Parameters

key

string

Returns

Component | undefined

resolveDetail?

optional resolveDetail?: (source) => ObjectData[]

Forwarded verbatim to each auto-tiled view's own resolveView call — a viewDetail head reads its flat component through it (see ResolveViewOptions.resolveDetail in views.ts). A POSITIONED viewObject (one with at) goes the other way round, through resolveFlatOperations → Model's own view node.

Parameters

source

string

Returns

ObjectData[]

resolveFlatOperations?

optional resolveFlatOperations?: (operations, parameters?) => ObjectData[]

Resolves the sheet's own operations (everything past the extracted auto-tile view nodes — flat lane, authored in sheet mm, y UP from the paper's bottom-left, CAD paper-space convention: viewObject/ tableEntity/imageEntity place shared content by key with their own at/scale) — Model's flat evaluator bound. parameters is the SHEET'S OWN signature, forwarded so the isolated scope those operations evaluate in can bind it (2026-07-26 — a free sheet parameter used to be inert). Passed from here rather than closed over by the caller because drawingSetToSvg runs one callback across every sheet in the set.

Parameters

operations

(Operation | Group)[]

parameters?

(Operation | Group)[]

Returns

ObjectData[]

templates?

optional templates?: Template[]

Templates the sheet may name. Absent + omitted sheet.template = ISO default.

titleBlock?

optional titleBlock?: Record<string, string>

Leftover detector — title-block facts belong on the template or the sheet's placeComponent args. Passing this fails loud.

views?

optional views?: View[]

The model's named views[] lane — a top-level viewObject node with no at arg (the auto-tile bucket, see autoTileViewSpecs) resolves its source key against this.


TiledViewport

TiledViewport = { crop?: CropFractions; scale: number | "auto"; spec: View; }

One auto-tiled view placement on a sheet.

crop?

optional crop?: CropFractions

scale

scale: number | "auto"

spec

spec: View

Last updated: 📖 5 min readEdit on GitHub