Inter-section dispatch table
Inter-section edges, running port or junction to port or junction, are routed by _route_inter_section in routing/inter_section_handlers.py.
It chooses the route’s shape from a declarative table, _INTER_SECTION_RULES, rather than from a hand-written if-ladder.
Before emission, every inter-section member is classified from one _InterFacts snapshot.
The route system freezes that family ID, calls the family once to build an immutable RouteMemberGeometryPlan, then emits a fresh copy of that exact template.
Production never takes a second pass through this table.
Every rule’s handler builds its route from a centerline through the bundle builder, meaning build_concentric_bundle or build_tapered_bundle.
No handler assembles per-line points or corner radii by hand.
The runtime curve guard is a backstop rather than the mechanism that keeps the routes correct.
The fact space
Section titled “The fact space”_InterFacts resolves the geometry and topology each rule keys on:
- Relative position: the source and target grid columns and rows (
src_col/row,tgt_col/row), plus the derivedsame_y,same_x,same_col,cross_row, andneeds_bypass. The last of these means a multi-column hop with an intervening section in the source or target row. - Exit side: whether the source is a LEFT or RIGHT exit port, a TOP or BOTTOM perpendicular exit (
is_perp_exit), a TB BOTTOM exit (is_tb_bottom_exit, oris_tb_perp_exit_against_flowwhen it feeds an entry the flow-direction drop cannot reach), or a junction. - Entry side:
entry_sideis the target entry port’s side, one of LEFT, RIGHT, TOP, or BOTTOM, orNonewhen the target is a junction.merge_epis the resolved entry-port station when the target is a merge junction. - Spatial queries: section coordinates and horizontal and vertical crossing checks are resolved through the snapshot, which keeps every predicate and route builder on the same endpoint facts.
The rules
Section titled “The rules”The source claims appear in their canonical classification order. Construction subtracts every earlier claim from each later predicate. Every row therefore owns an exclusive region of the fact space. The runtime rule table is therefore pairwise disjoint and can be reordered without changing which family owns an edge. If no predicate matches, the standard L-shape is the fall-through. Planned template construction resolves its frozen family through the same table’s stable rule ID without evaluating the predicates again, and production emission does not revisit the table or call the family.
| # | Stable family | Rule | Fires when | Route |
|---|---|---|---|---|
| 1 | PERP_EXIT_FAR_SIDE_WRAP | perp-exit -> far-side entry wrap | A TOP/BOTTOM exit feeds a far-side LEFT/RIGHT entry. | _route_perp_exit_farside_entry_wrap continues through the row gap and enters from the port’s outward side. |
| 2 | PERP_EXIT | perp-exit | The source is a TOP/BOTTOM exit on a horizontal section. | _route_perp_exit uses a column drop or up-and-over route. |
| 3 | TB_PERP_EXIT_OVER | TB perp-exit over | A TB/BT trailing exit feeds an entry against its flow. | _route_perp_exit_over uses the opposite-side corridor. |
| 4 | SAME_Y_STRAIGHT | same-Y straight | Endpoints share Y, need no bypass, and neither entry is approached from its far side. | _route_straight_connector emits a horizontal run. |
| 5 | TB_BOTTOM_EXIT_AROUND_STACK | TB bottom exit around stack | A TB bottom-exit drop crosses sections stacked between source and target. | _route_around_stack diverts through a clear gap. |
| 6 | TB_BOTTOM_EXIT | TB bottom exit | The source is a TB/BT bottom exit. | _route_tb_bottom_exit emits the ordinary drop or jog. |
| 7 | TOP_ENTRY_L_SHAPE | TOP entry L-shape | The target entry side is TOP. | _route_top_entry_l_shape supplies the required horizontal lead-in. |
| 8 | BOTTOM_ENTRY_L_SHAPE | BOTTOM entry L-shape | The target entry side is BOTTOM. | _route_bottom_entry_l_shape supplies the mirrored lead-in. |
| 9 | SAME_X_VERTICAL_DROP | same-X vertical drop | Endpoints share X and are not stacked same-side exceptions. | _route_straight_connector emits a vertical run. |
| 10 | BOTTOM_EXIT_JUNCTION_RIGHT_LANDINGS | bottom-exit junction right landings | A fan plan owns the junction’s right-side landings. | _route_bottom_exit_junction_right_landings emits the fan landing template. |
| 11 | BOTTOM_EXIT_JUNCTION_VIA_GAP | bottom-exit junction via gap | The plain junction route crosses a section and a gap detour is available. | _route_bottom_exit_junction_via_gap_leaf emits the detour. |
| 12 | BOTTOM_EXIT_JUNCTION | bottom-exit junction | The source is a bottom-exit junction using the plain shape. | _route_bottom_exit_junction emits the drop-then-turn route. |
| 13 | MERGE_TRUNK_AROUND_BELOW | merge trunk around below | A primary merge trunk has no target-side channel. | _route_merge_trunk_around_below loops under the target. |
| 14 | MERGE_TRUNK | merge trunk | Merge classification names this feeder as the primary trunk. | _route_merge_trunk emits the U-shaped trunk. |
| 15 | MERGE_BRANCH | merge branch | Merge classification names this feeder as a non-trunk branch. | _route_merge_branch_feeder joins the trunk channel. |
| 16 | LEFT_ENTRY_CORRIDOR | LEFT entry corridor | A cross-row LEFT entry needs the clear corridor leaf. | _route_left_entry_corridor emits the corridor route. |
| 17 | BYPASS_L_SHAPE | bypass L-shape | A bypass-classified hop resolves to its L-shaped leaf. | _route_l_shape emits the leaf directly. |
| 18 | BYPASS_LEFT_ENTRY | bypass LEFT entry | A bypass reaches the ordinary LEFT-entry wrap leaf. | _route_left_entry_family emits the wrap. |
| 19 | BYPASS_LEFT_EXIT_AROUND_BELOW | bypass LEFT exit around below | A bypassing LEFT exit must loop below a far-side LEFT entry. | _route_left_exit_around_below_left_entry emits the loop. |
| 20 | BYPASS_CELLMATE_GAP_DROP | bypass cell-mate gap drop | A cell-mate blocks the source-row bypass. | _route_bypass_cellmate_gap_drop uses the gap drop. |
| 21 | BYPASS_PACKED_CELL_SAME_ROW | bypass packed-cell same row | A packed cell-mate stands on a same-row hop’s source or target Y. | _route_bypass_packed_cell_same_row shares the sibling corridor, or takes the row-top one. |
| 22 | BYPASS_RIGHT_ENTRY_CROSS_ROW | bypass RIGHT entry cross-row | A bypass reaches the cross-row RIGHT-entry leaf. | _route_right_entry_cross_row emits the wrap. |
| 23 | BYPASS_FAMILY | bypass family | The member needs the ordinary U-shaped bypass. | _route_u_bypass_family emits the U route. |
| 24 | NEAR_VERTICAL_JUNCTION | near-vertical same-col junction | A junction drops almost straight into a same-column entry. | _route_near_vertical_junction preserves the near-vertical channel. |
| 25 | RIGHT_ENTRY_WRAP | RIGHT entry wrap | A RIGHT entry is fed from the left, or a stacked RIGHT exit feeds it. | _route_right_entry_wrap travels over or around the target’s right side. |
| 26 | LEFT_ENTRY_WRAP | LEFT entry wrap family | A LEFT entry is approached leftward across rows. | _route_left_entry_family selects its remaining named wrap construction. |
| 27 | SERPENTINE_LEFT | serpentine LEFT exit -> LEFT entry | A LEFT exit feeds a stacked same-column LEFT entry. | _route_left_exit_left_entry_drop leads out to a clear left channel. |
| 28 | LEFT_EXIT_FAR_SIDE_WRAP | LEFT exit -> far-side LEFT entry wrap | A LEFT exit reaches a LEFT entry from its far side without a bypass obstacle. | _route_left_exit_around_below_left_entry enters from the outward side. |
| 29 | MERGE_ENTRY_STRAIGHT | merge entry straight | A merge feeder is nearly collinear with its entry port. | _route_merge_entry_straight emits the straight leaf. |
| 30 | MERGE_ENTRY_CORRIDOR | merge entry corridor | A merge feeder must cross a section and a corridor is viable. | _route_merge_entry_corridor emits the corridor leaf. |
| 31 | MERGE_ENTRY_AROUND_BELOW | merge entry around below | A merge feeder must cross a section and has no viable corridor. | _route_merge_entry_around_below loops below the row. |
| 32 | MERGE_ENTRY_PERPENDICULAR | merge entry perpendicular | The resolved merge entry port is TOP or BOTTOM. | _route_merge_entry_perpendicular emits the staircase leaf. |
| 33 | MERGE_ENTRY | merge entry family | The target resolves through the ordinary merge-entry L-shape. | _route_merge_entry_family emits the L-shaped remainder. |
| 34 | RIGHT_ENTRY_PLOUGH_BYPASS | RIGHT entry plough -> bypass | A higher-row route to a RIGHT entry would cross an intervening section. | _route_right_entry_plough_bypass deflects through the bypass. |
| 35 | RIGHT_ENTRY_CROSS_ROW_WRAP | RIGHT entry cross-row wrap | A source above and right of a RIGHT entry travels left with no bypass obstacle. | _route_right_entry_cross_row uses the target-side band or around-below route. |
| - | STANDARD_L_SHAPE | fall-through | No rule matches. | _route_l_shape emits the standard construction. |
The promoted bottom-exit, bypass, and merge-entry leaves each have a stable family ID. Classification, source-turn planning, and production emission therefore all name the same geometry.
The table first runs in classification mode, then its selected families build non-convergence member templates in canonical order. Their gap slots are materialized together, and their occupied vertical channels are frozen before convergence planning. The convergence planner uses those channels as fixed external inputs while it freezes every supported merge trunk, feeder join, continuation, and endpoint owner. It may construct canonical trial routes for the convergence members it owns, but it does not route other members again. Rules 9 and 14 consume those convergence decisions. A continuation covered by a named feeder is suppressed before production emission, with the carrier recorded in the route-system binding. Merge landing and covered-hop cleanup are therefore unnecessary.
The table is the complete inter-section handler inventory. The emission ownership inventory, covering emitters, fallbacks, and validators, is route emission inventory.
The curve guard as a backstop
Section titled “The curve guard as a backstop”assert_render_curve_invariants (routing/invariants.py) runs on every render, and the stage-boundary checks run under validate=True.
Every rule in the table builds its route through the centerline bundle builder, which makes a flipped, pinched, or collinear bundle impossible by construction.
These checks are therefore a thin safety net, and they never fire in normal operation.