Skip to content

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

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.

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.