Layout Algorithm

Layout is deterministic and runs once per scope. The stages are graph preparation, layer assignment, layer ordering, coordinate assignment, placement, and orthogonal routing.

Graph preparation

buildGraphModel validates the input, assigns nodes and edges to scopes, infers port counts, and builds compact typed-array indexes for node lookup and render counts. Parallel edges retain their input positions as stable indexes.

Layer assignment

assignLayers scans each scope's topological node order from the end. It assigns each node its longest distance to a sink, then reverses the distances so edges flow from lower layer numbers to higher layer numbers.

Layer ordering

createWorkingLayerGraph inserts one virtual vertex for every intermediate layer crossed by a long edge. Virtual vertices participate in ordering and crossing counts, then disappear when the final layer arrays are written back to original node IDs. Port-aware ordering preserves the connection order needed by the endpoint ports.[1]

Eight iterations each perform a top-down and bottom-up median sort. Neighbor positions are primary, port order is secondary, and stable input or edge order breaks ties. A final adjacent-switch pass keeps a switch when the affected bilayer's crossing count does not increase.

Coordinate assignment

computeBrandesKopfCoordinates runs four combinations of layer direction, within-layer direction, and incoming or outgoing neighbor alignment. It performs conflict marking, median alignment, block compaction, and class-shift accumulation.[2] Complete blocks are placed as units, and class shifts are accumulated in sink order using the published correction.[3]

The four assignments are normalized and combined by the middle-pair average. The result is integer-valued; placement adds the gaps required by labels and ports while preserving layer order.

Placement

placeNodesOnGrid assigns integer grid-unit rectangles. Node width is the maximum of port demand, estimated label width, and the minimum node width. Leaf nodes are three grid units high. Routing-area heights and vertical-channel reserve depend on the layer and edge structure. GRID_UNIT = 12 pixels.

Expanded compound scopes are laid out bottom-up. Parent dimensions include the child layouts, label bar, and inner gaps. Collapsed compounds use leaf dimensions.

Orthogonal routing

Spaces between node layers contain horizontal tracks; gaps between node columns contain vertical channels. Interval occupancy stores node rectangles, track segments, port handles, and vertical reservations without allocating one record per traversed grid cell. The pipe-and-track arrangement follows the layered routing model in [4]; the interval representation is specific to ArrowCrab.

Determinism and verification

Node IDs, ports, and input edge indexes provide stable tie breaks. The same graph and code path produce the same layout, route geometry, and crossing markers. npm run verify:layout checks orthogonality, node-interior avoidance, marker consistency, snapshot round trips, deterministic output, and seeded arbitrary-port cases.