Skip to content
D
Documentation

Models

reference
10 min readUpdated

Import these from @grafloria/engine.

On their own pages

Functions

bumpMutationEpoch

Bump the epoch by hand, for a mutation that legitimately bypasses markDirty() (in-place points rewrites, say). Prefer markDirty().

ts
function bumpMutationEpoch(): number

distanceToSegment

Distance from p to the segment a→b. The primitive both hit-tests are built on.

ts
function distanceToSegment(p: Point, a: Point, b: Point): number

getMutationEpoch

Read the current mutation epoch. Cheap enough to call every frame.

ts
function getMutationEpoch(): number

hasPressure

True when the device gave us pressure worth keeping (i.e. it actually varies).

ts
function hasPressure(points: readonly StrokePoint[]): boolean

linkLabelPosition

The position along the path (0-1) a label resolves to.

slot WINS over position. Naming a slot is an explicit act; position is a required field that most slot users only fill in to satisfy the type, so letting it win would make slot silently do nothing.

ts
function linkLabelPosition(
  label: Pick<LinkLabel, 'position' | 'slot'>
): number

segmentDistance

Minimum distance between two SEGMENTS.

The eraser needs this and a point-test cannot replace it. A pointermove at 60Hz over a fast flick lands samples 80px apart; testing only the sample POINTS lets the eraser jump clean over a stroke it visibly swept through. So the eraser tests the segment it travelled, not the points it happened to land on.

ts
function segmentDistance(a1: Point, a2: Point, b1: Point, b2: Point): number

Constants

DEFAULT_GROUP_HEADER_HEIGHT

ts
const DEFAULT_GROUP_HEADER_HEIGHT: 24

DEFAULT_GROUP_PADDING

Defaults for groups AUTHORED in code (constructor path). A fitted frame needs breathing room and a title band or its label lands under the first member. Loaded documents are exempt: restore assigns the stored values (padding verbatim — possibly undefined, which getPadding() resolves to 0 — and headerHeight number-or-0), so legacy geometry is byte-stable.

ts
const DEFAULT_GROUP_PADDING: 16

DEFAULT_PORT_SNAP_RADIUS

How near a port must be to a point for {@link DiagramModel.findNearestPort} to consider it, in world units. Roughly a fingertip at 100% zoom.

ts
const DEFAULT_PORT_SNAP_RADIUS: 24

DEFAULT_SIMPLIFY_EPSILON

Douglas-Peucker tolerance, in WORLD units.

0.6 is tuned against real traces: below ~0.4 you keep the sensor jitter you were trying to remove; above ~1.2 a deliberate small loop (the dot of an "i", a tick) starts to visibly flatten. At 0.6 a 500-point scribble lands around 40-70 points and is indistinguishable from the raw trace at 100% zoom.

It is world-space, so ink drawn while zoomed OUT is simplified more aggressively in screen terms — which is right: you cannot see detail you did not draw.

ts
const DEFAULT_SIMPLIFY_EPSILON: 0.6

DEFAULT_STROKE_STYLE

ts
const DEFAULT_STROKE_STYLE: StrokeStyle

LINK_LABEL_SLOT_POSITIONS

Where each of the three edge label SLOTS sits along the path.

Pulled IN from the endpoints on purpose: at exactly 0 and 1 a slot label would land under the arrowhead and on top of the port. 0.12 / 0.88 clears both while still reading as "at the start / at the end of this edge".

ONE definition, shared by the model (LinkModel.addLabel), the renderer (LabelRenderer) and the edge optimizer — three places that must never disagree about where a label actually is.

ts
const LINK_LABEL_SLOT_POSITIONS: Record<'start' | 'center' | 'end', number>

Interfaces

ChangeEntry

ts
interface ChangeEntry

Properties

NameTypeDefaultDescription
timestampnumber
propertystring
oldValueany
newValueany

CollapsedState

The reversible snapshot captured when a group collapses. Stored (serialized) on the group so a collapsed diagram round-trips and can be expanded losslessly after a save/load — not just within one session.

ts
interface CollapsedState

Properties

