Skip to content

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 -> Render

Each 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.

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.

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_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.

The per-phase preconditions, postconditions and invariants are in CONTRACT.md. The topology stress fixture inventory is in examples/topologies/README.md.

The central structure is MetroGraph (parser/model.py), holding:

  • lines (MetroLine): coloured routes, with an optional style of solid, dashed or dotted.
  • stations (Station): mutable dataclasses. Layout writes x, y, layer and track directly onto them. is_port stations take part in layout but are invisible at render time.
  • edges (Edge): directed, each tagged with a line_id.
  • sections (Section): subgraph groupings with a direction of LR, RL or TB, plus a grid position and a bbox.
  • ports (Port): synthetic entry and exit points on section boundaries, created during _resolve_sections.