1
0

feat: the boards stop being three products

The owner asked twice why there are still three separate boards. The honest
answer, and what this round executes: **it feels like three boards, but not
because the scale jumps 20x — because the three packs draw three different
Californias, and the loudest difference is that the mountains are four times
taller on one of them.**

**THE 20x HORIZONTAL SCALE JUMP IS INVISIBLE**, and measuring that collapsed the
cost of this whole round. `World.project` is a uniform scale in x/z with no
vertical term, and a uniform scale leaves a perspective image identical — so a
camera carried across the seam on matched true-metre offsets draws a
pixel-identical horizontal frame. 1,919 -> 94 m/unit costs nothing to look at.
Rescaling was never the problem. A boot card, a tab strip and a 4.17x vertical
deflation were.

**THE PAUSE WAS MOSTLY FAKE.** A switch covered the screen for 1,715 ms but only
608 ms blocked the main thread; the page drew 46 of 69 frames with nothing to
show, because the outgoing board had already been disposed. `mountCity` now
retains it: the incoming board builds BEHIND a live, interactive picture, and
`stage.setScene` fires only on completion. Measured across all six directions,
three runs each — boot card yes -> **no**, opaque cover 726-1,415 ms -> **0**,
blank frames 21-46 -> **exactly 1**, wall clock down 12-29%, blocked main thread
down 15-47%. A return to a board already seen links **zero** shader programs and
blocks **zero** milliseconds: 298-312 ms of camera flight where it was ~1,600 ms
behind a card. Disposal had been throwing away the shader cache too — linkProgram
ran 38, 59, 78, 109, 127 across five mounts and never reused one.

**The transition is a fog dip, not a crossfade**, through the `setAerialFog` seam
built last round. Every both-boards-live crossfade breaks a budget — ca+sf is
2,640,307 triangles against bay-area's 2,600,000 cap — and a fade never lands
inside the harness's sample window, which is the "a cap you do not measure is a
cap you do not have" failure this repo already argues against. The dip costs zero
triangles and zero draw calls, and it hides the 4.17x deflation, the 4,025 m
projection disagreement and the vanishing 2 km freeway symbols at once, because
all three happen at maximum obscuration. It is also diegetic: a descent through
haze.

The first dip was wrong and the photograph caught it: collapsing to 6% of board
SPAN turned the whole night frame into one flat field — the exact "turns the map
off" failure the risk list named. Re-anchored to 70% of camera STAND-OFF, so the
coastline survives and only the relief melts.

**One ladder, one places list.** 26 authored chapters become 24 rungs sorted
descending by STAND-OFF, not altitude — by altitude they interleave badly and
altitude cannot tell a low oblique from a high plan. The three-board tab strip is
off by default; the left column is now one scrolling list of all 24 rungs under
three region headings that does not change when the board does. Only which row is
lit changes. Label collisions are resolved in the ladder and never in a pack, so
the 29 index-aimed capture guards are untouched.

The minimap stops turning through 90 degrees between boards: every board is
pinned to a rectangle with California's proportions.

**SF and SoCal are not regressed**, and that was the acceptance that mattered:
95.9-98.8% of board pixels are delta-0 against a baseline hash-verified identical
to what the live site serves, and every one of the 34-70 surviving pixels per
frame is an aircraft or a hull.

**A real defect found only by photograph:** `minimap.setMarkers()` had zero call
sites. Every marker on every board was gone — the LA studio's door dot, the Bay
Area's eight company markers — dropped when the minimap went per-board.
Typecheck, tests, budgets and the console were all green with that bug in.

Also fixed: two capture presets that lied. `look.mjs`'s `glyph-la` and `glyph-sf`
claimed California chapter closeups and returned SoCal and Bay Area frames,
because they aimed by chapter index and the indices had moved. Aiming is now by
identity, with a guard test.

NOT SHIPPED, DELIBERATELY: the pack merge. At Bay density it is 34.04M triangles,
13x the highest budget — dead, not a trade. At SoCal density it is 1.99M and fits
today, and the price is San Francisco rendering at 164 m lots instead of 40 m,
i.e. SF looking the way SoCal looks now. SF and SoCal carry every marketing still
on the site. That is the owner's decision and it is worthless as an argument and
decisive as a photograph, so it ships as a measurement artifact with a
side-by-side still and is wired into nothing. The four data reconciliations that
would make one world honest — one exaggeration rule, roads in metres, one
projection centre, one coastline convention — are behind TERA_RECONCILE, default
OFF.

