Area of the layout bounds — the amount of page the drawing claims. Smaller is denser. Constant time.
Metrics
Metrics :: # (opaque)
Public measurements for comparing layout quality. Every metric is algorithm-independent — it scores positions, routes, or bounds in the shared output vocabulary — so consumers and regression tests can compare any two layouts with the same numbers. All metrics are total and deterministic: any well-typed input has a defined, finite result.
How many times edges visually cross each other — the strongest
single predictor of how tangled a drawing reads. Counts transverse
intersections between segments of different routes; segments within
one route never count against each other, and two segments that only
touch at a point (for example routes attaching to the same node
boundary) or overlap collinearly are not crossings. Routes are
straightened first: lines and polyline pieces score as themselves,
and curve chains are scored by their chords (from to to of each
cubic segment), an approximation that keeps the count exact for the
polyline skeleton a curve smooths. O(s^2) over all segments; a
sweep-based version for large drawings is future work.
How many corners the reader's eye must follow along the edges.
Lines have none; a polyline bends at each interior vertex where the
incoming and outgoing directions differ; a curve chain bends at each
joint where the incoming tangent (to - ctl_b) and outgoing tangent
(ctl_a - from) differ — smooth chains with matching tangents add
nothing. Directions differ when the cross-product magnitude exceeds
1e-9 relative to both segment lengths, or when they reverse
outright. Linear in the total number of route pieces.
The drawing's shape as width over height — near 1 is roughly square, large is a wide banner, small is a tall column. Degenerate rule, chosen to stay finite: a zero-height bounds with zero width (a point or empty layout) reports 1, and a zero-height bounds with positive width (a fully flat layout) reports its width, which still grows the flatter and wider the drawing gets. Constant time.
How many node pairs sit closer than the required clearance — pairs a
reader sees as colliding or crowded. Each node's box is centered on
its position and expanded by gap / 2 on every side; a pair counts
when the expanded boxes strictly overlap on both axes, so boxes that
exactly touch are allowed. Positions and sizes are index-aligned;
indices present in only one list are ignored. O(n^2) over node
pairs.
How far the drawing moved between two layouts of the same nodes —
the stability measure (F7): small numbers mean a reader's mental map
survives a re-layout. Distances are between index-aligned position
pairs; indices present in only one list are ignored, and no pairs at
all reports { mean: 0, max: 0 }. Linear in the number of pairs.
stress : { nodes : List({ width : F64, height : F64 }), edges : List({ from : U64, to : U64 }) }, List({ x : F64, y : F64 }), F64 -> F64
How faithfully drawn distances reflect graph distances — 0 means
every reachable pair sits exactly where the graph structure says it
should; larger values mean the drawing compresses or stretches
relationships. For every unordered pair of mutually reachable nodes,
the ideal distance is the unweighted hop count times unit, and the
pair contributes the squared relative error ((drawn - ideal) /
ideal)^2 (the classic 1/ideal^2 stress weighting). Self pairs,
unreachable pairs, pairs without a position, and pairs whose ideal
distance is not positive and finite contribute nothing, so the
result is defined and finite for any input — this is a metric, not a
validator, and self-loops or out-of-range edge endpoints are simply
skipped. Fewer than two nodes scores 0. O(n * (n + m)) for the
per-node breadth-first searches plus O(n^2) pair summation.