Chapter Six

The Fork

Where lines meet, split, and part ways — and why no special code is needed.

We touched on line splitting briefly in Chapter 4 — two lines sharing a corridor, then diverging. But real networks have richer junction patterns. Lines merge from different directions into a shared trunk. Three lines enter a hub, two continue together and one branches off. A line terminates mid-corridor while others continue.

The remarkable thing is that our connect-the-dots system handles all of these without any junction-specific code. Every junction pattern is just a consequence of different lines having different station sequences. Let's prove it by building every common junction type.

Pattern 1: The Y-Split

Two lines share a trunk, then diverge at a junction station. This is the most common pattern — we saw it in Chapter 4. Let's render it cleanly with proper station markers:

Live Editor — Y-Split: two lines diverge

At Junction, both lines arrive together (offset ±3px), then their next stations are in different directions. L1 heads up to Hillside, L2 heads down to Lakefront. The bezier smoothing creates the fork curves automatically — no junction geometry code at all.

Pattern 2: The Merge

The opposite of a split — two lines arriving from different directions and joining into a shared corridor. Structurally identical to a split, just read in the other direction:

Live Editor — Merge: two branches join a trunk

L1 arrives from the north, L2 from the south. At Grand Central they merge into a shared trunk heading east. Again — no merge-specific code. The offset system sees that Hub→Mid has 2 lines, so it spaces them. North→Hub has only L1, so it centers it. The bezier transition handles the rest.

Pattern 3: The Through-Branch

Three lines enter a shared section. Two continue together, one branches off. This is common in real networks where an express line peels away from a local trunk:

Live Editor — Through-branch: one line peels off, two continue

Three lines share the trunk from West End to Exchange. At Exchange, L3 peels away toward the Airport while L1 and L2 continue east. Watch the visual: the three-line corridor narrows to two lines after Exchange, and L3 curves smoothly upward. The station pill at Exchange has three transfer dots; East Side only has two.

All emergent. All from the same rendering functions.

Pattern 4: The Cross

Two lines cross at a station but don't share any other track. This is an interchange, not a shared corridor. The lines arrive from perpendicular directions:

Live Editor — Crossing lines at a transfer station

This is the one case where we do something slightly manual. When two lines cross at perpendicular angles, a single pill can't span both directions. Instead, we draw a larger circular interchange marker with transfer dots arranged along each line's axis.

Most transit networks have only a handful of true perpendicular crossings. The vast majority of interchanges are shared-corridor patterns (types 1-3 above) where the standard pill works perfectly.

When to Use Manual Markers

The automatic drawStation() from Chapter 5 handles 90% of stations. For the remaining 10% — perpendicular crossings, complex hubs with 5+ lines from multiple directions, unusually shaped interchanges — it's fine to hand-draw the marker. Transit maps are a blend of systematic rendering and editorial judgment. The system handles the regular cases; you polish the special ones.

Pattern 5: The Terminus Mid-Corridor

A line ends at a station where other lines continue. The terminating line's track simply stops:

Live Editor — One line terminates, others continue

L1 runs the full length. L2 starts at Market and ends at Garden. L3 starts at Central and runs to the end. The corridor width expands and contracts: 1 line → 2 lines → 3 lines → 2 lines. The station markers adapt automatically — Central has three transfer dots, Market has two, Start has just a dot.

The termini of L2 and L3 happen mid-corridor. Their tracks simply end with the stroke-linecap="round" cap. No special terminus logic needed for the path rendering — the smooth path just starts and stops at the right stations.

All Five Patterns, One System

Let's put them all in perspective. Every junction pattern we've seen is handled by the same three-step process:

1. For each line, look up its station sequence.
2. For each consecutive pair, compute the parallel offset based on how many lines share that segment.
3. Draw a smooth path through the offset points.

Y-splits, merges, through-branches, mid-corridor termini — they're all just different arrangements of station sequences. The rendering system doesn't know about "junctions." It only knows about stations and the lines that connect them.

The only exception is the perpendicular crossing (Pattern 4), which benefits from a custom station marker. Everything else is automatic.

A Reflection

When we started this project, junctions felt like the hardest problem. How do you draw a smooth fork? How do you handle three lines merging from different angles? How do you render a line terminating in the middle of a shared corridor?

The answer turned out to be: you don't. You don't handle junctions at all. You draw each line independently through its station sequence, and the junction geometry emerges from the combination of offsets and bezier smoothing. The "hard problem" dissolved when we stopped trying to solve it directly.

We've now covered every visual element of a transit map: lines, curves, parallel corridors, station markers, and junctions. In the next chapter, we stop using toy examples and render a piece of a realistic network.

Drawing Transit — An open guide to programmatic transit maps