Skip to content
D
Documentation

Canvas — functions

reference
4 min readUpdated

Functions

Import these from @grafloria/renderer.

Functions

applyMatrix

ts
function applyMatrix(m: Matrix, p: Point): Point

arcToCubics

SVG elliptical arc → cubic béziers (endpoint→centre parameterisation, SVG 1.1 appendix F.6). Used by A path commands — the actor shape's head, cylinder caps, jump-point arcs.

ts
function arcToCubics(
  x1: number,
  y1: number,
  rx: number,
  ry: number,
  rotationDeg: number,
  largeArc: boolean,
  sweep: boolean,
  x2: number,
  y2: number
): PathCmd[]

boundsIntersect

ts
function boundsIntersect(a: Bounds, b: Bounds): boolean

boundsUnion

ts
function boundsUnion(a: Bounds | null, b: Bounds | null): Bounds | null

canvasSafety

What would canvas mode cost this diagram?

Note the asymmetry, and that it is the whole design: this question is only ever asked about stepping TO canvas. Going back to SVG can lose nothing — it only ever restores focusable, labelled, HTML-capable DOM — so it is never guarded, never refused, and always allowed.

ts
function canvasSafety(input: CanvasSafetyInput): CanvasSafety

circlePath

ts
function circlePath(cx: number, cy: number, r: number): PathCmd[]

classListOf

The class tokens on an element (className prop, space separated).

ts
function classListOf(props: VNodeProps | undefined): string[]

collectDefinitions

Every id-bearing definition anywhere in the tree: paint servers and filters from <defs>, and the per-node <clipPath>s the label engine emits INLINE (as a sibling of the text it clips). A single recursive pre-pass, because <defs> is appended LAST by the SVG renderer while the elements referencing it are painted first.

ts
function collectDefinitions(root: VNode): Map<string, VNode>

collectEntities

The top-level entities of a rendered tree: every keyed child of the links and nodes layers. These are the units of change — the granularity at which the SVG renderer caches, and therefore the granularity at which identity means "unchanged".

The connection-preview layer is deliberately NOT an entity: it exists only mid-drag, changes every frame, and is handled by {@link previewIsActive}.

ts
function collectEntities(root: VNode): Map<string, VNode>

colorKeyFromPixel

#rrggbb from a picking-canvas pixel.

ts
function colorKeyFromPixel(r: number, g: number, b: number, a: number): string | null

distanceToPath

Shortest distance from p to the path's outline (open OR closed).

ts
function distanceToPath(cmds: PathCmd[], p: Point): number

distanceToSegment

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

ellipsePath

Ellipse centred at (cx, cy) as four cubic segments.

ts
function ellipsePath(cx: number, cy: number, rx: number, ry: number): PathCmd[]

entityOf

node-n1 / link-l1 / port-p3 → the entity that owns the subtree.

ts
function entityOf(vnode: VNode): EntityScope | null

explainHazards

A human-readable account of what a canvas switch would take away.

ts
function explainHazards(hazards: readonly CanvasHazard[]): string

flattenPath

Flatten a command list into polylines. steps controls curve subdivision — 16 segments per curve keeps the deviation well under a pixel for the curve sizes a diagram draws, and hit-testing only ever needs "within tolerance".

ts
function flattenPath(cmds: PathCmd[], steps = 16): SubPath[]

fontString

Build the canvas font shorthand from a resolved style.

ts
function fontString(style: ComputedStyle): string

geometryOf

VNode primitive → path commands, in the element's own local coordinates.

ts
function geometryOf(vnode: VNode): PathCmd[]

linePath

ts
function linePath(x1: number, y1: number, x2: number, y2: number): PathCmd[]

mergeRects

Merge rects that overlap or nearly touch, so we don't clip 40 slivers.

ts
function mergeRects(rects: Bounds[], slack = 8): Bounds[]

multiply

m1 · m2 — apply m2 FIRST, then m1 (the SVG/canvas nesting convention).

ts
function multiply(m1: Matrix, m2: Matrix): Matrix

nextColorKey

Colour keys for the offscreen picking canvas.

The index is spread across the 24-bit colour space with a stride so that adjacent records get FAR-APART colours. That matters: a browser antialiases even the picking pass, and a blended edge pixel between two adjacent keys must not land on a third VALID key. With a large stride, a blend of two keys is overwhelmingly likely to be a colour no record owns — which reads as "miss", not as "wrong entity". Exact-match lookup does the rest.

ts
function nextColorKey(index: number): string

padBounds

