# 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
```
