From 9c9e78f6f90175130b26fb3d034184627dcb4d11 Mon Sep 17 00:00:00 2001 From: Kartios Date: Fri, 7 Aug 2026 05:50:32 -0700 Subject: [PATCH] The Southland gets a door, and the people in it move MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Mateo Court**, a courtyard block in the Arts District — a third pack and a third *kind* of building. Two office floors and a shed already existed, so this is the plan the format had not been shown: a ring of rooms round an open-air yard with **no corridor anywhere**. Every door opens onto the yard, and the yard does the job a corridor does in the other two. It is also the first pack whose `ceiling: null` means there is genuinely no ceiling at any height rather than "take the lid off so the shot can see in", and the first sited off the Bay Area board at all. That last part needed a fix, not just a coordinate. `OFFICE_MARKERS` was gated on `id === "sf"`, which was correct for exactly as long as every office was in the Bay Area — it would have kept the Los Angeles building off the Los Angeles board and pinned it to San Francisco's. The gate is the board's own bounds now, which is the same question asked honestly. Every door through the 0.25 m courtyard skin is 1.2 m rather than the usual 0.9, and that is not a style choice: `blocked` inflates by the walker's radius *and* the wall's thickness, so a 0.9 m leaf through a wall that thick seals the room behind it while rendering perfectly. Two rooms were sealed exactly that way on the first pass. The test says so, because a well-meaning edit back to 0.9 for consistency would do it again. **People move.** `samplePresenceAt` thinned the roster by hour, which fixed a building that was full at 1 a.m., but everybody was still pinned to their own desk all day. Occupancy is a hand-written booking table now: meetings fill a room for a plausible length, the kitchen island fills at lunch, and a `Presence` binds to a seat id — so "in a meeting" means occupying a meeting-room seat, and the whole thing is choosing seat ids rather than inventing positions. The flood fill that checks a pack is walkable existed three times over. One copy now, since the third is where a divergence lives. **Films.** A new `office-dusk` reel — six hours over Lumbridge HQ catching the moment the house lights take over from the sun, which is the one thing only a time-lapse can show and which did not exist. The three existing reels are re-rendered rather than re-captioned: `films.ts` in v4 named a commit eleven behind HEAD, so the published reels were shot before there were any clouds and before the city drew office pins. Reviewers caught roughly a dozen false statements in the new prose across these files — a room census that did not add up, a wall-thickness count, a claim that every room has daylight when one does not, and a cost figure saying `updateSun` runs once a second when it runs once a minute. The consequential ones are fixed. In a codebase where the comments are the design record, a confident wrong number is a defect. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/brand-assets/films.mjs | 146 ++- src/adapters/sample.ts | 828 +++++++++++++--- src/main.ts | 30 +- src/offices/mateo-court.ts | 1650 ++++++++++++++++++++++++++++++++ src/offices/sites.ts | 21 +- src/test/office.test.ts | 174 +++- 6 files changed, 2700 insertions(+), 149 deletions(-) create mode 100644 src/offices/mateo-court.ts diff --git a/scripts/brand-assets/films.mjs b/scripts/brand-assets/films.mjs index 2967640..d0d2f86 100644 --- a/scripts/brand-assets/films.mjs +++ b/scripts/brand-assets/films.mjs @@ -84,13 +84,24 @@ const deliverSize = (spec) => { }; /** - * `chapter` and `expect` work exactly as they do in `shots.mjs` — an index into - * the chapter list, and the `shortLabel` it is asserted to be. + * `door`, `chapter` and `expect` work exactly as they do in `shots.mjs`: which + * building the engine is showing, an index into the chapter list, and the + * `shortLabel` that index is asserted to be before the shutter opens. * - * `from` and `to` are the ends of the day the camera watches. They are not - * midnight to midnight: the hours either side of dawn and dusk are the whole - * point and the small hours are eighteen identical dark frames, so the window - * is cropped to the part that moves. + * What is deliberately *not* carried over is `shots.mjs`'s `office` field and + * the picker press behind it. Every film below is of the building its door opens + * on, so that press would be a capture path no film exercises — and an untried + * step in a pipeline is worse than an absent one, because it looks like coverage. + * A Frontier Valley film means porting those fifteen lines along with it; the + * office entry at the bottom says why the hangar is the second office film to + * shoot rather than the first. + * + * `from` and `to` are the ends of the stretch of day the camera watches, and + * every film crops to the part of it that moves. For the city films that means + * dropping the small hours, which are dark frames indistinguishable from each + * other; for `office-dusk` it means a window narrow enough that the lights + * coming on lasts half a second instead of one frame. `hero-soma` is the + * exception, and argues for itself on its own entry. */ const FILMS = [ { @@ -108,7 +119,7 @@ const FILMS = [ poster: 0.86, place: "Financial District, San Francisco", caption: - "One camera, eighteen hours, six seconds. Nothing here is keyframed: every frame is the engine asked for a different instant, and the light, the shadows, the sky and the window lights all follow from that one number. The clock in the corner is the film captioning itself.", + "One camera, eighteen hours, six seconds. Nothing here is keyframed: every frame is the engine asked for a different instant, and the light, the shadows, the sky, the map in the corner and the window lights all follow from that one number. The readout at the top of the panel is the film captioning itself — the hour, how high the sun is, which band of twilight that puts it in, and how much of the moon is lit.", alt: "A time-lapse of the San Francisco financial district seen from above. Shadow sweeps across the towers as the sun crosses the sky, the water changes colour, and after sunset the tower windows light up one by one.", }, { @@ -162,15 +173,103 @@ const FILMS = [ to: "2026-08-06T22:40:00-07:00", frames: 180, fps: 30, - // The region shot is the one where the day does the most work. Shadow - // sweeps the length of two mountain ranges, and then the thing the relief - // was hiding turns up: the cities, which are somewhere else entirely. + /** + * The region shot, which is now the weather film as much as the light one. + * + * This is the only one of the three city cameras that is *above* the cloud + * deck — `clouds.ts` sizes its own hard case off exactly this chapter, "the + * camera 430 units above the ground with the cloud base at 52" — so it is + * the only one that watches the marine layer from on top rather than from + * under it. And with no weather feed wired, which is what a capture against + * a local `dist/` always is, there is a marine layer to watch: `atmosphere.ts` + * models the cover from the layer's own diurnal curve on apparent solar time + * rather than defaulting to an empty sky. Over San Francisco on this date the + * model runs about 0.88 before dawn, thins to 0.27 by mid-afternoon, and is + * back over 0.8 by nine. + * + * **Do not judge this one from a rough cut**, and the reason is a trap worth + * writing down. Cover is eased toward its target over a six-second time + * constant in *real* time (`COVER_TAU` in `clouds.ts`), while a frame costs + * about half a second of real time no matter how many hours of sky it steps + * over — measured here at 0.51 s, by differencing an 8-frame run against a + * 28-frame one. So the deck always runs *behind* the day, and how far behind + * is decided by the frame count rather than by the weather. At `--frames 40` + * one frame is twenty-eight minutes of sky, the layer never gets near its + * target, and the board reads as permanently socked in — which it is not. + * At 180 frames a frame is six minutes and the lag through the steepest part + * of the burn-off works out around a fifth of cover: trailing, but the deck + * visibly opens and shuts. The frame count is part of what this picture *is*, + * not just how smooth it is. + * + * The poster moved off 0.20 for a related reason: 0.20 is twenty past eight + * in the morning, which is now a white rectangle. 0.51 is ten to two, the + * clearest the board gets. + */ title: "A day across the whole board", - poster: 0.2, + poster: 0.51, place: "San Francisco Bay Area", caption: - "Relief is easiest to read when the light moves. Shadow runs the length of two mountain ranges and back, and then at the end the thing the terrain never told you appears on its own: the cities, which are somewhere else entirely.", - alt: "A time-lapse of the whole San Francisco Bay Area from above. Long shadows rake across the hills as the sun crosses, and after dark the built-up ground emerges as scattered clusters of light between the ranges.", + "Relief is easiest to read when the light moves, and this camera is high enough to read the weather the same way. With nobody to ask for an observation — which is what a fresh clone of this engine has, and what this camera had — the sky is modelled from the Pacific marine layer's own daily curve instead of left empty: the deck that covers the board before dawn burns back through the afternoon and closes in again after sunset. Under it, shadow runs the length of two mountain ranges and back — and at the end the thing the terrain never told you comes through the gaps, which is the cities, which are somewhere else entirely.", + alt: "A time-lapse of the whole San Francisco Bay Area from above, seen from over a cloud deck. The cloud covers the board at dawn, thins through the middle of the day to let long shadows rake across the hills and the bay show through, then closes back in at dusk, after which the built-up ground reads as scattered clusters of light between the dark ranges.", + }, + { + /** + * The one film the stills cannot stand in for. + * + * `office-floor` in `shots.mjs` is this exact camera at two hours — 11:20 and + * 21:30 — and that pair is a fair account of both of them. What a pair cannot + * show is the part in between, which is the part that is actually the claim: + * the building has no night theme, it has a sun going down, and the fittings + * come up as it does. An event with a duration wants the medium that has one. + * + * **The window is cut to the switch-over, not to the working day.** + * `luminaires.ts` ramps the house lights from nothing at six degrees of solar + * elevation to full at zero, and over this building's own coordinate on this + * date that band is 19:38 to 20:12 — thirty-four minutes. Spread across the + * eighteen-hour window the city films use it would be six frames and a + * blink. Across these six hours it is seventeen, a little over half a second, + * with another thirty-nine frames of twilight behind it in which the fittings + * hold the building on their own. Dawn does the same thing in reverse and is + * deliberately out of frame: one legible switch-over beats two unreadable + * ones, and a sixteen-hour window buys the second one by making both a blur. + * + * The other end is chosen too. Solar noon here is 13:15 with the sun at 69°, + * which is straight down into an open-topped model and flat on every surface + * — the interior version of the midday problem the shot list keeps away from. + * By half past three the sun is at 54° and falling, and the shadows on the + * two plates have somewhere to go for the whole reel. + * + * The last frame is 21:30, which is `office-floor`'s night frame exactly. So + * the film ends on the still, and anyone who suspects one of them is lying + * can put them side by side. + * + * `chapter: 0` is the arrival pose and needs no click. It is also the only + * camera that holds all forty-eight metres of the plate and both storeys at + * once, which is what makes this read as a *building* changing state rather + * than as one room dimming. The Commons was the other candidate: it shows the + * void far better and the ceiling far worse, and the ceiling is the subject. + * + * Lumbridge HQ rather than Frontier Valley, and the hangar is the better + * second film than first — it is one storey and two robots against two + * storeys and four, its lights are a single run of trusses, and reaching it + * needs the office-picker step this script does not have. See the note above + * `FILMS`. + */ + id: "office-dusk", + door: "office", + chapter: 0, + expect: "The Floor", + from: "2026-08-06T15:30:00-07:00", + to: "2026-08-06T21:30:00-07:00", + frames: 180, + fps: 30, + /** Frame 147, twenty-five past eight: fittings at full, sky not yet black. */ + poster: 0.82, + title: "Six hours in the office, and the lights taking over", + place: "Lumbridge HQ", + caption: + "Forty-eight metres by eighteen, two storeys with fourteen metres of interstitial between them, and a sun that belongs to the building rather than to the picture: the pack declares where on the earth it stands, so this floor is lit by the real afternoon over Transbay and switches to its own fittings as that afternoon ends. The changeover is a ramp across six degrees of solar elevation rather than a theme, which is why it takes a film to show it at all. The four Optimus units walking the two floors brighten whatever fitting they pass under.", + alt: "A time-lapse of an office interior seen from outside as an open-topped model, both storeys visible at once and held a long way apart. Afternoon daylight rakes across the two floor plates and the sky behind the building warms to orange and then goes dark blue; the ceiling fittings on both levels come up as it does, until the building is lit entirely from within. Small figures sit at the desks and walk between them.", }, ]; @@ -344,22 +443,13 @@ async function film(browser, spec, dir) { } } -/** - * Frames to an MP4, plus the poster a `