feat: one California becomes a board you can descend into
Three changes, and the first is the one that mattered. `orbitMinDistance` was `boardSpan * 0.02`. On the Bay Area that is 20.1 units of 94.3 m — 1.89 km, the range every close photograph of this product was taken at. On the merged board the same rule is 11.1 units of 1,919 m, which is 21.3 km: eleven times further out, on the one board that carries both metros' districts, landmarks and bridges. The floor is a fraction of board *size*, and board size is exactly what `unify.ts` changed without changing what stands on it — so the merged board could never resolve a city no matter what the level of detail did, because the camera was not allowed to arrive. A board that carries `detail` districts now floors on true metres instead. Boards without them keep the span rule, which is right for an atlas. The metro chapters come back. They were merged, photographed and reverted at `64618cb`, and the recorded reason was specific and true: the corridor's 4.7 km atlas glyph filled every close frame. `23e4bb8` — the next commit — added `overviewLayers`, which withdraws that glyph inside `DETAIL_STANDOFF_M`, naming that same photograph. The blocker was gone and nobody came back for the rungs it had blocked. Each rung is restated in this board's units: `distance` on `latScale` alone, `height` carrying the exaggeration ratio as well, or the camera arrives at the right altitude over relief drawn 2.6x taller and every authored angle flattens. And with `?one=1` there is one entry in `CITIES`. That is what makes the merge a board rather than a nicer tier on a three-board product: every ladder rung's `board` is `california`, so `goToPlace` never switches; the two doors stop intercepting clicks and fly their authored poses; and `DEFAULT_CITY_ID` is the one board that exists. Without the flag nothing moves, which keeps the capture guards and every budget cell aiming at the boards they were written against. Measured, all twelve budget cells PASS. `california-one` reads 355,859 triangles and 415 draws against 400,000 / 460 — unchanged from the 352,927 / 420 recorded when the board landed. No cap was raised. What this does NOT fix, photographed rather than assumed: at FiDi's 7.7 km the camera is inside the terrain, and at the Bay's 45 km the ground is visibly faceted. Both are one cause — `verticalExaggeration` is a per-board constant, correctly derived at 15x for framing the whole state, and a board you can traverse from 1,551 km to 7.7 km cannot hold it constant. See TODO.md.
This commit is contained in:
+59
-20
@@ -179,30 +179,69 @@ export function unifiedCalifornia(): { city: City; report: UnifyReport } {
|
||||
);
|
||||
|
||||
/*
|
||||
* ---- Chapters: the state's, and only the state's -------------------------
|
||||
* ---- Chapters: the state's, and both metros' -----------------------------
|
||||
*
|
||||
* **Tried, photographed, reverted.** Merging all twenty-four rungs onto this
|
||||
* board is arithmetically easy — `focus.lat/lng` is absolute, `distance` is a
|
||||
* horizontal length in the owning board's units and `height` has already been
|
||||
* through that board's exaggeration, so each converts by keeping its true
|
||||
* metres. That was done, and the camera arrives exactly where it should.
|
||||
* **Tried, photographed, reverted — and now restored, because the thing that
|
||||
* was wrong with it was fixed one commit later.**
|
||||
*
|
||||
* The frame it arrives at is the problem, and it is a fact about the board
|
||||
* rather than about the conversion. Flying to FiDi puts the camera about two
|
||||
* scene units from its target on a board that is 551 units across, and what
|
||||
* is at that range is *state-scale content*: `createFreewayWorld` draws the
|
||||
* corridor as a deliberate 4.7 km-wide atlas glyph — `main.ts` says so where
|
||||
* it sets the corridor altitude, because DRIVE mode has to be able to drive
|
||||
* down it on a board where a real freeway is a fifth of a pixel — and the
|
||||
* terrain under it is 300 m cells. The delivered picture is a black slab
|
||||
* across San Francisco.
|
||||
* The revert was recorded here at `64618cb` and its reason was specific: at
|
||||
* FiDi's stand-off the frame filled with *state-scale content*, because
|
||||
* `createFreewayWorld` draws the corridor as a deliberate 4.7 km-wide atlas
|
||||
* glyph and a 4.7 km ribbon lying across a 12 km peninsula is a black slab.
|
||||
* That was a true report of the board as it stood.
|
||||
*
|
||||
* So a rung that flies you into that is worse than not having the rung. The
|
||||
* metro chapters come back when the corridor is drawn at true width on this
|
||||
* board, which is `createFreewayWorld`'s hard-coded scene units and the
|
||||
* `roads` reconciliation rule between them — not this file.
|
||||
* `23e4bb8` — the next commit — added `overviewLayers` to `engine/scene.ts`,
|
||||
* which **withdraws the corridor glyph** once the camera comes inside
|
||||
* `DETAIL_STANDOFF_M`, for exactly this reason and naming exactly this
|
||||
* photograph. The blocker was removed and nobody came back for the rungs it
|
||||
* was blocking, so the board kept six chapters and both metros stayed
|
||||
* unreachable on the one board built to hold them.
|
||||
*
|
||||
* What the revert note also said is still true and is *not* a blocker: the
|
||||
* terrain under a metro here is 300 m cells against that metro's own 45 m.
|
||||
* That is a quality gap, it is named in `UNIFIED_FINE_METRES`, and its fix is
|
||||
* terrain level of detail. A rung that arrives at coarse ground is a board
|
||||
* that needs finer ground; it is not a reason to have no rung.
|
||||
*
|
||||
* ## The conversion
|
||||
*
|
||||
* `focus.lat/lng` is absolute and carries over untouched. The other two are
|
||||
* lengths in the owning board's scene units and have to be restated in this
|
||||
* one's:
|
||||
*
|
||||
* - **`distance` is horizontal**, so it keeps its true metres:
|
||||
* `d x (mpu_src / mpu_here)`, and `mpu` is `111320 / latScale`, so the
|
||||
* whole factor is `latScale_here / latScale_src`.
|
||||
* - **`height` is vertical, and vertical is exaggerated per pack.** It has
|
||||
* already been through the source board's `verticalExaggeration`, and
|
||||
* this board draws its relief at a different one — 15x against San
|
||||
* Francisco's 3.6. Converting on true metres alone would put the camera
|
||||
* at the right altitude over the wrong-looking hills and flatten every
|
||||
* authored angle. It carries the exaggeration ratio too, which keeps the
|
||||
* camera at the same height *relative to the terrain it is looking at* —
|
||||
* which is what makes the framing survive the move.
|
||||
*
|
||||
* Each metro's own whole-board chapter is dropped: `california-overview`
|
||||
* already is the whole board, and two rungs that frame the same thing at the
|
||||
* same range is a ladder with a repeated step.
|
||||
*/
|
||||
const mergedChapters = state.chapters;
|
||||
const mergedChapters: City["chapters"] = [
|
||||
...state.chapters,
|
||||
...metros.flatMap((m) => {
|
||||
const horizontal = state.latScale / m.latScale;
|
||||
const vertical = horizontal * (state.verticalExaggeration / m.verticalExaggeration);
|
||||
return m.chapters
|
||||
.filter((chapter) => chapter.id !== "all")
|
||||
.map((chapter) => ({
|
||||
...chapter,
|
||||
focus: {
|
||||
...chapter.focus,
|
||||
distance: chapter.focus.distance * horizontal,
|
||||
height: chapter.focus.height * vertical,
|
||||
},
|
||||
}));
|
||||
}),
|
||||
];
|
||||
|
||||
const fine = UNIFIED_FINE_METRES / 111_320;
|
||||
|
||||
|
||||
+33
-1
@@ -761,7 +761,39 @@ export async function createScene(
|
||||
Math.hypot(eastX, southZ),
|
||||
);
|
||||
|
||||
const orbitMinDistance = Math.max(4, boardSpan * 0.02);
|
||||
/**
|
||||
* Does this board carry metro detail? Asked here, off the pack, rather than
|
||||
* read from `hasDetail` below, because the orbit floor is a constructor
|
||||
* argument and `hasDetail` is not known until the blocks are built.
|
||||
*/
|
||||
const carriesDetail = city.districts.some((d) => d.detail === true);
|
||||
|
||||
/**
|
||||
* The closest the camera may stand to what it is looking at, in true metres.
|
||||
*
|
||||
* **1,900 m, and it is the Bay Area board's own floor rather than a new
|
||||
* number.** `boardSpan * 0.02` is 20.1 units there and 94.3 m to the unit, so
|
||||
* San Francisco has always let you come to 1.89 km — and that is the range
|
||||
* every close photograph of this product was taken at.
|
||||
*
|
||||
* On the merged board the *same rule* is 11.1 units of 1,919 m, which is
|
||||
* **21.3 km**: eleven times further out, on the one board that carries both
|
||||
* metros' districts, landmarks and bridges. The rule is a fraction of board
|
||||
* size, and board size is exactly the thing `cities/unify.ts` changed without
|
||||
* changing what is standing on it. So the state tier could never resolve a
|
||||
* city no matter what the level of detail did — not because the lots are
|
||||
* coarse, but because the camera is not allowed to arrive.
|
||||
*
|
||||
* A fraction of the board is still right for a board with nothing close to
|
||||
* look at: California with no cities on it is an atlas, 21 km is the scale it
|
||||
* reads at, and dropping its floor would only let a visitor push their face
|
||||
* into a 3,473 m terrain cell. Hence the two cases, keyed on the one fact
|
||||
* that distinguishes them.
|
||||
*/
|
||||
const ORBIT_MIN_STANDOFF_M = 1_900;
|
||||
const orbitMinDistance = carriesDetail
|
||||
? ORBIT_MIN_STANDOFF_M / world.metresPerUnit
|
||||
: Math.max(4, boardSpan * 0.02);
|
||||
/**
|
||||
* The far end of the orbit, held rather than written twice.
|
||||
*
|
||||
|
||||
+57
-7
@@ -239,11 +239,36 @@ import type { PresenceIndicator } from "./realtime/presenceIndicator.ts";
|
||||
*/
|
||||
const ONE_CALIFORNIA = new URLSearchParams(location.search).get("one") === "1";
|
||||
|
||||
const CITIES: { id: string; label: string; city: City }[] = [
|
||||
{ id: "california", label: "California", city: ONE_CALIFORNIA ? unifiedCalifornia().city : CALIFORNIA },
|
||||
{ id: "sf", label: "Bay Area", city: SAN_FRANCISCO },
|
||||
{ id: "socal", label: "SoCal", city: SOCAL },
|
||||
];
|
||||
/**
|
||||
* The boards on offer — three, or **one**.
|
||||
*
|
||||
* With `?one=1` this is a single entry, and that is the whole of what makes the
|
||||
* merged board a *board* rather than a nicer statewide tier on a three-board
|
||||
* product. Everything downstream that decides "is this somewhere else" reads
|
||||
* this array:
|
||||
*
|
||||
* - `LADDER` is built from it, so every rung's `board` is `california` and
|
||||
* `goToPlace`'s `rung.board !== cityId` switch never fires. Clicking FiDi
|
||||
* moves the camera instead of tearing the board down and building another.
|
||||
* - `PREFETCH_TIERS` and the handover rule get one tier, so there is no
|
||||
* handover: nothing to promote to, nothing to demote from, and no cut to
|
||||
* conceal with a fog dip.
|
||||
* - The office return links and `?city=` fall back to the one board that
|
||||
* exists rather than to a board the visitor was never on.
|
||||
*
|
||||
* The metro packs are still imported and still merged — `unifiedCalifornia()`
|
||||
* reads both — they simply stop being *destinations*. Without the flag nothing
|
||||
* here moves, which is what keeps the twenty-nine capture guards and every cell
|
||||
* of `scripts/performance-budget.mjs` aiming at the boards they were written
|
||||
* against.
|
||||
*/
|
||||
const CITIES: { id: string; label: string; city: City }[] = ONE_CALIFORNIA
|
||||
? [{ id: "california", label: "California", city: unifiedCalifornia().city }]
|
||||
: [
|
||||
{ id: "california", label: "California", city: CALIFORNIA },
|
||||
{ id: "sf", label: "Bay Area", city: SAN_FRANCISCO },
|
||||
{ id: "socal", label: "SoCal", city: SOCAL },
|
||||
];
|
||||
|
||||
/**
|
||||
* The board a bare URL lands on. **The detailed one, not the state tier.**
|
||||
@@ -277,7 +302,14 @@ const CITIES: { id: string; label: string; city: City }[] = [
|
||||
* `?city=california` still goes straight to the state board, and nothing about
|
||||
* the deep link changed.
|
||||
*/
|
||||
const DEFAULT_CITY_ID = "sf";
|
||||
/*
|
||||
* With one board there is nothing to choose: the argument above is entirely
|
||||
* about which of three products a bare URL lands on, and `?one=1` is the answer
|
||||
* that there are not three. The landing *pose* is a separate question and still
|
||||
* belongs to the chapter list — `california-overview` frames the state, and the
|
||||
* metro rungs restored in `cities/unify.ts` are how you get down from it.
|
||||
*/
|
||||
const DEFAULT_CITY_ID = ONE_CALIFORNIA ? "california" : "sf";
|
||||
|
||||
/** The corridor's scale doors, and the office each detailed board arrives near. */
|
||||
const CALIFORNIA_DESTINATIONS = new Map<string, { cityId: string; officeId: string }>([
|
||||
@@ -5210,7 +5242,25 @@ function flyToChapter(viewId: string) {
|
||||
renderChrome();
|
||||
}
|
||||
else {
|
||||
const destination = cityId === "california" ? CALIFORNIA_DESTINATIONS.get(view.id) : undefined;
|
||||
/*
|
||||
* A door is a door only while there is somewhere else to go.
|
||||
*
|
||||
* `los-angeles` and `san-francisco` carry real authored poses — 38 units at
|
||||
* 26 of height, which is a 73 km stand-off over the city — and on the three
|
||||
* -board product those poses are never used, because this line intercepts
|
||||
* the click and switches boards instead. On the merged board both cities
|
||||
* are *on this board*, so the interception is what stops you looking at
|
||||
* them: it is the reason a visitor who clicks SF ends up somewhere the
|
||||
* merged board is not.
|
||||
*
|
||||
* Left alone, the fall-through below flies the authored pose, and the
|
||||
* offices stay reachable the way they already are on every other board —
|
||||
* the amber pins, which `scene.ts` gates on the board's own bounds and the
|
||||
* merged board's bounds contain both.
|
||||
*/
|
||||
const destination = cityId === "california" && !ONE_CALIFORNIA
|
||||
? CALIFORNIA_DESTINATIONS.get(view.id)
|
||||
: undefined;
|
||||
if (destination) {
|
||||
requestControlMode("overview");
|
||||
officeId = destination.officeId;
|
||||
|
||||
+90
-9
@@ -18,6 +18,7 @@ import {
|
||||
unifiedCalifornia,
|
||||
} from "../cities/unify.ts";
|
||||
import CALIFORNIA from "../cities/california.ts";
|
||||
import { reconciledCity } from "../cities/reconcile.ts";
|
||||
import SAN_FRANCISCO from "../cities/sf.ts";
|
||||
import SOCAL from "../cities/socal.ts";
|
||||
|
||||
@@ -129,19 +130,99 @@ describe("one California", () => {
|
||||
});
|
||||
|
||||
describe("one California's chapters", () => {
|
||||
it("is the state's list, until the corridor is drawn at true width", () => {
|
||||
/*
|
||||
* The metro rungs were merged, photographed and reverted; `unify.ts` records
|
||||
* why in full. The short version is that the conversion was right and the
|
||||
* frame was not — a two-unit stand-off on a 551-unit board lands inside the
|
||||
* corridor's 4.7 km-wide atlas glyph. This test exists so that the day
|
||||
* somebody merges them again, they do it deliberately.
|
||||
*/
|
||||
/*
|
||||
* The metro rungs were merged, photographed and reverted at `64618cb`, and
|
||||
* this test used to assert the revert so that whoever merged them again did it
|
||||
* deliberately. This is that deliberate merge, and the reason is in `unify.ts`
|
||||
* in full: the revert's stated blocker was the corridor's 4.7 km atlas glyph
|
||||
* filling every close frame, and `23e4bb8` — the next commit — withdraws that
|
||||
* glyph inside `DETAIL_STANDOFF_M`. The guard is kept and turned around: it
|
||||
* now asserts the conversion, which is the part a careless edit breaks
|
||||
* silently.
|
||||
*/
|
||||
it("carries the state's rungs first, in their authored order", () => {
|
||||
// `createScene` opens on `chapters[0]` and the capture harness reaches the
|
||||
// state's chapters by index, so the state's list has to stay a prefix.
|
||||
assert.deepEqual(
|
||||
city.chapters.map((c) => c.id),
|
||||
city.chapters.slice(0, CALIFORNIA.chapters.length).map((c) => c.id),
|
||||
CALIFORNIA.chapters.map((c) => c.id),
|
||||
);
|
||||
});
|
||||
|
||||
it("adds both metros' rungs, without their whole-board duplicates", () => {
|
||||
const ids = city.chapters.map((c) => c.id);
|
||||
for (const metro of [SAN_FRANCISCO, SOCAL]) {
|
||||
for (const chapter of metro.chapters) {
|
||||
if (chapter.id === "all") {
|
||||
// `california-overview` already frames the whole board; a second rung
|
||||
// at the same range is a repeated step on the ladder.
|
||||
assert.ok(!ids.includes(chapter.id), `whole-board rung "${chapter.id}" was merged`);
|
||||
continue;
|
||||
}
|
||||
assert.ok(ids.includes(chapter.id), `metro rung "${chapter.id}" is missing`);
|
||||
}
|
||||
}
|
||||
// Nothing merged twice, which a naive concat of two packs sharing an id
|
||||
// would do without failing anything else.
|
||||
assert.equal(new Set(ids).size, ids.length);
|
||||
});
|
||||
|
||||
it("restates each merged rung in this board's units, keeping its true metres", () => {
|
||||
/*
|
||||
* `distance` is horizontal and converts on `latScale` alone. `height` is
|
||||
* vertical, has already been through the source pack's exaggeration, and
|
||||
* has to carry the ratio of exaggerations as well — otherwise the camera
|
||||
* arrives at the right altitude over relief drawn 4.2x taller and every
|
||||
* authored angle flattens. Both are checked against the arithmetic rather
|
||||
* than against a recorded number, so a pack edit cannot falsify them.
|
||||
*/
|
||||
/*
|
||||
* Reconciled on both sides, because `unifiedCalifornia` merges reconciled
|
||||
* packs and `reconciledCity` is **not** the identity: the relief rule
|
||||
* rewrites `verticalExaggeration`, which takes San Francisco from the 3.6
|
||||
* its file authors to 5.78. Checking the arithmetic against the raw packs
|
||||
* measures a board nobody is looking at — and it fails, which is the only
|
||||
* reason this is written down rather than assumed.
|
||||
*/
|
||||
const here = reconciledCity(CALIFORNIA);
|
||||
for (const raw of [SAN_FRANCISCO, SOCAL]) {
|
||||
const metro = reconciledCity(raw);
|
||||
const horizontal = here.latScale / metro.latScale;
|
||||
const vertical =
|
||||
horizontal * (here.verticalExaggeration / metro.verticalExaggeration);
|
||||
for (const source of metro.chapters) {
|
||||
if (source.id === "all") continue;
|
||||
const merged = city.chapters.find((c) => c.id === source.id);
|
||||
assert.ok(merged, `metro rung "${source.id}" is missing`);
|
||||
// The place itself is absolute and must not have moved at all.
|
||||
assert.equal(merged.focus.lat, source.focus.lat);
|
||||
assert.equal(merged.focus.lng, source.focus.lng);
|
||||
assert.ok(
|
||||
Math.abs(merged.focus.distance - source.focus.distance * horizontal) < 1e-9,
|
||||
`"${source.id}" stand-off was not restated in this board's units`,
|
||||
);
|
||||
assert.ok(
|
||||
Math.abs(merged.focus.height - source.focus.height * vertical) < 1e-9,
|
||||
`"${source.id}" height ignored the exaggeration ratio`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("puts every merged rung inside the board it now belongs to", () => {
|
||||
// A rung whose focus is off the bounds is a camera aimed at empty sea, and
|
||||
// the merge is the one operation that can produce one.
|
||||
for (const chapter of city.chapters) {
|
||||
assert.ok(
|
||||
chapter.focus.lat >= city.bounds.minLat && chapter.focus.lat <= city.bounds.maxLat,
|
||||
`chapter "${chapter.id}" points off the board in latitude`,
|
||||
);
|
||||
assert.ok(
|
||||
chapter.focus.lng >= city.bounds.minLng && chapter.focus.lng <= city.bounds.maxLng,
|
||||
`chapter "${chapter.id}" points off the board in longitude`,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("one California's cities are lotted like cities", () => {
|
||||
|
||||
Reference in New Issue
Block a user