Tests 1,570 -> 1,651, server 295. All ten budget cells pass, no cap raised.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-23 06:20:18 -07:00
parent bcac6aa41a
commit 4bd8481be1
32 changed files with 6628 additions and 264 deletions
+217
View File
@@ -0,0 +1,217 @@
/**
* The board cache: what stays in memory, and what gets thrown away first.
*
* This is the eviction policy behind the thing the owner actually feels. A board
* switch used to cover the screen with an opaque card for 1,711 / 1,079 / 997 ms
* while only 604 / 372 / 222 ms of it blocked the main thread — so two thirds of
* every pause was a live page with nothing to draw, because `mountCity` disposed
* the outgoing board before the incoming one existed. Keeping it is the fix, and
* keeping it means deciding what may be kept.
*
* The properties worth a test are the ones whose failure is silent. An eviction
* that frees the board on screen is a black canvas. An eviction that frees a
* board and forgets to hand it back is a leak of a whole scene graph, because
* `environmentRig`'s ledger is a strong reference and only the caller can call
* `release`. A `put` that quietly drops an existing record is the same leak with
* a different cause. None of those throw.
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { createBoardCache, residentCapacity } from "../engine/boards.ts";
interface Board {
id: string;
}
const board = (id: string): Board => ({ id });
describe("resident capacity is a property of the device and nothing else", () => {
it("keeps every board on a desktop and two on a handheld", () => {
// Three is every board this build has, which is what makes the
// Southland-to-Bay rebuild disappear as well as the return to California.
assert.equal(residentCapacity(false), 3);
// `deviceProfile()` changes only the pixel ratio and the shadow map, so a
// phone carries exactly the same resident geometry as a laptop against a
// much smaller WebGL budget. Two is California plus whichever metro is on
// screen — provably enough, because the two metro rectangles do not
// intersect and California contains both.
assert.equal(residentCapacity(true), 2);
});
});
describe("the cache keeps what it is told to keep", () => {
it("returns a stored board without rebuilding anything", () => {
const cache = createBoardCache<Board>({ capacity: 3 });
const sf = board("sf");
assert.deepEqual(cache.put("sf", sf), []);
assert.equal(cache.get("sf"), sf);
assert.equal(cache.has("sf"), true);
assert.equal(cache.get("socal"), null);
assert.equal(cache.size(), 1);
});
it("never evicts a pinned board, even past its ceiling", () => {
// California is pinned because it is the cheapest board (5.86 MB of GPU
// buffers against the Bay Area's 14.12) and is the root of every descent.
const cache = createBoardCache<Board>({ capacity: 2, pinned: ["california"] });
cache.put("california", board("california"));
cache.put("sf", board("sf"));
const evicted = cache.put("socal", board("socal"));
assert.deepEqual(evicted.map((b) => b.id), ["sf"]);
assert.equal(cache.has("california"), true);
assert.equal(cache.has("socal"), true);
});
it("evicts the least recently shown, not the least recently built", () => {
const cache = createBoardCache<Board>({ capacity: 2 });
cache.put("california", board("california"));
cache.put("sf", board("sf"));
// Going back to California makes the Bay Area the stale one.
cache.touch("california");
const evicted = cache.put("socal", board("socal"));
assert.deepEqual(evicted.map((b) => b.id), ["sf"]);
assert.deepEqual(cache.ids(), ["california", "socal"]);
});
it("never evicts the board being put in", () => {
// The failure this rules out is a black canvas: the incoming board is the
// one about to go on the stage, and a capacity of one would otherwise make
// it its own victim.
const cache = createBoardCache<Board>({ capacity: 1 });
cache.put("california", board("california"));
const sf = board("sf");
const evicted = cache.put("sf", sf);
assert.deepEqual(evicted.map((b) => b.id), ["california"]);
assert.equal(cache.get("sf"), sf);
});
it("hands back everything it drops, exactly once", () => {
// `environmentRig.release(scene)` and `SceneHandle.dispose()` are the
// caller's to run, and it can only run them on a record it is given. A
// dropped record that is never returned is the whole scene graph retained.
const cache = createBoardCache<Board>({ capacity: 2 });
const dropped: string[] = [];
for (const id of ["a", "b", "c", "d", "e"]) {
for (const victim of cache.put(id, board(id))) dropped.push(victim.id);
}
assert.deepEqual(dropped, ["a", "b", "c"]);
assert.deepEqual(cache.ids(), ["d", "e"]);
});
it("hands back a record it is replacing in place", () => {
const cache = createBoardCache<Board>({ capacity: 3 });
const first = board("sf");
const second = board("sf");
cache.put("sf", first);
assert.deepEqual(cache.put("sf", second), [first]);
assert.equal(cache.get("sf"), second);
assert.equal(cache.size(), 1);
});
it("does not report a board as evicted when it is put back unchanged", () => {
const cache = createBoardCache<Board>({ capacity: 3 });
const sf = board("sf");
cache.put("sf", sf);
assert.deepEqual(cache.put("sf", sf), []);
assert.equal(cache.get("sf"), sf);
});
it("takes one out on request and leaves the rest alone", () => {
const cache = createBoardCache<Board>({ capacity: 3 });
const sf = board("sf");
cache.put("california", board("california"));
cache.put("sf", sf);
assert.equal(cache.remove("sf"), sf);
assert.equal(cache.remove("sf"), null);
assert.deepEqual(cache.ids(), ["california"]);
});
it("drains everything, including the pinned board", () => {
const cache = createBoardCache<Board>({ capacity: 3, pinned: ["california"] });
cache.put("california", board("california"));
cache.put("sf", board("sf"));
assert.deepEqual(cache.drain().map((b) => b.id), ["california", "sf"]);
assert.equal(cache.size(), 0);
});
it("treats a capacity below one as one rather than throwing", () => {
const cache = createBoardCache<Board>({ capacity: 0 });
const sf = board("sf");
assert.deepEqual(cache.put("sf", sf), []);
assert.equal(cache.get("sf"), sf);
});
});
describe("the two-deep case the product actually runs", () => {
it("never makes the board on screen the victim, whatever the order", () => {
/*
* The bug this is here for was live and is the worst shape retention can
* fail in. With a ceiling of two and California pinned, inserting the
* *incoming* board while the outgoing one was still on the stage left the
* outgoing board as the only legal victim — so a Bay-to-Southland switch on
* a phone disposed the picture the visitor was looking at, blanked the
* canvas, and only then swapped. `main.ts` fixes it by writing the cache
* after `stage.setScene`, which makes the least-recently-*shown* board the
* victim; this asserts the property the cache itself guarantees, which is
* that the id being put is never evicted.
*/
const cache = createBoardCache<Board>({
capacity: residentCapacity(true),
pinned: ["california"],
});
cache.put("california", board("california"));
const sf = board("sf");
cache.put("sf", sf);
// The order `main.ts` now uses: the incoming board is shown first, so it is
// the id being put and the outgoing one is the stale entry.
const socal = board("socal");
const evicted = cache.put("socal", socal);
assert.deepEqual(evicted, [sf], "the board that has just been hidden is the victim");
assert.equal(cache.get("socal"), socal, "and the one on screen is still resident");
});
it("rebuilds nothing on a Bay -> Southland -> Bay round trip on a handheld", () => {
// California pinned, one metro slot. Coming back to the Bay Area rebuilds
// it — that is the honest cost of two-deep on a phone — but coming back to
// California, which is the root of every descent, never does.
const cache = createBoardCache<Board>({
capacity: residentCapacity(true),
pinned: ["california"],
});
const california = board("california");
cache.put("california", california);
cache.put("sf", board("sf"));
const droppedForSocal = cache.put("socal", board("socal"));
assert.deepEqual(droppedForSocal.map((b) => b.id), ["sf"]);
// Back to California: resident, so nothing is built and nothing is dropped.
assert.equal(cache.get("california"), california);
cache.touch("california");
assert.equal(cache.has("socal"), true);
});
it("rebuilds nothing at all on a desktop, in any order", () => {
const cache = createBoardCache<Board>({
capacity: residentCapacity(false),
pinned: ["california"],
});
const records = new Map<string, Board>();
let rebuilt = 0;
const visit = (id: string) => {
if (cache.has(id)) {
cache.touch(id);
return;
}
rebuilt++;
const record = board(id);
records.set(id, record);
assert.deepEqual(cache.put(id, record), []);
};
for (const id of ["california", "sf", "socal", "california", "sf", "socal", "sf"]) visit(id);
assert.equal(rebuilt, 3, "three boards exist, so three builds and no more");
for (const [id, record] of records) assert.equal(cache.get(id), record);
});
});
+235
View File
@@ -0,0 +1,235 @@
/**
* The chapter strip is a public interface, and this is the fast half of the
* check that says so.
*
* `scripts/check-chapter-identity.mjs` is the whole contract: it boots the built
* bundle under every `?city=` and compares the rendered `#chapters` strip —
* `data-view`, `data-view-index`, the printed number and the short label —
* against `scripts/fixtures/chapter-identity.json`. That takes a browser and
* about a minute. This file re-states the part of the same fixture that lives in
* the packs, so a reorder fails in `npm test` in a second, and so the negative
* case can be *watched failing*: a guard nobody has seen fail is not a guard.
*
* ## What is actually being protected
*
* Twenty-six aims in `scripts/brand-assets/{shots,films,capture}.mjs` point a
* camera at `#chapters .chapter[N]` — a **position** — and assert the short
* label they find there; 21 of them at a board a `?city=` reaches. Every one is
* a still, a film or the Open Graph card on lumbridgecorp.com. The
* label assertion was added after a share card shipped for a fortnight showing
* a chase camera on a freeway under the headline "Cities from above", and it is
* a good guard with two holes:
*
* - Short labels are not unique across boards. "Whole Board" is chapter 01 of
* both metro boards and "The Valley" is a different valley on each of them, so
* a reorder that preserves labels passes every one of them and shoots the wrong place.
* - `?city=` falls back to the first board rather than failing, so a board that
* lost its id serves California to a shot aimed at SoCal without an error.
*
* Identity here therefore means `(board, chapter id, position)` together, never
* a label and never a position on its own.
*/
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { describe, it } from "node:test";
import CALIFORNIA_CITY from "../cities/california.ts";
import SAN_FRANCISCO_CITY from "../cities/sf.ts";
import SOCAL_CITY from "../cities/socal.ts";
import type { Chapter } from "../engine/types.ts";
import { chromeState } from "../ui/chromeState.ts";
import type { ChromeInputs } from "../ui/chromeState.ts";
interface FixtureChapter {
index: number;
dataView: string;
dataViewIndex: string;
number: string;
shortLabel: string;
}
interface Fixture {
takenAt: string;
boards: Record<string, { activeBoard: string; chapters: FixtureChapter[] }>;
}
const fixture = JSON.parse(
readFileSync(new URL("../../scripts/fixtures/chapter-identity.json", import.meta.url), "utf8"),
) as Fixture;
/** `?city=` value to the pack it selects — the same table `main.ts` holds. */
const BOARDS: readonly (readonly [string, { chapters: Chapter[] }])[] = [
["california", CALIFORNIA_CITY],
["sf", SAN_FRANCISCO_CITY],
["socal", SOCAL_CITY],
];
/**
* Every way this list of chapters differs from the one the imagery was aimed at.
*
* A list of sentences rather than an assertion, so that the negative test can
* call the same function on a deliberately broken list and check that it
* *reports* rather than that it *throws*. The two callers below are the whole
* point: one proves the packs are still what the fixture says, the other proves
* this function can tell when they are not.
*/
function drift(boardId: string, chapters: readonly Chapter[]): string[] {
const expected = fixture.boards[boardId];
if (expected === undefined) return [`?city=${boardId}: not in the fixture`];
const problems: string[] = [];
if (expected.chapters.length !== chapters.length) {
problems.push(
`?city=${boardId}: ${chapters.length} chapters, the fixture has ${expected.chapters.length}`,
);
}
for (let i = 0; i < Math.min(expected.chapters.length, chapters.length); i += 1) {
const want = expected.chapters[i];
const got = chapters[i];
if (want === undefined || got === undefined) continue;
// `data-view` is the chapter's id, and `data-view-index` is its position in
// the rendered list — `mount.ts` writes both from the same array. Checking
// them here is checking that the DOM identity and the pack identity are the
// same fact, which is what lets a browser-free test stand in for the DOM one.
if (got.id !== want.dataView) problems.push(`${boardId}[${i}] id ${got.id}${want.dataView}`);
if (String(i) !== want.dataViewIndex) {
problems.push(`${boardId}[${i}] position ${i}${want.dataViewIndex}`);
}
if (got.number !== want.number) {
problems.push(`${boardId}[${i}] number ${got.number}${want.number}`);
}
if (got.shortLabel !== want.shortLabel) {
problems.push(`${boardId}[${i}] label "${got.shortLabel}" ≠ "${want.shortLabel}"`);
}
}
return problems;
}
function inputs(overrides: Partial<ChromeInputs> = {}): ChromeInputs {
return {
mode: "overview",
available: ["overview"],
access: { tier: "anon", signInUrl: "/login.html", subject: null },
inside: false,
officeDepth: null,
viewport: { width: 1440, height: 900, coarsePointer: false },
feeds: { markers: false, weather: false, flights: false },
degraded: [],
devices: [],
firstVisit: false,
panelOpen: true,
planOpen: true,
...overrides,
};
}
describe("chapter identity", () => {
it("matches the fixture the marketing imagery was aimed at", () => {
const problems = BOARDS.flatMap(([id, city]) => drift(id, city.chapters));
assert.deepEqual(
problems,
[],
`The chapter strip moved. Every line below is a capture aim in ` +
`scripts/brand-assets/** that now points somewhere else:\n ${problems.join("\n ")}`,
);
});
it("is 26 rungs over three boards, which is the number the ladder has to carry", () => {
// Not a style assertion: the ladder work re-expresses these 26 as one
// ordered descent, and 24 of them are camera poses — `los-angeles` and
// `san-francisco` on the state board are doors that call `switchCity`
// rather than `flyTo`, which is why two capture presets that claimed to be
// California closeups were in fact photographs of the metro boards.
assert.equal(CALIFORNIA_CITY.chapters.length, 6);
assert.equal(SAN_FRANCISCO_CITY.chapters.length, 12);
assert.equal(SOCAL_CITY.chapters.length, 8);
assert.equal(
BOARDS.reduce((total, [, city]) => total + city.chapters.length, 0),
26,
);
});
it("puts the pack's id and short label straight onto the button", () => {
// The DOM contract, stated where it can be checked without a browser:
// `chromeState` maps a chapter to a row `id`/`label`, and `mount.ts` writes
// that `id` into `data-view` and the row's position into `data-view-index`.
// If this mapping ever stops being the identity function, the fixture above
// stops standing in for the DOM and `check-chapter-identity.mjs` becomes the
// only real guard.
for (const [boardId, city] of BOARDS) {
const state = chromeState(
inputs({ views: city.chapters, activeViewId: city.chapters[0]?.id ?? null }),
);
assert.deepEqual(
state.views.map((view) => view.id),
city.chapters.map((chapter) => chapter.id),
`${boardId}: row ids are not the pack's chapter ids`,
);
assert.deepEqual(
state.views.map((view) => view.label),
city.chapters.map((chapter) => chapter.shortLabel),
`${boardId}: row labels are not the pack's short labels`,
);
assert.deepEqual(
state.views.map((view) => view.number),
city.chapters.map((chapter) => chapter.number),
`${boardId}: printed numbers are not the pack's numbers`,
);
}
});
it("cannot be guarded by short label alone, because labels collide across boards", () => {
// This is the reason the index-and-label aims are not enough, stated
// as a fact about the data rather than as an opinion. If this assertion ever
// fails because the labels became unique, the aims are still aimed by
// position and this file still has to exist — but the failure mode gets
// quieter, which is worth knowing about.
const labels = BOARDS.flatMap(([, city]) => city.chapters.map((chapter) => chapter.shortLabel));
const collisions = labels.filter((label, i) => labels.indexOf(label) !== i);
assert.deepEqual([...new Set(collisions)].sort(), ["The Valley", "Whole Board"]);
});
describe("the guard itself", () => {
// A guard nobody has watched fail is not a guard. Each of these breaks the
// list in one of the three ways a ladder rewrite realistically breaks it,
// and asserts that `drift` says so.
it("fails when two chapters are swapped, even though every label survives", () => {
const swapped = [...SAN_FRANCISCO_CITY.chapters];
const [a, b] = [swapped[2], swapped[3]];
assert.ok(a !== undefined && b !== undefined);
swapped[2] = b;
swapped[3] = a;
const problems = drift("sf", swapped);
assert.ok(problems.length > 0, "a reorder went unnoticed");
assert.ok(
problems.some((line) => line.includes("sf[2] id")),
`expected a complaint about sf[2], got:\n ${problems.join("\n ")}`,
);
});
it("fails when a chapter is inserted, which shifts every index after it", () => {
const inserted: Chapter[] = [...SOCAL_CITY.chapters];
const first = inserted[0];
assert.ok(first !== undefined);
inserted.splice(1, 0, { ...first, id: "san-diego", shortLabel: "San Diego", number: "99" });
const problems = drift("socal", inserted);
assert.ok(problems.length > 0, "an insertion went unnoticed");
assert.ok(
problems.some((line) => line.includes("9 chapters")),
`expected a complaint about the count, got:\n ${problems.join("\n ")}`,
);
});
it("fails when a chapter keeps its place and changes its id", () => {
// The ladder rewrite's most likely shape: same 26 rungs, renamed to a
// single sequence. `data-view` is what a re-pointed harness would key on,
// so a silent rename is the one drift that would otherwise look like
// nothing at all.
const renamed = CALIFORNIA_CITY.chapters.map((chapter, i) =>
i === 1 ? { ...chapter, id: "rung-02" } : chapter,
);
const problems = drift("california", renamed);
assert.deepEqual(problems, ["california[1] id rung-02 ≠ la-sf-us-101"]);
});
});
});
+158
View File
@@ -0,0 +1,158 @@
/**
* The fog dip: the transition that is not a crossfade.
*
* Two boards cannot be drawn at once and that is a measured fact rather than an
* engine limit. Against the caps in `scripts/performance-budgets.json`,
* California + the Bay Area is 2,640,307 triangles against a cap of 2,600,000,
* California + the Southland is 1,805,344 against 1,700,000, and the two metros
* together are 3,694,949 against everything; rendering only the outgoing board's
* terrain still breaks the California direction at 586,535 against 440,000. And
* `performance-budget.mjs` samples eight seconds after a three-second warm-up,
* so a sub-second fade never lands inside the window — a transition whose cost
* the gate cannot see is the exact failure this repo argues against.
*
* So the transition is a collapse of the outgoing board's own aerial fog, a
* swap, and a lift of the incoming board's. Zero extra triangles, zero extra
* draw calls, no render target — and it is also the *right* transition, because
* a true dissolve at a matched pose would put the seam's 4.17x vertical
* deflation and its up-to-4,025 m registration slide on screen simultaneously
* and in register.
*
* What is testable without a GPU is the arithmetic, and the arithmetic is where
* the two failures live: a dip that does not actually reach either endpoint, and
* a dip whose collapsed pair is shared between boards whose scene units differ
* by a factor of twenty.
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
collapsedFog,
dipFog,
FOG_DIP_IN_SECONDS,
FOG_DIP_OUT_SECONDS,
FOG_DIP_REACH_FRACTION,
type AerialFog,
} from "../engine/atmosphere.ts";
/** A clear-day pair of the shape `Atmosphere.aerial` returns for the Bay Area. */
const CLEAR: AerialFog = { near: 1_150, far: 3_900 };
describe("the collapsed pair is measured against the camera, not the board", () => {
it("scales with how far back the camera is standing", () => {
// Fog planes are scene distances and California's unit is twenty times the
// Bay Area's, so one shared pair would leave one board unfogged and wash the
// other out completely.
const wide = collapsedFog(808);
const close = collapsedFog(41);
assert.ok(wide.far > close.far);
assert.equal(wide.far, 808 * FOG_DIP_REACH_FRACTION);
assert.equal(close.far, 41 * FOG_DIP_REACH_FRACTION);
});
it("leaves the near limb of the board faintly readable and everything else gone", () => {
// The failure this replaced: a far plane at 6% of the board's span put every
// fragment past it, and the night board photographed as one flat field of
// #16203a with no coastline in it. `fog_fragment` is linear between the
// planes, so a far plane at 70% of the stand-off means the fragment nearest
// the camera — roughly half a board-span nearer than the target — is heavily
// hazed rather than erased.
const standoff = 808;
const fog = collapsedFog(standoff);
const nearest = standoff - 554 / 2;
const factor = (nearest - fog.near) / (fog.far - fog.near);
assert.ok(factor > 0.8, `the near limb is hidden (${factor})`);
assert.ok(factor < 1, `but it is not erased (${factor})`);
// Everything at or past what the camera is pointed at is gone outright.
assert.ok((standoff - fog.near) / (fog.far - fog.near) >= 1);
});
it("closes to just in front of the camera, and not onto it", () => {
const fog = collapsedFog(808);
assert.ok(fog.near > 0, "a positive near plane is what keeps the whole dip on one curve");
assert.ok(fog.near < fog.far);
});
it("refuses to produce a degenerate pair for a degenerate reach", () => {
for (const reach of [0, -1]) {
const fog = collapsedFog(reach);
assert.ok(fog.far > 0, `reach ${reach} still yields a usable far plane`);
assert.ok(Number.isFinite(fog.far));
}
});
});
describe("a dip reaches both ends and nothing beyond them", () => {
const hidden = collapsedFog(808);
it("is exactly the start at zero and exactly the end at one", () => {
assert.deepEqual(dipFog(CLEAR, hidden, 0), CLEAR);
assert.deepEqual(dipFog(CLEAR, hidden, 1), hidden);
// The lift is the same call with the endpoints swapped, which is why there
// is one function and not two.
assert.deepEqual(dipFog(hidden, CLEAR, 0), hidden);
assert.deepEqual(dipFog(hidden, CLEAR, 1), CLEAR);
});
it("clamps rather than overshooting a caller's clock", () => {
assert.deepEqual(dipFog(CLEAR, hidden, 1.4), hidden);
assert.deepEqual(dipFog(CLEAR, hidden, -0.2), CLEAR);
assert.deepEqual(dipFog(CLEAR, hidden, Number.NaN), CLEAR);
});
it("closes monotonically, and never leaves the board briefly clearer", () => {
// A fog that opened even slightly partway through the collapse would be a
// flash of the outgoing board at exactly the moment the swap is meant to be
// invisible.
let previous = Number.POSITIVE_INFINITY;
for (let t = 0; t <= 1.0001; t += 0.02) {
const far = dipFog(CLEAR, hidden, t).far;
assert.ok(far <= previous + 1e-9, `far grew at t=${t}: ${far} after ${previous}`);
previous = far;
}
});
it("fades in ratios rather than in metres", () => {
// Fog distance is perceived logarithmically. A linear ramp spends the first
// half of the dip doing almost nothing visible and the second half slamming
// shut. The ease is symmetric, so halfway through the dip the far plane is
// exactly the *geometric* mean of the two ends — which is the statement that
// this is a ratio fade and not a distance fade.
const mid = dipFog(CLEAR, hidden, 0.5).far;
assert.ok(
Math.abs(mid - Math.sqrt(CLEAR.far * hidden.far)) < 1e-9,
`midpoint ${mid} is not the geometric mean of ${CLEAR.far} and ${hidden.far}`,
);
assert.ok(mid < (CLEAR.far + hidden.far) / 2, "and it is below the arithmetic mean");
assert.ok(mid > hidden.far, "and it has not arrived early either");
});
it("keeps the pair coherent at every step", () => {
// The failure this catches is real and was found by writing it: a linear
// near plane against a geometric far plane crosses over partway through the
// dip, and `scene.fog` with `near > far` draws nothing coherent.
for (let t = 0; t <= 1.0001; t += 0.1) {
const fog = dipFog(CLEAR, hidden, t);
assert.ok(Number.isFinite(fog.near), `near is finite at t=${t}`);
assert.ok(Number.isFinite(fog.far), `far is finite at t=${t}`);
assert.ok(fog.near >= 0);
assert.ok(fog.far >= fog.near, "a far plane inside the near plane draws nothing");
}
});
});
describe("the two halves are timed differently on purpose", () => {
it("leaves faster than it arrives", () => {
// Leaving is an instruction the visitor just gave and wants obeyed; arriving
// is a picture they are being shown.
assert.ok(FOG_DIP_OUT_SECONDS < FOG_DIP_IN_SECONDS);
});
it("fits inside the pause it is hiding", () => {
// The whole transition is 0.8 s. The switch it covers was measured at
// 997-1,711 ms of opaque card, of which 222-604 ms blocked the main thread —
// so the dip is on the order of the thing it replaces rather than an
// addition to it.
assert.ok(FOG_DIP_OUT_SECONDS + FOG_DIP_IN_SECONDS <= 1.0);
});
});
+358
View File
@@ -0,0 +1,358 @@
/**
* The ladder: one descent over three boards, and the rule that hands the camera
* from one to the next.
*
* Two things are being protected here and only one of them is the arithmetic.
*
* The first is **containment**. The ladder is a new view over the packs, never a
* replacement for them: twenty-nine capture guards in `scripts/brand-assets` aim
* at `#chapters .chapter` by index and assert the button's short label, and every
* still on lumbridgecorp.com is shot that way. So this file asserts that no rung
* carries a short label, a number or an id the pack did not, and that the packs'
* own chapter arrays are untouched — a ladder that could only be built by editing
* a pack is a ladder that re-shoots the marketing site.
*
* The second is **oscillation**. `handover` is shipped pure and tested here
* before it is ever allowed to fire on a wheel notch, because a camera parked at
* a threshold without a hysteresis band tears down and rebuilds a board on every
* event, and that is not a bug you find by reading the function.
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
activeRung,
BOARD_REGION,
buildLadder,
chapterAltitudeMetres,
chapterStandoffMetres,
DEMOTE,
handover,
handoverStandoff,
PROMOTE,
placesRows,
REGION_LABELS,
type HandoverBoard,
} from "../engine/ladder.ts";
import CALIFORNIA from "../cities/california.ts";
import SAN_FRANCISCO from "../cities/sf.ts";
import SOCAL from "../cities/socal.ts";
const PACKS = [
{ id: "california", city: CALIFORNIA },
{ id: "sf", city: SAN_FRANCISCO },
{ id: "socal", city: SOCAL },
];
const LADDER = buildLadder(PACKS);
/** The three boards as `handover` sees them. Metres per unit is `World`'s own arithmetic. */
const BOARDS: HandoverBoard[] = PACKS.map((pack) => ({
id: pack.id,
bounds: pack.city.bounds,
metresPerUnit: 111_320 / pack.city.latScale,
}));
describe("the ladder is built from the packs and changes none of them", () => {
it("folds the two California doors and keeps every other chapter", () => {
const authored =
CALIFORNIA.chapters.length + SAN_FRANCISCO.chapters.length + SOCAL.chapters.length;
assert.equal(authored, 26, "the three packs author twenty-six chapters between them");
assert.equal(LADDER.length, 24, "the two California doors fold onto the metro rungs");
// The doors are gone as rungs of their own and present as an origin on the
// two rungs they open. They are not camera poses — `main.ts` matches them
// against `CALIFORNIA_DESTINATIONS` and calls `switchCity` — so a rung for
// each would be a duplicate of the board it opens.
assert.equal(LADDER.some((rung) => rung.key === "california:los-angeles"), false);
assert.equal(LADDER.some((rung) => rung.key === "california:san-francisco"), false);
const southland = LADDER.find((rung) => rung.key === "socal:all");
const bay = LADDER.find((rung) => rung.key === "sf:all");
assert.deepEqual(southland?.doorFrom, { board: "california", id: "los-angeles" });
assert.deepEqual(bay?.doorFrom, { board: "california", id: "san-francisco" });
});
it("carries the packs' own ids, numbers and short labels unchanged", () => {
for (const pack of PACKS) {
for (const chapter of pack.city.chapters) {
const rung = LADDER.find((candidate) => candidate.key === `${pack.id}:${chapter.id}`);
// The two folded doors are the only chapters without a rung.
if (rung === undefined) {
assert.ok(
chapter.id === "los-angeles" || chapter.id === "san-francisco",
`${pack.id}:${chapter.id} has no rung and is not a door`,
);
continue;
}
assert.equal(rung.id, chapter.id);
assert.equal(rung.number, chapter.number);
assert.equal(rung.shortLabel, chapter.shortLabel);
assert.equal(rung.description, chapter.description);
}
}
});
it("resolves the label collisions in the ladder rather than in a pack", () => {
// Both metro packs open on a chapter labelled "The Whole Board", and both
// call a chapter "The Valley". Renaming either in its pack would move a
// string twenty-nine index-aimed capture guards assert on.
const labels = LADDER.map((rung) => rung.label);
assert.equal(new Set(labels).size, labels.length, `duplicate ladder label in ${labels}`);
assert.equal(SAN_FRANCISCO.chapters[0]?.shortLabel, "Whole Board");
assert.equal(SOCAL.chapters[0]?.shortLabel, "Whole Board");
assert.equal(LADDER.find((r) => r.key === "socal:all")?.label, "The Southland");
assert.equal(LADDER.find((r) => r.key === "sf:all")?.label, "The Bay Area");
});
it("puts every rung under a region that has a heading", () => {
for (const rung of LADDER) {
assert.equal(rung.region, BOARD_REGION[rung.board]);
assert.equal(typeof REGION_LABELS[rung.region], "string");
}
});
});
describe("stand-off, and not altitude, is the ladder's coordinate", () => {
it("sorts strictly descending by stand-off", () => {
for (let i = 1; i < LADDER.length; i++) {
const above = LADDER[i - 1];
const below = LADDER[i];
assert.ok(above && below);
assert.ok(
above.standoffM >= below.standoffM,
`${above.key} (${above.standoffM}) sorts above ${below.key} (${below.standoffM})`,
);
}
});
it("separates the three boards by stand-off and interleaves them by altitude", () => {
const on = (board: string) => LADDER.filter((rung) => rung.board === board);
const california = on("california");
const socal = on("socal");
const sf = on("sf");
// The measured claim the coordinate choice rests on: by stand-off the three
// boards form three clean bands.
const lowestCalifornia = Math.min(...california.map((r) => r.standoffM));
const highestSocal = Math.max(...socal.map((r) => r.standoffM));
const highestSf = Math.max(...sf.map((r) => r.standoffM));
assert.ok(lowestCalifornia > highestSocal, "California bottoms out above the Southland");
assert.ok(highestSocal > highestSf, "the Southland's whole board stands off further than the Bay's");
assert.ok(Math.round(lowestCalifornia / 1000) === 242, `California bottoms at 242 km, got ${lowestCalifornia}`);
assert.ok(Math.round(highestSocal / 1000) === 109, `the Southland tops at 108.8 km, got ${highestSocal}`);
assert.ok(Math.round(highestSf / 1000) === 71, `the Bay tops at 71.2 km, got ${highestSf}`);
// And by altitude they do not: a SoCal chapter sits inside California's range.
const californiaAltitudes = california.map((r) => r.altitudeM);
const lowestCaliforniaAltitude = Math.min(...californiaAltitudes);
const highestCaliforniaAltitude = Math.max(...californiaAltitudes);
const interleaved = [...socal, ...sf].filter(
(rung) =>
rung.altitudeM > lowestCaliforniaAltitude && rung.altitudeM < highestCaliforniaAltitude,
);
assert.ok(
interleaved.length > 0,
"an altitude ladder would interleave the boards; that is why stand-off is the coordinate",
);
});
it("derives both numbers from the authored focus block alone", () => {
const chapter = SAN_FRANCISCO.chapters[0];
assert.ok(chapter);
const expectedStandoff =
Math.hypot(chapter.focus.distance, chapter.focus.height) * (111_320 / SAN_FRANCISCO.latScale);
assert.equal(chapterStandoffMetres(chapter, SAN_FRANCISCO.latScale), expectedStandoff);
// The vertical exaggeration divides back out, exactly as
// `SceneHandle.cameraAltitudeMetres` does through `World.unitsToMetres`.
assert.equal(
chapterAltitudeMetres(chapter, SAN_FRANCISCO.latScale, SAN_FRANCISCO.verticalExaggeration),
(chapter.focus.height * (111_320 / SAN_FRANCISCO.latScale)) /
SAN_FRANCISCO.verticalExaggeration,
);
});
});
describe("the printed list groups what the array interleaves", () => {
const rows = placesRows(LADDER);
it("holds exactly the same rungs", () => {
assert.equal(rows.length, LADDER.length);
assert.deepEqual(
[...rows].map((r) => r.key).sort(),
[...LADDER].map((r) => r.key).sort(),
);
});
it("prints three region runs rather than nine", () => {
const runs: string[] = [];
for (const rung of rows) if (runs[runs.length - 1] !== rung.region) runs.push(rung.region);
assert.deepEqual(runs, ["state", "southland", "bay"]);
// The thing grouping exists to avoid: a globally sorted list changes region
// far more often than it has regions.
const interleavedRuns: string[] = [];
for (const rung of LADDER) {
if (interleavedRuns[interleavedRuns.length - 1] !== rung.region) {
interleavedRuns.push(rung.region);
}
}
assert.ok(
interleavedRuns.length > 5,
`a global sort changes region ${interleavedRuns.length} times; grouping is why the list is readable`,
);
});
it("still descends inside every group", () => {
for (let i = 1; i < rows.length; i++) {
const above = rows[i - 1];
const below = rows[i];
assert.ok(above && below);
if (above.region !== below.region) continue;
assert.ok(above.standoffM >= below.standoffM, `${above.key} then ${below.key}`);
}
});
});
describe("the lit rung is an altimeter, not a menu", () => {
it("keeps the rung you flew to lit when the camera is on its pose", () => {
const soma = LADDER.find((rung) => rung.key === "sf:soma");
assert.ok(soma);
assert.equal(activeRung(LADDER, "sf", soma.standoffM, "soma")?.key, "sf:soma");
});
it("does not hop between the four Bay rungs that sit inside 12% of each other", () => {
const eastBay = LADDER.find((rung) => rung.key === "sf:east-bay");
const sanJose = LADDER.find((rung) => rung.key === "sf:south-bay");
assert.ok(eastBay && sanJose);
assert.ok(
Math.abs(eastBay.standoffM - sanJose.standoffM) / sanJose.standoffM < 0.12,
"these two are the near-collision the tolerance exists for",
);
// Parked on the East Bay's own pose, the East Bay stays lit even though San
// Jose's number is only two per cent away.
assert.equal(activeRung(LADDER, "sf", eastBay.standoffM, "east-bay")?.key, "sf:east-bay");
});
it("follows the camera once it has left that pose", () => {
const whole = LADDER.find((rung) => rung.key === "sf:all");
const missionBay = LADDER.find((rung) => rung.key === "sf:mission-bay");
assert.ok(whole && missionBay);
// Still nominally "on" the whole-board chapter, but flown down to street
// level: the list has to say where the camera is.
assert.equal(activeRung(LADDER, "sf", missionBay.standoffM, "all")?.key, "sf:mission-bay");
});
it("only ever lights a rung on the resident board", () => {
for (const standoff of [3_000, 30_000, 300_000, 3_000_000]) {
assert.equal(activeRung(LADDER, "socal", standoff, null)?.board, "socal");
}
});
it("answers null for a board with no rungs", () => {
assert.equal(activeRung(LADDER, "paris", 10_000, null), null);
});
});
describe("handover does not oscillate", () => {
/** Downtown San Francisco: inside the sf rectangle, inside California's. */
const SF_TARGET = { lat: 37.7749, lng: -122.4194 };
/** Downtown Los Angeles: inside the socal rectangle, inside California's. */
const LA_TARGET = { lat: 34.05, lng: -118.24 };
/** Fresno: inside California and inside neither metro. */
const FRESNO = { lat: 36.74, lng: -119.78 };
const query = (over: {
current: string;
standoffM: number;
lat: number;
lng: number;
dragging?: boolean;
}) => handover({ boards: BOARDS, ...over });
it("promotes below the authored threshold and stays put above it", () => {
const sf = handoverStandoff("sf");
assert.equal(sf, 71_200);
assert.equal(
query({ current: "california", standoffM: sf * PROMOTE * 0.99, ...SF_TARGET }),
"sf",
);
assert.equal(query({ current: "california", standoffM: sf * PROMOTE * 1.01, ...SF_TARGET }), null);
});
it("promotes at most once and demotes at most once over a full sweep", () => {
const sf = handoverStandoff("sf");
let current = "california";
let promotions = 0;
let demotions = 0;
const step = (standoffM: number) => {
const next = query({ current, standoffM, ...SF_TARGET });
if (next === null) return;
if (next === "sf") promotions++;
else demotions++;
current = next;
};
// Down through the threshold in one per cent steps, and back up again.
for (let f = 1.4; f >= 0.5; f -= 0.01) step(sf * f);
assert.equal(current, "sf", "the sweep down ends on the finer board");
for (let f = 0.5; f <= 1.4; f += 0.01) step(sf * f);
assert.equal(promotions, 1, "one promotion over the whole sweep");
assert.equal(demotions, 1, "one demotion over the whole sweep");
assert.equal(current, "california", "the sweep up ends back on the state");
});
it("has a dead band between the promote and demote thresholds", () => {
const sf = handoverStandoff("sf");
// Inside the band, neither direction fires, from either side of it.
for (const f of [PROMOTE + 0.01, 1.0, DEMOTE - 0.01]) {
assert.equal(query({ current: "california", standoffM: sf * f, ...SF_TARGET }), null);
assert.equal(query({ current: "sf", standoffM: sf * f, ...SF_TARGET }), null);
}
});
it("never fires while a drag is in flight", () => {
const sf = handoverStandoff("sf");
assert.equal(
query({ current: "california", standoffM: sf * 0.2, dragging: true, ...SF_TARGET }),
null,
);
assert.equal(
query({ current: "sf", standoffM: sf * 10, dragging: true, ...SF_TARGET }),
null,
);
});
it("refuses to promote where there is no finer board — 97.4% of the state", () => {
for (const standoffM of [200_000, 60_000, 20_000, 4_000]) {
assert.equal(query({ current: "california", standoffM, ...FRESNO }), null);
}
});
it("promotes into the Southland over Los Angeles and never into the Bay", () => {
assert.equal(
query({ current: "california", standoffM: handoverStandoff("socal") * 0.5, ...LA_TARGET }),
"socal",
);
// The two metro rectangles do not intersect, so the candidate set is never
// larger than one — the assertion is that the *other* one is not a candidate.
assert.equal(query({ current: "sf", standoffM: 10_000, ...LA_TARGET }), null);
});
it("demotes to California and never straight into the other metro", () => {
assert.equal(
query({ current: "sf", standoffM: handoverStandoff("sf") * DEMOTE * 1.01, ...SF_TARGET }),
"california",
);
assert.equal(handoverStandoff("california"), Number.POSITIVE_INFINITY);
// California hands over to nothing, so no stand-off demotes off it.
assert.equal(query({ current: "california", standoffM: 5_000_000, ...SF_TARGET }), null);
});
it("refuses a nonsense stand-off rather than guessing", () => {
for (const standoffM of [0, -1, Number.NaN, Number.POSITIVE_INFINITY]) {
assert.equal(query({ current: "california", standoffM, ...SF_TARGET }), null);
}
});
});
+100
View File
@@ -0,0 +1,100 @@
/**
* Three packs, one place: do the boards agree about where a thing is?
*
* `World.lngScale` is `latScale × cos(centre.lat)`, and each pack takes the
* cosine at its **own** centre — 37.30°, 37.77°, 33.82°. A degree of longitude
* is then worth 88,552 m on the state board and 92,484 m on Southern
* California, so the two boards put the same coordinate in two different places
* and the gap grows with distance from the anchor. At a 109 km stand-off the
* worst of it is about 3% of the frame: a visible sideways slide in any
* transition that shows both boards at once, and a permanent disagreement about
* where Riverside is.
*
* The numbers below are measured on these packs, not quoted from a brief.
*/
import assert from "node:assert/strict";
import { afterEach, describe, it } from "node:test";
import CALIFORNIA from "../cities/california.ts";
import { REFERENCE_LAT, setReconcile } from "../cities/reconcile.ts";
import SF from "../cities/sf.ts";
import SOCAL from "../cities/socal.ts";
import { World } from "../engine/world.ts";
/** Downtown Los Angeles: inside both southern boards, so a fair shared origin. */
const ANCHOR: [number, number] = [34.05, -118.24];
const PLACES: [string, number, number][] = [
["the shared north-east corner", 34.36, -117.22],
["Riverside", 33.98, -117.37],
["the south-west corner", 33.28, -118.88],
];
/** Where a board puts a place, in true metres from the anchor. */
function offset(world: World, lat: number, lng: number): [number, number] {
const [x0, z0] = world.project(...ANCHOR);
const [x1, z1] = world.project(lat, lng);
return [(x1 - x0) * world.metresPerUnit, (z1 - z0) * world.metresPerUnit];
}
function disagreement(a: World, b: World, lat: number, lng: number): number {
const [ax, az] = offset(a, lat, lng);
const [bx, bz] = offset(b, lat, lng);
return Math.hypot(ax - bx, az - bz);
}
afterEach(() => setReconcile(null));
describe("projection", () => {
it("disagrees by kilometres today", () => {
setReconcile(false);
const california = new World(CALIFORNIA);
const socal = new World(SOCAL);
const worst = Math.max(...PLACES.map(([, lat, lng]) => disagreement(california, socal, lat, lng)));
assert.ok(worst > 3_500, `expected kilometres of drift, measured ${worst.toFixed(0)} m`);
assert.ok(worst < 4_200, `measured ${worst.toFixed(0)} m — the packs moved, re-read this test`);
});
it("agrees within 50 m once every board squashes longitude the same way", () => {
setReconcile(["projection"]);
const worlds = [CALIFORNIA, SF, SOCAL].map((pack) => new World(pack));
for (const [name, lat, lng] of PLACES) {
for (const a of worlds) {
for (const b of worlds) {
const gap = disagreement(a, b, lat, lng);
assert.ok(gap < 50, `${name}: ${a.city.id} vs ${b.city.id} is ${gap.toFixed(0)} m apart`);
}
}
}
});
it("puts every board on the one reference latitude", () => {
setReconcile(["projection"]);
for (const pack of [CALIFORNIA, SF, SOCAL]) {
const world = new World(pack);
const expected = pack.latScale * Math.cos((REFERENCE_LAT * Math.PI) / 180);
assert.ok(Math.abs(world.lngScale - expected) < 1e-9, pack.id);
// And the hill squash follows it, or hills stop being circles in scene
// space on the two boards whose own latitude is not the reference.
assert.ok(Math.abs(world.lngSquash - world.lngScale / pack.latScale) < 1e-15, pack.id);
}
});
/**
* The price, stated so it cannot be discovered later. Southern California is
* projected at 37.3° rather than at its own 33.82°, so it draws narrower in x
* than a projection taken at its own latitude would — measured, 4.25% — and
* San Francisco 0.64% wider. That is the trade: local aspect, for
* registration that is exact by construction.
*/
it("costs Southern California 4.25% of its width and San Francisco 0.64%", () => {
setReconcile(["projection"]);
const shrink = (pack: typeof SOCAL): number => {
const before = pack.latScale * Math.cos((pack.center.lat * Math.PI) / 180);
return new World(pack).lngScale / before - 1;
};
assert.ok(Math.abs(shrink(SOCAL) + 0.0425) < 0.0005, String(shrink(SOCAL)));
assert.ok(Math.abs(shrink(SF) - 0.0064) < 0.0005, String(shrink(SF)));
assert.equal(shrink(CALIFORNIA), 0);
});
});
+221
View File
@@ -0,0 +1,221 @@
/**
* The reconciliation, and the one property that protects the live site: with
* the flag off, nothing anywhere is different.
*
* "Byte-identical with the flag off" is asserted here rather than only in a
* photograph because a photograph proves it for one frame of one board on one
* afternoon, and this proves it for the object the engine reads. `reconciledCity`
* returning its argument **by identity** is the strongest form of that claim
* available: not a copy that happens to carry the same numbers, the same object.
*/
import assert from "node:assert/strict";
import { afterEach, describe, it } from "node:test";
import CALIFORNIA from "../cities/california.ts";
import {
COAST_RAMP_CELLS,
RELIEF_IN_FRAME,
blendedPeakMetres,
describe as describeReconciliation,
reconciledCity,
reconciledCoastFalloff,
reconciledExaggeration,
reconciledPalette,
reconciledPaletteMix,
setReconcile,
} from "../cities/reconcile.ts";
import SF from "../cities/sf.ts";
import SOCAL from "../cities/socal.ts";
import { DEFAULT_PALETTE } from "../engine/terrain.ts";
import { World } from "../engine/world.ts";
const PACKS = [CALIFORNIA, SF, SOCAL];
afterEach(() => setReconcile(null));
describe("reconcile — the flag off", () => {
it("hands every pack back by identity", () => {
setReconcile(false);
for (const pack of PACKS) assert.equal(reconciledCity(pack), pack);
});
it("leaves the World's derived scalars exactly where they were", () => {
setReconcile(false);
for (const pack of PACKS) {
const world = new World(pack);
assert.equal(world.city, pack);
assert.equal(world.lngScale, pack.latScale * Math.cos((pack.center.lat * Math.PI) / 180));
assert.equal(world.lngSquash, world.lngScale / pack.latScale);
assert.equal(world.metresPerUnit, 111_320 / pack.latScale);
assert.equal(world.city.verticalExaggeration, pack.verticalExaggeration);
assert.equal(world.city.coastFalloff, pack.coastFalloff);
assert.deepEqual(
world.city.roads.map((road) => road.width),
pack.roads.map((road) => road.width),
);
}
});
/**
* `lngSquash` stopped being `cos(centre.lat)` and became `lngScale/latScale`,
* which is the same number for every authored pack and the right one for a
* reconciled one. This is the assertion that the "same number" half is true —
* it is what every hill on every shipped board stands on.
*/
it("computes the same hill squash the old expression did", () => {
setReconcile(false);
for (const pack of PACKS) {
const world = new World(pack);
const old = Math.cos((pack.center.lat * Math.PI) / 180);
assert.ok(Math.abs(world.lngSquash - old) < 1e-15, `${pack.id}: ${world.lngSquash} vs ${old}`);
}
});
});
describe("reconcile — one relief rule", () => {
/**
* The whole claim of rule (a), as one number per board.
*
* Relief in the frame is peak scene units over board span — ARCHITECTURE
* §12.1's rule, and the one it moved California from 13 to 15 to hold. Today
* the three boards read 7.24%, 4.51% and 6.92%; the rule puts all three on
* `RELIEF_IN_FRAME`.
*/
it("puts all three boards on one relief-in-frame number", () => {
setReconcile(["exaggeration", "projection"]);
const relief = PACKS.map((pack) => {
const world = new World(pack);
const span = Math.max(
(pack.bounds.maxLat - pack.bounds.minLat) * pack.latScale,
(pack.bounds.maxLng - pack.bounds.minLng) * world.lngScale,
);
return world.metres(blendedPeakMetres(pack)) / span;
});
for (const value of relief) assert.ok(Math.abs(value - RELIEF_IN_FRAME) < 1e-6, String(value));
});
/**
* The board ARCHITECTURE §12.1 tuned against a photograph does not move, and
* that is not a coincidence: `RELIEF_IN_FRAME` is California's own number,
* rounded to three significant figures. The 0.1% here is that rounding.
*/
it("leaves California where the photograph put it", () => {
assert.ok(Math.abs(reconciledExaggeration(CALIFORNIA) - 15) < 0.015);
});
/**
* The honest limit of rule (a), asserted so nobody assumes otherwise.
*
* Apparent relief at a *matched* pose — the same true-metre stand-off on both
* boards, which is what the seam actually is — is proportional to the
* exaggeration itself and not to relief-in-frame. The rule takes the Bay Area
* direction from 4.17× to 2.60× and leaves the Southern California direction
* at 4.4×, because those two boards were already on one number. Only a single
* exaggeration everywhere would make it 1.0×, and that flattens the state to
* 1.7% of its own frame.
*/
it("narrows the seam step without closing it", () => {
setReconcile(["exaggeration", "projection"]);
const ex = (pack: typeof CALIFORNIA): number => new World(pack).city.verticalExaggeration;
assert.ok(ex(CALIFORNIA) / ex(SF) < 4.167, "the Bay Area direction must improve");
assert.ok(Math.abs(ex(CALIFORNIA) / ex(SF) - 2.6) < 0.1);
assert.ok(Math.abs(ex(CALIFORNIA) / ex(SOCAL) - 4.41) < 0.1);
});
});
describe("reconcile — one ground convention", () => {
it("makes the coastal ramp a fixed multiple of the coarse cell", () => {
for (const pack of PACKS) {
const cell = pack.cellLat * (pack.coarseFactor ?? 1);
assert.ok(Math.abs(reconciledCoastFalloff(pack) / cell - COAST_RAMP_CELLS) < 1e-12);
}
});
/**
* The rule is California's own ratio, so the state board's 0.025° comes back
* out of it to within 0.2%. That is what makes it a rule found rather than a
* rule invented.
*/
it("reproduces California's authored falloff", () => {
assert.ok(Math.abs(reconciledCoastFalloff(CALIFORNIA) / CALIFORNIA.coastFalloff - 1) < 0.002);
});
it("only ever widens the metro ramps, which is the direction that hides steps", () => {
for (const pack of [SF, SOCAL]) assert.ok(reconciledCoastFalloff(pack) > pack.coastFalloff);
});
/** The north anchor is `DEFAULT_PALETTE`, copied. This is the copy not drifting. */
it("keeps its copy of DEFAULT_PALETTE honest", () => {
setReconcile(["ground"]);
const north = reconciledPalette({ ...SF, bounds: { ...SF.bounds, minLat: 90, maxLng: -122 } });
for (const [key, value] of Object.entries(DEFAULT_PALETTE)) {
assert.equal(north[key as keyof typeof north], value, key);
}
});
it("gives California its own palette back, exactly", () => {
const mixed = reconciledPalette(CALIFORNIA);
assert.equal(reconciledPaletteMix(CALIFORNIA), 1);
for (const [key, value] of Object.entries(CALIFORNIA.palette ?? {})) {
assert.equal(mixed[key as keyof typeof mixed], value, key);
}
});
/**
* `alpine` is a fact about the board, not a taste: it is the colour of ground
* above the snow line, so a board with no ground up there does not get one.
* San Francisco's tallest is 1,186 m against a 1,900 m threshold.
*/
it("hands out the alpine stop only to a board that reaches the snow line", () => {
assert.equal(reconciledPalette(SF).alpine, undefined);
assert.ok(reconciledPalette(SOCAL).alpine !== undefined);
assert.ok(reconciledPalette(CALIFORNIA).alpine !== undefined);
});
});
describe("reconcile — the mechanics", () => {
it("is idempotent, which is what the terrain worker's second pass depends on", () => {
setReconcile(true);
for (const pack of PACKS) {
const once = reconciledCity(pack);
const clone = JSON.parse(JSON.stringify(once)) as typeof once;
assert.equal(reconciledCity(once), once);
assert.equal(reconciledCity(clone), clone);
assert.equal(clone.reconciled, true);
}
});
it("returns one object per pack and rule set, so a rebuild agrees with itself", () => {
setReconcile(["roads"]);
const roads = reconciledCity(SF);
assert.equal(reconciledCity(SF), roads);
setReconcile(["roads", "ground"]);
assert.notEqual(reconciledCity(SF), roads);
setReconcile(["roads"]);
assert.equal(reconciledCity(SF), roads);
});
it("turns each rule on alone", () => {
setReconcile(["roads"]);
const roadsOnly = reconciledCity(CALIFORNIA);
assert.equal(roadsOnly.verticalExaggeration, CALIFORNIA.verticalExaggeration);
assert.equal(roadsOnly.coastFalloff, CALIFORNIA.coastFalloff);
assert.equal(roadsOnly.lngScale, undefined);
assert.notEqual(roadsOnly.roads[0]?.width, CALIFORNIA.roads[0]?.width);
setReconcile(["exaggeration"]);
const reliefOnly = reconciledCity(SF);
assert.notEqual(reliefOnly.verticalExaggeration, SF.verticalExaggeration);
assert.deepEqual(
reliefOnly.roads.map((r) => r.width),
SF.roads.map((r) => r.width),
);
});
it("reports what it did", () => {
const report = describeReconciliation(SOCAL);
assert.equal(report.id, "socal");
assert.ok(report.lngMetresPerDegree.before > report.lngMetresPerDegree.after);
assert.ok(report.roadMetres.after[0] > 30 && report.roadMetres.after[1] < 56);
});
});
+99
View File
@@ -0,0 +1,99 @@
/**
* A road is a road on every board, or it is a symbol on one of them.
*
* Measured on these packs: San Francisco's roads are 1932 m wide and Southern
* California's 3155 m, which is what those roads are. California's US-101 and
* I-5 are **1,919 m and 2,034 m** — wider than the cities they join, and the
* most prominent marks on the state board. At the seam one has to become the
* other, and a 2 km ribbon turning into a 30 m line is a change of drawing
* convention rather than an LOD transition.
*
* Rule (b) reads every width as true metres and floors it at the narrowest
* fraction of a board span the two metro packs already agree on. The floor is
* the interesting part: it was measured off the shipped packs rather than
* chosen, and it is what makes "the metro boards do not move" a property of the
* rule rather than a coincidence to be checked.
*/
import assert from "node:assert/strict";
import { afterEach, describe, it } from "node:test";
import CALIFORNIA from "../cities/california.ts";
import { LEGIBLE_SPAN_FRACTION, reconciledRoadWidth, setReconcile } from "../cities/reconcile.ts";
import SF from "../cities/sf.ts";
import SOCAL from "../cities/socal.ts";
import type { City } from "../engine/types.ts";
import { World } from "../engine/world.ts";
const metresPerUnit = (city: City): number => 111_320 / city.latScale;
/** Every road on a board, in true metres, as the engine will draw it. */
function drawn(city: City): number[] {
const world = new World(city);
return world.city.roads.map((road) => road.width * world.metresPerUnit);
}
afterEach(() => setReconcile(null));
describe("road width", () => {
it("is a symbol two kilometres wide on the state board today", () => {
setReconcile(false);
const widths = drawn(CALIFORNIA);
assert.deepEqual(widths.map(Math.round), [1919, 2034]);
});
it("leaves both metro boards untouched, to the digit", () => {
setReconcile(false);
const before = { sf: drawn(SF), socal: drawn(SOCAL) };
setReconcile(["roads"]);
assert.deepEqual(drawn(SF), before.sf);
assert.deepEqual(drawn(SOCAL), before.socal);
});
/**
* Why the floor is 0.000199 and not a round number: the two metro packs,
* authored separately, put their narrowest road at 0.00019940 and 0.00020356
* of their own span. Two authors agreeing to within 2% about how thin a line
* may get is the measurement the constant is taken from, and the floor sits
* just under the tighter of the two so that neither board moves.
*/
it("sits just under the narrowest road either metro pack authored", () => {
for (const pack of [SF, SOCAL]) {
const world = new World(pack);
const span = Math.max(
(pack.bounds.maxLat - pack.bounds.minLat) * pack.latScale,
(pack.bounds.maxLng - pack.bounds.minLng) * world.lngScale,
);
const narrowest = Math.min(...pack.roads.map((road) => road.width)) / span;
assert.ok(narrowest > LEGIBLE_SPAN_FRACTION, `${pack.id}: ${narrowest}`);
assert.ok(narrowest < LEGIBLE_SPAN_FRACTION * 1.05, `${pack.id}: ${narrowest}`);
}
});
it("collapses California's freeways by 9x, onto the floor", () => {
setReconcile(["roads"]);
const widths = drawn(CALIFORNIA);
for (const metres of widths) {
assert.ok(metres > 200 && metres < 220, `${metres} m`);
}
assert.ok(1919 / (widths[0] as number) > 9);
});
/**
* The floor is a floor, not a width: a road wider in true metres than the
* floor keeps its own width. Nothing on the shipped boards exercises this —
* every metro road clears the floor and both California roads fall onto it —
* so it is asserted directly rather than left to be discovered by a pack that
* one day authors a 400 m causeway on a small board.
*/
it("is a floor and not a width", () => {
const wide = { ...CALIFORNIA.roads[0], widthM: 400_000 } as (typeof CALIFORNIA.roads)[number];
const units = reconciledRoadWidth(CALIFORNIA, wide);
assert.ok(Math.abs(units * metresPerUnit(CALIFORNIA) - 400_000) < 1);
});
it("reads an undeclared width as true metres already", () => {
const street = SF.roads.find((road) => road.kind === "street");
assert.ok(street !== undefined && street.widthM === undefined);
assert.ok(Math.abs(reconciledRoadWidth(SF, street) - street.width) < 1e-9);
});
});