ts
function padBounds(b: Bounds, pad: number): Bounds

parseDashArray

"5,5" / "5 5" / [5,5] → [5, 5]. "none" → [].

ts
function parseDashArray(value: unknown): number[] | undefined

parseInlineStyle

Parse an inline style — the renderer emits BOTH forms: a CSS string ("fill: red; stroke-width: 2", from the shape registry and the link style computation) and an object ({ cursor: 'move' }, from the interaction overlays). Both are normalised to kebab-case CSS declarations here.

ts
function parseInlineStyle(style: unknown): Record<string, string>

parsePath

Parse an SVG path d string into {@link PathCmd}s.

Full command coverage (M m L l H h V v C c S s Q q T t A a Z z) including implicit repeated coordinate sets ("M 0 0 10 10" ⇒ moveto + lineto) and the smooth-curve reflection rules. Arcs are converted to cubics.

Written by hand rather than leaned on the DOM (SVGPathElement) so it works headlessly — in Node, in a worker, in a test — which is exactly where the canvas backend has to be provable.

ts
function parsePath(d: string | undefined | null): PathCmd[]

parseTransform

Parse an SVG transform attribute into a matrix.

Supports the forms the renderer actually emits — translate / rotate / scale / matrix — composed left-to-right, exactly as SVG composes them. An unrecognised function is skipped rather than throwing: a transform we cannot read must not take the whole frame down.

ts
function parseTransform(transform: string | undefined | null): Matrix

parseViewBox

"0 0 800 600" → a rectangle.

ts
function parseViewBox(raw: unknown): Rectangle | null

pathBounds

Bounding box of the flattened path. null for an empty path.

ts
function pathBounds(cmds: PathCmd[]): Bounds | null

pointInPath

Non-zero-winding point-in-path — the same fill rule a 2D context uses by default, so "is this point inside the filled shape?" answers identically to "did the fill put a pixel here?".

ts
function pointInPath(cmds: PathCmd[], p: Point): boolean

polyPath

points="1,2 3,4" (or 1 2 3 4) → a polyline / polygon command list.

ts
function polyPath(points: string | undefined, close: boolean): PathCmd[]

previewIsActive

True when the tree carries a live connection / reconnection preview. It has no stable identity and moves with the pointer, so any frame containing one is repainted whole — the honest, correct answer, and it only happens mid-drag.

ts
function previewIsActive(root: VNode): boolean

readCssVarOverrides

Read the --grafloria-* values ACTUALLY COMPUTED on a host element.

This is what makes canvas mode honour a host that overrides the theme through CSS (.dark-mode [data-grafloria-instance] { --grafloria-node-fill: #222 }) — the variable is resolved by the browser's own cascade and handed to the canvas resolver as a concrete value. Returns {} in a headless environment.

ts
function readCssVarOverrides(element: Element | null | undefined): Record<string, string>

rectPath

Axis-aligned rectangle, with optional (possibly asymmetric) corner radii.

ts
function rectPath(
  x: number,
  y: number,
  width: number,
  height: number,
  rx = 0,
  ry = rx
): PathCmd[]

rotation

ts
function rotation(degrees: number, cx = 0, cy = 0): Matrix

scaling

ts
function scaling(sx: number, sy: number = sx): Matrix

textAlignFor

SVG text-anchor → canvas textAlign.

ts
function textAlignFor(anchor: ComputedStyle['textAnchor']): CanvasTextAlign

textBaselineFor

SVG dominant-baseline → canvas textBaseline.

The renderer only ever emits middle (centred labels), hanging (top-aligned text blocks) and baseline; anything else falls back to the canvas default, which is what an unset dominant-baseline means in SVG too.

ts
function textBaselineFor(baseline: string | undefined): CanvasTextBaseline

textLines

The lines a <text> element draws.

Two shapes, both emitted by the shared text-block engine:

  • single line → textContent on the <text> itself,
  • multi line → one <tspan> per line, each with x and a dy offset from the running baseline (the first dy carries the block's vertical alignment). Canvas has no tspan, so the dy chain is accumulated here.
ts
function textLines(vnode: VNode, x: number, y: number): TextLine[]

toNumber

"2px" / 2 / "2" → 2. Returns undefined for anything unparseable.

ts
function toNumber(value: unknown): number | undefined

transformCmds

ts
function transformCmds(cmds: PathCmd[], m: Matrix): PathCmd[]

translation

ts
function translation(dx: number, dy: number): Matrix

Was this page helpful?

Canvas — functions — Grafloria