Import these from @grafloria/engine.
Functions
affectedRegion
The nodes allowed to move: the changed set grown by radius hops.
This is what "re-run layout only in the affected region" means concretely — and it is only possible because anchors are now real: the layout works AROUND the frozen nodes instead of laying out over them and being corrected afterwards.
tsfunction affectedRegion(
diagram: DiagramModel,
changed: string[],
radius: number
): Set<string>
alignToPrevious
The translation that minimises total squared displacement between two layouts.
For a pure translation the optimum is exactly the difference of the centroids of the shared nodes — no search, no iteration. This is the single highest-value function in the file: a layered layout is defined only up to translation, so a new node widening rank 0 can shift the ENTIRE drawing sideways. Every node then "moves" although the picture is unchanged, the movement budget blows, and a naive implementation starts fighting its own layout with constraints. Align first; constrain only what is left.
tsfunction alignToPrevious(next: Positions, previous: Positions): { positions: Positions; shift: { x: number; y: number } }
constraintsForStrategy
Note what this does NOT do: it does not emit positions to be clamped afterwards. It emits ANCHORS, which the layered engine honours during coordinate assignment — the whole reason the four scaffolded strategies never worked.
tsfunction constraintsForStrategy(
diagram: DiagramModel,
before: Positions,
options: IncrementalOptions
): SemanticConstraints
measureMovement
Measure how much the mental map was disturbed.
Only nodes that existed BEFORE count: a new node cannot "move", and including it would flatter the numbers exactly when the layout is at its most disruptive.
tsfunction measureMovement(
before: Positions,
after: Positions,
budget: MovementBudget | undefined,
savedByAlignment = 0
): MovementReport
planTween
tsfunction planTween(before: Positions, after: Positions): TweenPlan
Interfaces
IncrementalOptions
tsinterface IncrementalOptions
Properties
| Name | Type | Default | Description |
|---|---|---|---|
strategy? | IncrementalStrategy | ||
changed? | string[] | Node ids the user just added/edited. Everything else is "existing". | |
radius? | number | How far the disturbance is allowed to spread, in graph hops. Default 1. | |
budget? | MovementBudget |
MovementBudget
tsinterface MovementBudget
Properties
| Name | Type | Default | Description |
|---|---|---|---|
maxPerNode? | number | No single node may move further than this. | |
averagePerNode? | number | The mean movement across all nodes that existed before. |
MovementReport
tsinterface MovementReport
Properties
| Name | Type | Default | Description |
|---|---|---|---|
total | number | Sum of the distances every pre-existing node travelled. | |
average | number | ||
max | number | ||
moved | number | How many pre-existing nodes moved at all (beyond a 0.5px epsilon). | |
unmoved | number | Of the pre-existing nodes, how many stayed exactly put. | |
withinBudget | boolean | Did we stay inside the caller's budget? | |
savedByAlignment | number | How much of the movement the centroid re-alignment removed. |
TweenPlan
A tween plan: pure data, so the engine stays free of rAF/DOM/time.
The card asks for "an animated tweened transition from old to new positions instead of snapping". The engine's job is to say WHERE things go at time t; the host's job is to drive t. Keeping it that way is what lets the same code run in a worker, in SSR, and in a test.
tsinterface TweenPlan
Properties
| Name | Type | Default | Description |
|---|---|---|---|
movingIds | string[] | Nodes that actually move — a host can skip the rest entirely. |
Members
at(t: number): Positions— Positions at normalised time t ∈ [0, 1]. Eased.
Types
IncrementalStrategy
Also has every member of String, listed on its own entry.
tstype IncrementalStrategy =
| 'region'
| 'pin-existing'
| 'minimal-shift';
Positions
tstype Positions = Map<string, { x: number; y: number }>;
Properties
| Name | Type | Default | Description |
|---|---|---|---|
size | number |
Members
clear(): voiddelete(key: K): booleanforEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void— Executes a provided function once per each key/value pair in the Map, in insertion order.get(key: K): V | undefined— Returns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map.has(key: K): booleanset(key: K, value: V): this— Adds a new element with a specified key and value to the Map. If an element with the same key already exists, the element will be updated.entries(): MapIterator<[K, V]>— Returns an iterable of key, value pairs for every entry in the map.keys(): MapIterator<K>— Returns an iterable of keys in the mapvalues(): MapIterator<V>— Returns an iterable of values in the map[Symbol.iterator](): MapIterator<[K, V]>— Returns an iterable of entries in the map.readonly [Symbol.toStringTag]: string
Was this page helpful?