Placement & routing
Placement determines boxes; routing makes their relationships readable. They are separate because a good arrangement does not automatically produce traceable connectors.
flowchart LR Spec["PlacementSpec"] --> Dispatch["placeGraph()"] Dispatch --> Layered["layered"] Dispatch --> Clustered["clustered"] Dispatch --> Tree["tree-packed"] Dispatch --> Bespoke["bespoke<br/>banded · radial · grid · sequence"] Layered --> P["Placement"] Clustered --> P Tree --> P Bespoke --> P P --> Shared["shared routes"] P --> Authored["authored routes"] P --> Hybrid["hybrid routes"] Shared --> RP["routePlacement()"] Authored --> RP Hybrid --> RP
Placement output
Section titled “Placement output”Every strategy emits the same Placement: placed nodes and groups, raw route
hints, reserved label positions, self-loop ids, optional notation furniture,
and a discriminated routing ownership value.
Inside shared routing
Section titled “Inside shared routing”flowchart TB Start["routePlacement()"] --> Obstacles["resolve nodes + group chips"] Obstacles --> Session["ChannelRoutingSession"] Session --> Candidates["generate candidates<br/>straight · L · Z · U"] Candidates --> Score["score crossings<br/>obstacles · bends · length"] Score --> Choose["stable best candidate"] Choose --> Refine["redistribute ports<br/>nudge merged lanes"] Refine --> Labels["place labels<br/>most constrained first"]
The session class exists because one routing run shares occupied channels and chosen paths. Candidate geometry, scoring, and refinement live in named modules so their math can be read and tested independently.
The full score order and tuned constants live beside the implementation in
packages/engine/src/route/README.md.