1
0
This repository has been archived on 2026-08-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
tera/src/cities/unify.ts
T
karti 64618cb47d perf: one lake mesh per board, and the merged board's metro layers get an LOD
Closing the merged board's red budget cells, and one of the two fixes turned out
to be a win for every board in the product rather than for this one.

**Every lake on a board is now one mesh.** `createWater` built a `Mesh` and a
`MeshStandardMaterial` per inland-water polygon, so a board paid a draw call per
lake. It went unnoticed while the boards were separate — California draws one,
the Salton Sea — and a scene census on a phone found it the moment `unify.ts`
folded three packs together: **water=20 against water=1**, nineteen of the
fifty-eight draw calls that put the merged board over the mobile cap. They differ
in nothing but shape, so the geometries merge and the material is shared. The
0.05 lift is baked in before the merge, since afterwards there is no per-lake
mesh to carry it.

Measured on the shipping default, unrelated to any flag: socal 218 -> 205 draw
calls, bay-area 208 -> 202, california 374 -> 373, california-drive 233 -> 232.
It was never free anywhere.

**The metro-scale layers now follow the same reveal as the buildings.** Bridges,
airports, ports and vessels are handed to `applyDetailLod` and hidden with the
detail districts. That every one of them is metro detail on a merged board is
measured rather than assumed: the same census showed all seven named bridges,
all eight airport layers and every port mesh going from *zero* on the state pack
to one on the merged board — `california.ts` authors none of them. Together they
were the remaining 27 of those 58 draws, for structures sub-pixel at 1,919 m to
the unit.

Result on the merged board: california desktop 348,271 triangles of 440,000 and
372 draws of 415 on mobile, both green where mobile was 428 before.

**`DETAIL_REACH_M` is 60 km, down from 150.** At 150 km the test asked "are you
in the same half of the state as a city", which the corridor drive answers yes to
along most of its length. 60 km asks "are you looking at a city", which is what
the flag is for — and driving up US-101 the Southland now appears as you come
into it rather than while you are still in the Salinas Valley.

**The metro chapters were merged, photographed and reverted, and the reason is
recorded rather than the attempt deleted.** Converting a metro rung onto this
board is easy and was done correctly — `focus.lat/lng` is absolute, `distance`
is horizontal in the owning board's units and `height` has already been through
that board's exaggeration, so each keeps its true metres. The camera arrives
exactly where it should. What is *at* that range is the problem: two scene units
from the target on a 551-unit board, and the corridor there is a deliberate
4.7 km-wide atlas glyph, because DRIVE mode has to be able to drive down it on a
board where a real freeway is a fifth of a pixel. The delivered frame is a black
slab across San Francisco. A rung that flies you into that is worse than no rung.
They come back when the corridor is drawn at true width, which is
`createFreewayWorld` and the `roads` rule, not `unify.ts`.

`airportsAndCard.test.ts` asserted a literal one-liner that is now two statements
— the airport group is captured so the LOD can hide it. Split into the two facts
it was ever about: the `?? []` call exists, and the result is added.

1,701 tests pass. The full budget passes on the shipping default.

Still open on the merged board: `california-drive` mobile peaks at 315 draws
against 280. The steady state is +3 drawables over the state board; the peak is
the car driving into a metro and revealing it, against a cap set when the
corridor had no cities on it. That is a cap to re-derive for a board that now has
them, and it is an owner's call rather than a number to quietly raise — which is
why this is still behind `?one=1`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 02:53:26 -07:00

257 lines
12 KiB
TypeScript

