Architecture
nf-metro turns a Mermaid graph LR definition (augmented with %%metro
directives) into a metro-map-style SVG. The pipeline has three stages:
Parse -> Layout -> RenderEach stage hands a single MetroGraph (defined in
src/nf_metro/parser/model.py)
to the next. The graph is mutated in place: parsing fills in stations,
edges, lines, sections, and ports; layout writes coordinates onto those
objects; rendering reads them.
Stages
Section titled “Stages”Parse (src/nf_metro/parser/)
Section titled “Parse (src/nf_metro/parser/)”parse_metro_mermaid in parser/mermaid.py is a line-by-line regex
parser. It reads Mermaid subgraphs (sections), nodes (stations), and
edges, plus the %%metro directive extensions. After the line scan it
runs a post-parse pass that auto-infers layout, then rewrites
inter-section edges into port/junction chains via _resolve_sections.
See Parser for the directive model and the parse-then-resolve flow.
Layout (src/nf_metro/layout/)
Section titled “Layout (src/nf_metro/layout/)”compute_layout in layout/engine.py assigns every station, port,
junction, and section bbox an (x, y). It chains many small phases,
split into an anchor-setting (structural) layer and a content-placement
layer. The phase implementations live in layout/phases/; the
per-phase preconditions, postconditions, and invariants are documented
in
src/nf_metro/layout/CONTRACT.md.
Edge routing (horizontal runs plus 45-degree diagonal transitions, with
L-shaped inter-section routing) lives in layout/routing/.
See Layout pipeline for the full phase-by-phase walkthrough and Routing for the route families.
Render (src/nf_metro/render/)
Section titled “Render (src/nf_metro/render/)”render_svg in render/svg.py draws section boxes, routed edges (with
curved corners), pill-shaped station markers, labels, and the legend
using the drawsvg library. Visual properties come from a Theme
(render/style.py); themes are registered in themes/__init__.py.
render/animate.py adds travelling balls along the routed paths (the
--animate CLI flag).
Reference docs
Section titled “Reference docs”The per-phase preconditions, postconditions, and invariants are in CONTRACT.md. The topology stress fixture inventory is in examples/topologies/README.md.
Data model
Section titled “Data model”The central structure is MetroGraph (parser/model.py), holding:
lines(MetroLine): coloured routes, with an optionalstyle(solid/dashed/dotted).stations(Station): mutable dataclasses; layout writesx,y,layer,trackdirectly onto them.is_portstations participate in layout but are invisible at render time.edges(Edge): directed, each tagged with aline_id.sections(Section): subgraph groupings with adirection(LR/RL/TB), grid position, and bbox.ports(Port): synthetic entry/exit points on section boundaries, created during_resolve_sections.