NameTypeDefaultDescription
proxyNodeIdstringThe hidden placeholder node that presents the group as a node endpoint.
savedGeometry?{ position: { x: number; y: number }; size?: { width: number; height: number; depth: number }; bounds?: G...The group's exact geometry before it shrank (restored verbatim on expand).
savedPositionsRecord<string, { x: number; y: number }>Member (node) world positions at collapse time (restored on expand).
hiddenNodesArray<{ nodeId: string; prevVisible: boolean }>Members whose visibility we toggled, with their prior visible value.
removedLinksany[]Serialized links removed at collapse time (internal links + the parallel boundary links that were aggregated away). Re-created verbatim on expand.
proxyLinksArray<{ linkId: string; end: 'source' | 'target'; originalPortId: string; originalNodeId?: string; ...Boundary links that SURVIVED as proxy links: one per (external endpoint) bundle, re-pointed to the placeholder node. Records the original endpoint so expand can restore it, plus how many raw edges it now represents.

DiagramLoadOptions

ts
interface DiagramLoadOptions

Properties

NameTypeDefaultDescription
validate?'off' | 'warn' | 'strict'Structural integrity policy for the incoming document: - 'off' (default) skip validation - 'warn' validate and console.warn a one-line summary with the report - 'strict' validate and throw DiagramValidationError on any error

FitToContentsOptions

Options for {@link GroupModel.fitToContents}.

ts
interface FitToContentsOptions

Properties

NameTypeDefaultDescription
mode?GroupFitModeOverride the group's stored {@link GroupModel.fitMode} for this call.
deepRecursive?booleanDeep-recursive fit: fit every descendant group first (deepest first) so a parent fits around already-fitted children. Requires a diagram.

GroupRect

A resolved rectangle (all four sides present).

ts
interface GroupRect

Properties

NameTypeDefaultDescription
xnumber
ynumber
widthnumber
heightnumber

LaneConfig

Swimlanes & pools as a GENERIC banded group (not BPMN-named). A pool group tiles its child lane groups into bands along one axis; each lane is an ordinary group (so drop-to-assign, membership, constraints all reuse the existing machinery). This is intrinsic band config that round-trips.

ts
interface LaneConfig

Properties

NameTypeDefaultDescription
role'pool' | 'lane''pool' owns the band grid; 'lane' is one band inside a pool.
orientation'horizontal' | 'vertical'Band axis. 'horizontal' → lanes are rows stacked along Y (each spans the pool width). 'vertical' → lanes are columns along X (each spans the height). Set on the pool; lanes carry a copy for convenience.
laneOrder?string[]Pool only: ordered child lane group ids (band order).
headerSize?numberPool only: title-band thickness reserved along the main axis start (left for horizontal pools, top for vertical pools).
weight?numberLane only: relative cross-axis size when not fixed (default 1).
fixedSize?numberLane only: absolute cross-axis size (pins the band, overrides weight).

MembershipLeaf

ts
interface MembershipLeaf

Properties

NameTypeDefaultDescription
fieldstringDot-free key looked up on the node's data map.
op'eq' | 'ne' | 'in' | 'nin' | 'gt' | 'gte' | 'lt' | 'lte' | 'exists' | 'matches'
value?unknownComparison operand (array for in/nin; regex source string for matches).

NearestPortHit

What {@link DiagramModel.findNearestPort} found.

ts
interface NearestPortHit

Properties

NameTypeDefaultDescription
portPortModel
nodeNodeModel
distancenumberDistance from the query point to the port, in world units.

NearestPortOptions

Options for {@link DiagramModel.findNearestPort}.

ts
interface NearestPortOptions

Properties

NameTypeDefaultDescription
radius?numberMaximum distance, in world units (default {@link DEFAULT_PORT_SNAP_RADIUS}).
filter?(port: PortModel, node: NodeModel)Consider only the ports this accepts (e.g. valid targets for the dragged link).
portPosition?(port: PortModel, node: NodeModel)Where a port actually IS. Defaults to the bounding-box edge midpoint. Callers inside a renderer must pass the SHAPE-AWARE resolver (portWorldPosition) — see the note on findNearestPort.

SerializedGroup

ts
interface SerializedGroup extends SerializedEntity

Properties

NameTypeDefaultDescription
namestring
membersstring[]
isCollapsedboolean
bounds?{ x: number; y: number; width: number; height: number }
layoutType?LayoutType
layoutConfig?LayoutConfig
position?{ x: number; y: number }
size?{ width: number; height: number; depth: number }
parentGroupId?string
padding?GroupPadding
headerHeight?number
zIndex?number
fitMode?GroupFitMode
constrainChildren?boolean
collapsedState?CollapsedState
subgraphLayout?SubgraphGroupConfig
laneConfig?LaneConfig
membershipRule?MembershipRule
capacity?number
ts
interface SerializedLink extends SerializedEntity

Properties

NameTypeDefaultDescription
sourcePortIdstring
targetPortIdstring
sourceNodeId?string
targetNodeId?string
pathType'direct' | 'orthogonal' | 'smooth' | 'bezier'
router?LinkRouterNameExplicit routing geometry; absent = derived from pathType.
connector?LinkConnectorNameExplicit polyline rendering; absent = derived from pathType.
pointsPoint[]
segmentsPathSegment[]
labelsLinkLabel[]
state'default' | 'selected' | 'hovered' | 'highlighted'
stylePartial<LinkStyle>
dataRecord<string, any>

SerializedNode

ts
interface SerializedNode extends SerializedEntity

Properties

NameTypeDefaultDescription
positionPoint
sizeSize
rotationnumber
scalePoint
typestring
systemType?string
definitionId?string
parentId?string
childrenstring[]
portsSerializedPort[]
stateNodeState
behaviorNodeBehavior
stylePartial<NodeStyle>
dataRecord<string, any>
positionMode?PositioningMode
transformOrigin?Point
zIndex?numberModel-level stacking order. OMITTED when the node never set one, so every document written before this field existed round-trips byte-for-byte and no schema migration is needed — absence means "unset", not 0.
flexConfig?FlexItemConfig
gridConfig?GridItemConfig
portRenderingConfig?any
dragHandlerConfig?any
connectionGroup?string

SerializedStroke

ts
interface SerializedStroke extends SerializedEntity

Properties

NameTypeDefaultDescription
type'stroke'
pointsStrokePoint[]
styleStrokeStyle
label?stringAn author-supplied name. THE ENTIRE ACCESSIBILITY STORY LIVES ON THIS FIELD — see the a11y note on the renderer's ink layer. Absent for anonymous ink, which is the normal case and is rendered aria-hidden.

StrokePoint

One sample from the pointer.

pressure is 0..1 and OPTIONAL — a mouse does not have any. It is stored only when the device actually reported a varying one (see {@link hasPressure}), because a field that is always 0.5 is noise on the wire and a lie in the model.

It is not decoration: the renderer builds a variable-width outline from it. A pressure that does not change the picture would be exactly the "machinery wired to nothing" this project has shipped in all nine previous waves.

ts
interface StrokePoint

Properties

NameTypeDefaultDescription
xnumber
ynumber
pressure?number0..1. Absent when the device did not report a meaningful one.

StrokeStyle

How the ink looks. Flat and JSON-safe — this crosses the wire as an op payload.

ts
interface StrokeStyle

Properties

NameTypeDefaultDescription
colorstringAny CSS colour.
widthnumberNominal width in WORLD units (so ink zooms with the diagram, like everything else).
opacity?number0..1. Highlighter ink is translucent; a pen is not.

SubgraphGroupConfig

A group's own compound-layout configuration — the subset of the GroupInfo layout contract that is intrinsic to the group and round-trips.

ts
interface SubgraphGroupConfig

Properties

NameTypeDefaultDescription
algorithm?'dagre' | 'elk' | 'grid' | 'inherit' | (string & {})Algorithm for THIS group's contents. 'inherit' uses the parent/default. Any name in the layout registry works here (force, spectral, community, or an extension-registered engine), not just the dagre|elk pair Hard-coded — nested layout resolves the name against the registry. An unknown name falls back to the built-in grid rather than throwing.
fixed?booleanPinned: neither laid out internally nor moved by the parent layout.
layoutOptions?Record<string, unknown>Opaque options forwarded to the chosen layout adapter.

Types

GroupFitMode

How {@link GroupModel.fitToContents} reconciles the freshly computed content rectangle with the group's current rectangle.

  • exact — snap to the content rectangle (default).
  • grow-only — never shrink below the current rectangle (union).
  • shrink-only— never grow beyond the current rectangle (intersection-ish).
ts
type GroupFitMode = 'exact' | 'grow-only' | 'shrink-only';

GroupPadding

Per-side padding (a scalar expands to all four sides).

ts
type GroupPadding =
  | number
  | { top?: number; right?: number; bottom?: number; left?: number };

LinkConnectorName

ts
type LinkConnectorName =
  | 'straight'      // straight segments, hard corners
  | 'rounded'       // straight segments, cornerRadius arcs (the orthogonal look)
  | 'smooth'        // catmull-rom-style smoothing through the points
  | 'bezier'        // cubic bezier between endpoints
  | (string & {});

LinkRouterName

. pathType conflated two independent choices: WHERE the line goes (routing geometry) and HOW the polyline is drawn (connector rendering). They are now two orthogonal, per-link, serializable settings, with pathType kept as the back-compat shorthand that derives both when the explicit fields are absent.

Router names resolve against the engine's RoutingEngine registry, so a custom registered router is addressable per link by its registration name.

ts
type LinkRouterName =
  | 'straight'      // endpoint-to-endpoint, ignores obstacles
  | 'orthogonal'    // HVH/VHV elbows honouring port sides
  | 'manhattan'     // grid search with obstacle avoidance + direction-change cost
  | 'avoid'         // A* obstacle-avoiding router
  | 'elk'           // delegate geometry to ELK's edge router
  | (string & {});

MembershipRule

A SERIALIZABLE declarative membership predicate over a node's data (never eval'd code). Leaves match one field with an operator; branches compose with all/any/not. Kept intentionally small and closed so it round- trips and can be reasoned about / edited as data.

ts
type MembershipRule =
  | MembershipLeaf
  | { all: MembershipRule[] }
  | { any: MembershipRule[] }
  | { not: MembershipRule };

MemberValidation

Predicate used to gate group membership. Return false to reject a candidate entity from joining the group.

ts
type MemberValidation = (candidateId: string, group: GroupModel) => boolean;

Parameters

  • candidateId: id of the node/group being added
  • group: the group the candidate would join

PositioningMode

Positioning mode

  • absolute: Position relative to diagram origin (default, backward compatible)
  • relative: Position relative to parent
  • layout: Position managed by parent's layout algorithm (future)
ts
type PositioningMode = 'absolute' | 'relative' | 'layout';

Was this page helpful?