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, and
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 as sections, nodes as stations, and edges,
plus the %%metro directive extensions. After the line scan it runs a
post-parse pass that infers layout, then rewrites inter-section edges into
port and junction chains through _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 an (x, y) to every station,
port, junction and section bbox. It chains many small phases, split into a
structural anchor-setting layer and a content-placement layer. The phase
implementations live in layout/phases/, and the per-phase preconditions,
postconditions and invariants are documented in
src/nf_metro/layout/CONTRACT.md.
Edge routing lives in layout/routing/. It uses horizontal runs plus
45-degree diagonal transitions, with L-shaped inter-section 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 uses the drawsvg library to draw section
boxes, routed edges with curved corners, pill-shaped station markers, labels
and the legend. Visual properties come from a Theme in render/style.py, and
themes are registered in themes/__init__.py. render/animate.py adds
travelling balls along the routed paths, behind 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 optionalstyleofsolid,dashedordotted.stations(Station): mutable dataclasses. Layout writesx,y,layerandtrackdirectly onto them.is_portstations take part in layout but are invisible at render time.edges(Edge): directed, each tagged with aline_id.sections(Section): subgraph groupings with adirectionofLR,RLorTB, plus a grid position and a bbox.ports(Port): synthetic entry and exit points on section boundaries, created during_resolve_sections.