fix tracks to all be EW. no more NS tracks

This commit is contained in:
Jesse
2026-08-05 01:00:11 -04:00
parent 821d3cdffa
commit 2d348e1ecf
16 changed files with 1157 additions and 355 deletions
+36 -17
View File
@@ -294,18 +294,24 @@ export function divisionSvg(nodes: DivisionView[]): string {
/**
* The Office Area as a map: one card per grid square, rails drawn edge to edge.
*
* The through rail sits at a constant height on every card — the alignment the printed cards use —
* so abutting cards produce one unbroken line and a gap is visibly a gap.
* THE PRINTED CARD, and nothing else (docs/tracks.png). The through rail runs east-west across the
* DEAD CENTRE of every card, so abutting cards produce one unbroken line and a gap is visibly a
* gap. Everything that leaves through the north or south edge does so at 45°, through the middle of
* that edge — which is what lets two stacked cards read as one continuous diagonal.
*
* The card is 166 x 96, the printed sheet's 259:150 proportions. The aspect is not decoration: the
* frog where a 45° leg meets the through track sits H/2 from the centre, so a squarer card pushes
* it almost to the edge and a turnout's two routes stop being tellable apart.
*/
export function officeSvg(
cells: CellView[],
runningRow: number,
ghosts: { row: number; col: number }[] = [],
): string {
const W = 132;
const W = 166;
const H = 96;
const PAD = 3;
const RAIL = 30;
const RAIL = H / 2;
const esc = (t: string): string =>
String(t).replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[c] ?? c);
@@ -356,8 +362,8 @@ export function officeSvg(
return out;
};
// Where each port meets the card edge. East and west sit at the rail height so a straight run
// stays straight across the whole row; north and south are centred.
// Where each port meets the card edge. East and west sit at the rail height — the card's middle —
// so a straight run stays straight across the whole row; north and south are centred on the edge.
const port = (p: string): { x: number; y: number } => {
if (p === 'e') return { x: W, y: RAIL };
if (p === 'w') return { x: 0, y: RAIL };
@@ -389,15 +395,27 @@ export function officeSvg(
// The rails. A Modifier is not track and deliberately gets none — that is why nothing can be
// routed through it, and the picture should say so.
for (const link of cell.links) {
const a = port(link[0] ?? 'e');
const b = port(link[1] ?? 'w');
const straight = (link === 'ew' || link === 'we') || (link === 'ns' || link === 'sn');
if (straight) {
out += rail(a.x, a.y, b.x, b.y);
const from = link[0] ?? 'e';
const to = link[1] ?? 'w';
const vertical = from === 'n' || from === 's' ? from : to === 'n' || to === 's' ? to : '';
if (vertical === '') {
// The through track: edge to edge along the middle of the card.
out += rail(port(from).x, port(from).y, port(to).x, port(to).y);
} else {
// A diverging leg: run out from each edge and meet, which reads as a curve at this size.
const mid = { x: W / 2, y: RAIL + (H - RAIL) / 2 };
out += rail(a.x, a.y, mid.x, mid.y) + rail(mid.x, mid.y, b.x, b.y);
/**
* A 45° LEG, drawn as the card prints it: along the centre line from the east or west edge
* to the FROG, then out at exactly 45° through the middle of the north or south edge.
*
* The frog is `H/2` from the centre because a 45° run climbing half the card's height
* travels half its height sideways. This used to be a fixed elbow at (W/2, RAIL+(H-RAIL)/2)
* — below the rail on the assumption everything diverged downward — which drew a leg
* reaching north as a hook that dropped past the rail and came back up, and drew nothing at
* any angle the matching rule cares about.
*/
const side = vertical === from ? to : from;
const frog = { x: W / 2 + (side === 'e' ? H / 2 : -H / 2), y: RAIL };
const edge = port(vertical);
out += rail(port(side).x, port(side).y, frog.x, frog.y) + rail(frog.x, frog.y, edge.x, edge.y);
}
}
@@ -453,9 +471,10 @@ export function officeSvg(
}
if (cell.enhancements.length > 0) {
// Above the load pipeline, not on it: an `onCard` enhancement can land on a facility, and at
// the old baseline the label printed straight through the green/MEN|AT|WORK/red squares.
out += `<text class="bs-enh" x="6" y="${H - 46}">${esc(cell.enhancements.join(' · '))}</text>`;
// ABOVE the rail. An `onCard` enhancement can land on a facility, so it has to clear both the
// load pipeline below and the through track — and with the rail now down the middle of the
// card, the old y = H-46 baseline printed the label straight along the rail itself.
out += `<text class="bs-enh" x="6" y="26">${esc(cell.enhancements.join(' · '))}</text>`;
}
if (cell.tray) {
out += `<g class="bs-crew"><rect x="${W / 2 - 26}" y="${RAIL - 10}" width="52" height="20" rx="3"/>` +