Problem
_compute_canvas_bounds() (src/nf_metro/render/svg.py:265, called at svg.py:2305) computes canvas width/height from already-routed content and already-placed chrome - group captions, legend, logo, watermark are placed starting around svg.py:2305, before canvas bounds are finalized. The result is passed as a fixed input into realise_route_reservations(final_canvas=...) (svg.py:2440), which a separate guard, assert_canvas_corridors_hold_their_claims (src/nf_metro/layout/phases/guards.py:6573), checks canvas-side reservations against - warn-only, not enforced.
This is the inverse of how row/column envelopes work: there, the settled reservation determines the extent; here, the extent is measured after the fact and reservations can only report a shortfall.
Why this matters
Same direction as #1789/#1653: settle geometry from reservations rather than measuring it post-hoc. Unlike LEFT/TOP (a pure whole-map-translation problem - see the sibling issue), RIGHT/BOTTOM sizing is genuinely entangled with chrome placement: legend, logo, watermark, and captions are positioned using the canvas bounds that this issue proposes to invert. Making canvas an output here requires reordering or restructuring that placement, not just plumbing a reservation demand through.
What's needed
- Determine whether chrome placement can be made a function of settled route reservations directly (canvas RIGHT/BOTTOM as an output of settlement, chrome placed relative to it) rather than the current bounds-then-chrome-then-final-bounds sequence.
- Once RIGHT/BOTTOM sizing is a genuine settlement output, retire
assert_canvas_corridors_hold_their_claims and the final_canvas= fixed-input parameter to realise_route_reservations.
Scope
Render: src/nf_metro/render/svg.py (_compute_canvas_bounds, chrome placement, realise_route_reservations call site). Layout: src/nf_metro/layout/route_reservations.py, src/nf_metro/layout/phases/guards.py.
Relationship to other issues
Split from #1789 (part of the #1653 architecture programme). Sibling issue (#1880) tracks the LEFT/TOP canvas-margin settlement axis separately, since that half is a pure translation problem rather than one entangled with chrome placement.
Problem
_compute_canvas_bounds()(src/nf_metro/render/svg.py:265, called atsvg.py:2305) computes canvas width/height from already-routed content and already-placed chrome - group captions, legend, logo, watermark are placed starting aroundsvg.py:2305, before canvas bounds are finalized. The result is passed as a fixed input intorealise_route_reservations(final_canvas=...)(svg.py:2440), which a separate guard,assert_canvas_corridors_hold_their_claims(src/nf_metro/layout/phases/guards.py:6573), checks canvas-side reservations against - warn-only, not enforced.This is the inverse of how row/column envelopes work: there, the settled reservation determines the extent; here, the extent is measured after the fact and reservations can only report a shortfall.
Why this matters
Same direction as #1789/#1653: settle geometry from reservations rather than measuring it post-hoc. Unlike LEFT/TOP (a pure whole-map-translation problem - see the sibling issue), RIGHT/BOTTOM sizing is genuinely entangled with chrome placement: legend, logo, watermark, and captions are positioned using the canvas bounds that this issue proposes to invert. Making canvas an output here requires reordering or restructuring that placement, not just plumbing a reservation demand through.
What's needed
assert_canvas_corridors_hold_their_claimsand thefinal_canvas=fixed-input parameter torealise_route_reservations.Scope
Render:
src/nf_metro/render/svg.py(_compute_canvas_bounds, chrome placement,realise_route_reservationscall site). Layout:src/nf_metro/layout/route_reservations.py,src/nf_metro/layout/phases/guards.py.Relationship to other issues
Split from #1789 (part of the #1653 architecture programme). Sibling issue (#1880) tracks the LEFT/TOP canvas-margin settlement axis separately, since that half is a pure translation problem rather than one entangled with chrome placement.