/**
* One California, from three packs — as **one pack**, not three boards.
*
* ## Why this exists, and why it is not `reconcile.ts`
*
* `reconcile.ts` makes the three packs *agree*: one projection, one relief-in-
* frame, one road width. It leaves three boards that draw the same California
* and hands the camera between them. That was the answer to "the boards feel
* like three products". It is not the answer to "I want one full California",
* which is this file: a single `City` carrying the state's landform and both
* metros' detail, so the engine builds **one world, one lattice and one terrain
* mesh** and there is no seam anywhere to stitch.
*
* ## Why one pack rather than three scenes nested in each other
*
* The first attempt nested each metro's built scene inside the state board's
* under a similarity transform. The transform was right — measured, both metros
* landed where they belong at the size they should be — but two independently
* built terrains then occupied the same ground and interpenetrated: a block sat
* neatly on a coarse hill in one place and was half-buried in it three hundred
* metres away. That is nested-grid stitching, and the way to not have the
* problem is to not have two grids. One pack has one.
*
* ## The lattice is affordable, and the number that says so was measured
*
* `TODO.md` records a statewide lattice at metro fidelity as 23.7M points and
* dead. That is true at San Francisco's own 45 m cell, because `buildAxis`
* refines per **axis** — a focus region refines its whole row *and* its whole
* column, a plus rather than a box — so two metros at opposite corners refine
* most of the state. The blowup is a function of the fine cell, and it is
* gentle until 45 m:
*
* | fine cell | coarse | lattice | points | memory |
* | --- | --- | --- | --- | --- |
* | 45 m | x16 | 6002 x 7600 | 45.6M | 411 MB |
* | 100 m | x8 | 1676 x 2002 | 3.36M | 30 MB |
* | **150 m** | **x8** | **1120 x 1349** | **1.51M** | **14 MB** |
* | 200 m | x8 | 850 x 1013 | 0.86M | 8 MB |
*
* All three of today's boards together are 0.75M points. So 150 m costs about
* twice the whole product's current terrain and buys ground **23 times finer
* than the state board's 3,473 m cell** everywhere a city stands. No quadtree,
* no per-rectangle refinement, no change to `buildAxis` at all.
*
* ## What this file must do beyond concatenating arrays
*
* Everything in a `City` that describes a place is absolute — hills, districts,
* landmarks, bridges, ports and airports are all lat/lng — so most of the merge
* is a union. Two things are not:
*
* - **Hills sum.** `World.elevationAt` blends every hill that covers a point
* (tallest, plus 35% of the rest), so leaving the state's coarse hills under
* a metro's fine ones *adds* their heights and lifts the whole Bay. The
* state's hills are dropped where a metro replaces them.
* - **Districts overlap.** The state's seventeen districts include coarse
* stand-ins for Los Angeles and San Francisco; the metros carry 99 real
* ones. Both drawn is two cities in one place, so the coarse stand-ins go.
*
* Both filters are by centre-in-bounds, which is honest but not exact: a state
* hill centred outside a metro with a radius reaching into it still contributes.
* At the state's hill radii that is a gentle lift at the edge of a metro rather
* than a doubled mountain, and it is recorded here rather than hidden.
*/
import type { City, District, FocusRegion, Hill, LatLng } from "../engine/types.ts";
import { reconciledCity } from "./reconcile.ts";
import CALIFORNIA from "./california.ts";
import SAN_FRANCISCO from "./sf.ts";
import SOCAL from "./socal.ts";
/**
* The fine cell, in metres, and the factor the rest of the state is drawn at.
*
* **300 m, and it was measured down to that from 150.** The first pass used
* 150 m / x8, which is 1.23M lattice points, and the merged board then rendered
* **1,114,226 triangles at the whole-board pose against a 440,000 cap** — with
* every metro building already hidden by the detail LOD. That number is almost
* entirely *terrain*: hiding 99 districts' worth of lots moved it by 43,000
* triangles and moved draw calls by 111, which is how it was established that
* the buildings were never the problem.
*
* So the fine cell is what a statewide board can afford, and the honest ceiling
* on this engine — with no terrain level of detail, which is the piece that does
* not exist yet — is around 300 m / x16, or 0.18M points. That is **11.6x finer
* than the state pack's 3,473 m cell** and 6.7x coarser than San Francisco's own
* 45 m board.
*
* Closing that last gap is terrain LOD, and `TODO.md` already names its three
* blockers: placement drift (everything is placed once against `groundAt`), the
* shadow caster (15x triangles if selected independently), and determinism (the
* budget harness assumes geometry is not a function of camera path). None of
* them is in this file's way, and all of them are in that one's.
*/
export const UNIFIED_FINE_METRES = 300;
export const UNIFIED_COARSE_FACTOR = 16;
/** Is this point inside the rectangle? */
function within(lat: number, lng: number, r: FocusRegion): boolean {
return lat >= r.minLat && lat <= r.maxLat && lng >= r.minLng && lng <= r.maxLng;
}
/**
* The average of a polygon's vertices — enough to say which metro owns it.
*
* `LatLng` is a `[number, number]` tuple in this codebase and not an object, so
* the destructuring below is the whole of the type story.
*/
function centroid(polygon: readonly LatLng[]): { lat: number; lng: number } {
let lat = 0;
let lng = 0;
for (const [pLat, pLng] of polygon) {
lat += pLat;
lng += pLng;
}
const n = Math.max(1, polygon.length);
return { lat: lat / n, lng: lng / n };
}
export interface UnifyReport {
hills: { state: number; dropped: number; metro: number; total: number };
districts: { state: number; dropped: number; metro: number; total: number };
focusRegions: number;
cellLat: number;
coarseFactor: number;
}
let cached: { city: City; report: UnifyReport } | null = null;
/**
* The one board. Memoised, because `World` builds one per mount and the merge
* walks every district polygon in the product.
*/
export function unifiedCalifornia(): { city: City; report: UnifyReport } {
if (cached !== null) return cached;
const state = reconciledCity(CALIFORNIA);
const metros = [reconciledCity(SAN_FRANCISCO), reconciledCity(SOCAL)];
/*
* A metro's *bounds* rather than its focus regions decide what the state stops
* drawing, and the two are very different rectangles: San Francisco's focus
* region is 9.2% of its own board and its bounds are all of it. The state's
* coarse stand-in for a city covers the whole metro, so the whole metro is
* what has to be cleared — clearing only the focus region would leave a coarse
* Oakland beside a fine San Francisco.
*/
const claimed: FocusRegion[] = metros.map((m) => ({ ...m.bounds }));
const claims = (lat: number, lng: number): boolean =>
claimed.some((r) => within(lat, lng, r));
const stateHills = state.hills.filter((h: Hill) => !claims(h.lat, h.lng));
const metroHills = metros.flatMap((m) => m.hills);
const stateDistricts = state.districts.filter((d: District) => {
const c = centroid(d.polygon);
return !claims(c.lat, c.lng);
});
/*
* Marked `detail`, which is the flag `createBlocks` orders on and `scene.ts`
* reveals by camera stand-off. Without it the merged board measured 1,157,802
* triangles at the whole-board pose against a 440,000 cap, for buildings that
* are sub-pixel at 1,919 m to the unit. The lots exist either way; the flag
* decides when they are drawn.
*/
const metroDistricts: District[] = metros.flatMap((m) =>
m.districts.map((d) => ({ ...d, detail: true as const })),
);
/*
* ---- Chapters: the state's, and only the state's -------------------------
*
* **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.
*
* 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.
*
* 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.
*/
const mergedChapters = state.chapters;
const fine = UNIFIED_FINE_METRES / 111_320;
const city: City = {
...state,
/*
* The state's own id, on purpose. Every consumer that keys off a board — the
* fire gate, the ladder's region table, the budget harness's `data-board`
* assertion, `?city=` — already knows "california", and this *is* California.
* A new id would mean teaching all of them about a fourth board, which is
* the opposite of the point.
*/
id: state.id,
name: state.name,
cellLat: fine,
// The same lat:lng cell ratio the state pack authored, so cells stay square
// on the ground at this latitude rather than becoming letterboxes.
cellLng: fine * (CALIFORNIA.cellLng / CALIFORNIA.cellLat),
coarseFactor: UNIFIED_COARSE_FACTOR,
focusRegions: metros.flatMap((m) => m.focusRegions ?? []),
hills: [...stateHills, ...metroHills],
districts: [...stateDistricts, ...metroDistricts],
landmarks: [
...state.landmarks,
...metros.flatMap((m) => m.landmarks.map((l) => ({ ...l, detail: true as const }))),
],
bridges: [...state.bridges, ...metros.flatMap((m) => m.bridges)],
parks: [...state.parks, ...metros.flatMap((m) => m.parks)],
inlandWater: [...state.inlandWater, ...metros.flatMap((m) => m.inlandWater)],
airports: [...(state.airports ?? []), ...metros.flatMap((m) => m.airports ?? [])],
ports: [...(state.ports ?? []), ...metros.flatMap((m) => m.ports ?? [])],
chapters: mergedChapters,
/*
* Landmasses are NOT unioned. `isLand` is a point-in-any-polygon test, so a
* metro's finer coastline can only ever add land the state's outline already
* claimed — while costing a polygon test per sample over a 1.5M-point
* lattice. The state's coastline is the state's coastline.
*/
landmasses: state.landmasses,
reconciled: true,
};
const report: UnifyReport = {
hills: {
state: state.hills.length,
dropped: state.hills.length - stateHills.length,
metro: metroHills.length,
total: city.hills.length,
},
districts: {
state: state.districts.length,
dropped: state.districts.length - stateDistricts.length,
metro: metroDistricts.length,
total: city.districts.length,
},
focusRegions: city.focusRegions?.length ?? 0,
cellLat: fine,
coarseFactor: UNIFIED_COARSE_FACTOR,
};
cached = { city, report };
return cached;
}