1
0

Lumbridge Simulate Engine — the city, and the licence it can actually ship under

LSE is the third of the three, beside lumbridge-compute and lumbridge-bench: a
3D engine for walkable places. This first commit is the outside of the world —
San Francisco — plus the seams the inside will attach to.

The engine renders a City and a list of Markers and knows nothing else. It does
not know markers are usually companies and it will never learn that "rejected"
is red; that mapping lives in an adapter. Which is what lets one renderer serve
a private map, a public one, and a self-hoster with no Lumbridge account, none
of them a fork of the others.

Three things were designed around the licence rather than discovered after it,
because each one is a promise Apache 2.0 makes that is easy to break by
accident. No trademarks in the repo — logos are fetched at runtime, and
public/logos/ is gitignored. No OpenStreetMap-derived coordinates, which is why
every coastline in cities/sf.ts was traced by hand: Nominatim output is ODbL,
share-alike, and would attach to the whole pack. And no FlightRadar24 client —
their terms forbid scraping and redistribution, so flights are an interface
with a simulator and open community ADS-B behind it.

The privacy constraint and the licence constraint turned out to want the same
thing. Geocoded company positions and pipeline status both stay behind Workie's
API; the open repo holds the city and the renderer. The tempting shortcut —
commit an sf-companies.json — breaks both at once.

Ported out of Workie, where a 3D city engine had no business living. Workie's
/live is deleted rather than deprecated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Karti Tripathi
2026-08-04 21:49:08 -07:00
commit 67f8df8d53
20 changed files with 4789 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
/**
* The standalone demo: San Francisco, simulated traffic, chapter legend.
*
* Deliberately ships **no company data**. Markers are demonstrated using the
* city's own landmarks — buildings, not businesses — because company positions
* are geocoded (ODbL) and company pipeline status is private, and neither
* belongs in this repo. Real markers arrive at runtime from an adapter; see
* `src/adapters/` and ARCHITECTURE.md §3.
*/
import { createScene } from "./engine/scene.ts";
import { SimulatedFlights, type SimRoute } from "./engine/flights.ts";
import type { Marker, MarkerPalette } from "./engine/types.ts";
import SAN_FRANCISCO from "./cities/sf.ts";
/**
* Bay Area traffic, roughly where it actually is: SFO sits south of frame and
* its arrivals run down the peninsula, Oakland is east across the bay, and the
* coastal departures turn out over the Pacific.
*/
const ROUTES: SimRoute[] = [
{ callsign: "UAL 1", from: [37.95, -122.36], to: [37.66, -122.4], fromAlt: 2400, toAlt: 500, duration: 190 },
{ callsign: "ASA 22", from: [37.93, -122.31], to: [37.65, -122.38], fromAlt: 2100, toAlt: 450, duration: 210 },
{ callsign: "SWA 118", from: [37.64, -122.39], to: [37.9, -122.62], fromAlt: 700, toAlt: 5200, duration: 165 },
{ callsign: "DAL 407", from: [37.7, -122.21], to: [37.88, -122.55], fromAlt: 1800, toAlt: 6100, duration: 230 },
{ callsign: "UAL 88", from: [37.62, -122.6], to: [37.95, -122.28], fromAlt: 6800, toAlt: 8200, duration: 260 },
{ callsign: "N512SP", from: [37.83, -122.56], to: [37.7, -122.22], fromAlt: 1100, toAlt: 1300, duration: 300 },
{ callsign: "JBU 915", from: [37.96, -122.48], to: [37.63, -122.36], fromAlt: 3100, toAlt: 600, duration: 205 },
];
const MARKER_PALETTE: MarkerPalette = {
landmark: 0xf2b134,
neutral: 0x9aa4ad,
};
const canvas = document.querySelector<HTMLCanvasElement>("#scene");
if (!canvas) throw new Error("#scene canvas missing");
const scene = createScene(canvas, {
city: SAN_FRANCISCO,
markerPalette: MARKER_PALETTE,
flights: new SimulatedFlights(ROUTES),
onMarkerPick: (marker) => {
const card = document.querySelector<HTMLElement>("#detail");
if (!card) return;
if (!marker) {
card.hidden = true;
return;
}
card.hidden = false;
card.textContent = marker.label;
},
});
// Demo markers: the city's own named buildings.
const demoMarkers: Marker[] = SAN_FRANCISCO.landmarks
.filter((l) => l.label)
.map((l) => ({
id: l.name,
lat: l.lat,
lng: l.lng,
label: l.name,
colorKey: "landmark",
located: true,
}));
scene.setMarkers(demoMarkers);
// ---- Chapter legend -------------------------------------------------------
const nav = document.querySelector<HTMLElement>("#chapters");
const blurb = document.querySelector<HTMLElement>("#blurb");
function renderLegend(activeId: string) {
if (!nav) return;
nav.replaceChildren();
for (const chapter of scene.chapters) {
const button = document.createElement("button");
button.className = chapter.id === activeId ? "chapter active" : "chapter";
button.innerHTML = `<span class="num">${chapter.number}</span><span>${chapter.shortLabel}</span>`;
button.addEventListener("click", () => scene.flyTo(chapter.id));
nav.append(button);
}
const active = scene.chapters.find((c) => c.id === activeId);
if (blurb && active) blurb.textContent = active.description;
}
renderLegend(scene.current());
scene.onChapterChange(renderLegend);