Spaces: the inside of the world, and a sun that is actually where it should be
Ten agents wrote this in parallel against CONTRACT.md, which exists because the five design agents before them collided on fifteen blocking points — four files specified twice with incompatible contents, three separate backends for one box, and `Environment` exported twice meaning different things. What landed: a Stage owning only the renderer and the loop, with the city and an office as two scenes over it. They cannot share one — San Francisco is ~94 m per scene unit with 3.6x vertical exaggeration and an office is 1 unit = 1 m — and the city is paused rather than disposed on the way in, because rebuilding its 336,864-point heightfield costs about a second on the way back out. Offices are data. `src/offices/lumbridge-hq.ts` is fifteen rooms and seventy-six seats, and it is the file a self-hoster copies. Walls are a segment list with 1-D openings, so doors and windows are holes punched in a wall rather than placed objects, and the pass that splits a wall around its openings hands the walk-mode collider its segments for free. The sun is real. `solar.ts` is a NOAA/Meeus implementation with no imports at all — not even three.js — so time of day keeps working on a laptop in a field. Verified against known values: 75.45 degrees at the June solstice in SF, 28.79 at December, sunset at 03:15Z. The first screenshot after wiring it was a black rectangle, which turned out to be correct: it was midnight in San Francisco. Presence binds to a seat id and never to a coordinate. The pack knows where `eng-04` is; who is sitting in it is private data behind an API. Same shape as the marker rule, one level in. Two corrections to ARCHITECTURE.md are in here. Containment does not discharge ODbL — publishing OSM-derived coordinates is Public Use of a Derivative Database wherever the rows live, so the rule is about the geocoder (US Census, public domain) and not the storage. And a person at a desk is not a Marker; markers are geographic. One contract gap surfaced only in a screenshot: two agents read `height` on a viewpoint differently, so the establishing shot aimed at empty air fourteen metres above the roof. It now means what the same field means for a city. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+70
-8
@@ -85,12 +85,29 @@ export interface Road {
|
||||
kind: "street" | "freeway";
|
||||
}
|
||||
|
||||
/** A camera destination, and a sentence about why it is on the map. */
|
||||
export interface Chapter {
|
||||
/**
|
||||
* A named destination, as the interface knows it: what the legend prints and
|
||||
* what `flyTo` is keyed on.
|
||||
*
|
||||
* Split out of `Chapter` because an office has exactly the same idea — a short
|
||||
* list of places you can jump to — but positions them in metres, not in
|
||||
* latitude and longitude. Only this half of a chapter is shared; the pose is
|
||||
* not. `number` and `description` are optional here and required on `Chapter`,
|
||||
* because a city's chapters are a numbered tour with a sentence each and an
|
||||
* office's views are usually just "Reception" and "The desk bay".
|
||||
*/
|
||||
export interface View {
|
||||
id: string;
|
||||
number: string;
|
||||
label: string;
|
||||
shortLabel: string;
|
||||
number?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/** A camera destination, and a sentence about why it is on the map. */
|
||||
export interface Chapter extends View {
|
||||
number: string;
|
||||
description: string;
|
||||
focus: {
|
||||
lat: number;
|
||||
lng: number;
|
||||
@@ -98,7 +115,6 @@ export interface Chapter {
|
||||
height: number;
|
||||
rotation: number;
|
||||
};
|
||||
description: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,24 +195,70 @@ export interface ScenePalette {
|
||||
parkHigh: number;
|
||||
}
|
||||
|
||||
// ---- Lighting -------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Everything the light rig needs, as plain numbers.
|
||||
*
|
||||
* This is a *state*, not an observation: `Environment` — `{ time, sun, weather }`
|
||||
* — is what the world is doing, and a `LightingState` is what that means for the
|
||||
* rig. `Atmosphere` owns the conversion and is the only thing allowed to make
|
||||
* one; a scene applies it and never writes back. Two modules both constructing
|
||||
* and mutating the same three lights is the failure this shape exists to
|
||||
* prevent. See CONTRACT.md §4.
|
||||
*
|
||||
* Colours are `0xrrggbb`, matching `ScenePalette` and three.js.
|
||||
*/
|
||||
export interface LightingState {
|
||||
sun: {
|
||||
/**
|
||||
* Unit vector from the scene toward the sun. Distance is deliberately
|
||||
* absent: how far away to place the light is a fact about the scale of the
|
||||
* scene, and the sun does not know whether it is shining on 94 m per unit
|
||||
* or on 1 m per unit.
|
||||
*/
|
||||
direction: [number, number, number];
|
||||
color: number;
|
||||
intensity: number;
|
||||
};
|
||||
hemisphere: { sky: number; ground: number; intensity: number };
|
||||
ambient: { color: number; intensity: number };
|
||||
/**
|
||||
* Background gradient, or `null` to leave the background alone — which is
|
||||
* what an interior wants, since it has walls and no horizon.
|
||||
*/
|
||||
sky: { top: number; horizon: number } | null;
|
||||
/** `null` for no fog at all. An office gets none. */
|
||||
fog: { color: number; near: number; far: number } | null;
|
||||
}
|
||||
|
||||
// ---- Markers --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A thing on the map.
|
||||
* A thing worth pointing at, minus where it is.
|
||||
*
|
||||
* `colorKey` is deliberately opaque to the engine — it indexes into a palette
|
||||
* the caller supplies. The engine will not learn what "rejected" means.
|
||||
*
|
||||
* This is the half that survives a change of coordinate system: a pin on a city
|
||||
* at 37.79 N, -122.40 E and a pin on a desk 4.2 m along the east wall are the
|
||||
* same kind of thing to everything downstream of the geometry, so an office can
|
||||
* carry its own positions and still hand a `Pin` to the same detail card.
|
||||
*/
|
||||
export interface Marker {
|
||||
export interface Pin {
|
||||
id: string;
|
||||
lat: number;
|
||||
lng: number;
|
||||
label: string;
|
||||
colorKey: string;
|
||||
/** Optional href for the detail card. */
|
||||
url?: string;
|
||||
/** Optional one-liner for the detail card. */
|
||||
blurb?: string;
|
||||
}
|
||||
|
||||
/** A `Pin` placed on a city, in degrees. */
|
||||
export interface Marker extends Pin {
|
||||
lat: number;
|
||||
lng: number;
|
||||
/**
|
||||
* False when the position is a placeholder rather than a real address.
|
||||
* Rendered distinctly, because inventing a location on a map whose premise
|
||||
|
||||
Reference in New Issue
Block a user