Files
gods-eye-view/src/data/transitPresetStyle.test.mjs
T
Bilawal Sidhu 0bb61f53c6 fix(transit): keep the real silhouettes under sensor presets, make the selected trail visible, put Transit in Movement (#596)
## Changes

Follow-up to #587 after the owner's first look at the merged transit layer on Google 3D tiles.

- **Sensor presets keep the real silhouettes.** Under NVG, thermal (white-hot, black-hot, Ironbow) and noir, vehicles are the same silhouettes as in normal view, filled opaque white with a 2 px dark halo, at the CRT display sizes (26 px fleet, 39 px selected). The solid rounded "signature" bodies are gone.
- **The selected vehicle's trail is visible.** The retained history is now draped onto 3D tiles and terrain (`GroundPolylinePrimitive`, `ClassificationType.BOTH`, the way the traffic heat-lines are drawn), 3 px core over a 5 px dark backing, per-segment age colour, a short draped head clipped to the sprite. Two defects kept it off screen before: Cesium's per-instance attribute getters return copies, so the renderer's show/colour edits were discarded, and a marker did not recover after the camera jumped to it, which hid the trail with it. Trail geometry is now prepared on selection independent of floor resolution and marker visibility; sparse long intervals (rail) keep a bounded marker corridor instead of dropping the interval; classification survives map-source changes.
- **Transit sits in the Movement panel group** between Street Traffic and Bike Share (the panel orders by `PANEL_GROUPS`, not by catalog order; the catalog is moved too).

## How it was verified

- `npm run format:check` (854 files), `npm run check:boundaries`, `npm test` (4,062 passing), `npm run build` clean; tracking regression 109/109 in a real browser on Google 3D tiles.
- Browser harness (`scripts/qa-transit.mjs`), keyed on Google 3D tiles and keyless: keyed 194 pass, with the trail pixel check passing at 300, 600 and 900 m; the remaining keyed failures are the per-frame allocation gate already recorded as a follow-up from #587 (CPU and frame time within budget), four readability lines at threshold edges, and the card-anchor check now counting frames where the selected marker is legitimately off screen; keyless 193 pass with the remaining misses being threshold edges on the thin tram silhouette (centre 0.83–0.88 against 0.85) and one bus ring 0.28 against 0.25 on a pure-white roof.
- New harness checks: `Google 3D selected trail visible at 6 of 8 projected samples` (repeated off/on framebuffer captures at 300/600/900 m, samples outside the sprite footprint, a background-change negative control; fails on main, passes here), a camera-arrival check (select a moving vehicle, `setView` onto it, marker shown and trail body drawn within 2 s), `PANEL_ORDER` pinned, raster tests comparing every sensor raster's mask and aspect ratio to the normal silhouette.
- Direct look on a keyed build: a live CapMetro bus at 30 m/s selected through the layer API draws its draped trail along the highway lane (harness capture `keyed-600m-trail-visible.jpg`).
- Adversarial review by Codex Astra before the last two fix passes (stack-switch classification, marker corridor under the vertex cap, pixel-gate loophole), all three fixed with regressions.

Docs: `docs/CURRENT-STATE.md` and `CHANGELOG.md` updated in this PR. No data-source changes.
2026-09-15 13:26:41 -05:00

97 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from 'node:test';
import assert from 'node:assert/strict';
import {
TRANSIT_TIER_KEYS,
presetSpriteOutlinePx,
presetSpriteRgba,
presetSpriteScale,
transitModeTier,
transitStyleProfile,
} from './transitPresetStyle.js';
import { TRANSIT_MODES } from './transitFeeds.js';
test('profile mapping: NVG/FLIR/noir and cockpit nvg are mono, retro is crt, everything else normal', () => {
for (const mono of ['surveillance', 'thermal', 'noir', 'nvg'])
assert.equal(transitStyleProfile(mono), 'mono', mono);
assert.equal(transitStyleProfile('retro'), 'crt');
for (const plain of [
'normal',
'anime',
'snow',
'minecraft',
'',
null,
undefined,
'optical',
])
assert.equal(transitStyleProfile(plain), 'normal', String(plain));
});
test('normal uses mode colour, unit scale and a one-pixel halo', () => {
for (const mode of TRANSIT_MODES) {
assert.equal(presetSpriteRgba('normal', mode), null);
assert.equal(presetSpriteRgba(undefined, mode), null);
}
assert.equal(presetSpriteScale('normal'), 1);
assert.equal(presetSpriteOutlinePx('normal'), 1);
});
test('mono: every mode is a white core, larger, with a dark halo in screen pixels', () => {
for (const style of ['surveillance', 'thermal', 'noir', 'nvg']) {
for (const mode of TRANSIT_MODES) {
assert.deepEqual(
presetSpriteRgba(style, mode),
[255, 255, 255, 1],
`${style} ${mode}`,
);
}
for (const selected of [false, true]) {
assert.equal(
presetSpriteScale(style, selected),
presetSpriteScale('retro', selected),
);
assert.equal(presetSpriteOutlinePx(style, selected), 2);
}
}
});
test('crt: saturated per-mode colours, distinct, upsized, thinner halo', () => {
const seen = new Set();
for (const mode of TRANSIT_MODES) {
const rgba = presetSpriteRgba('retro', mode);
assert.ok(Array.isArray(rgba) && rgba.length === 4, mode);
seen.add(rgba.slice(0, 3).join(','));
const [r, g, b] = rgba;
assert.ok(
Math.max(r, g, b) === 255,
`${mode} is fully saturated on one channel`,
);
}
assert.equal(seen.size, TRANSIT_MODES.length, 'no two modes share a colour');
assert.ok(presetSpriteScale('retro') > 1.2);
assert.ok(
presetSpriteOutlinePx('retro') > 0 &&
presetSpriteOutlinePx('retro') < presetSpriteOutlinePx('thermal'),
);
});
test('every mode has its own bracket tier, unknown modes fall to transit_unknown', () => {
const tiers = new Set();
for (const mode of TRANSIT_MODES) tiers.add(transitModeTier(mode));
assert.equal(tiers.size, TRANSIT_MODES.length);
assert.equal(transitModeTier('spaceship'), 'transit_unknown');
assert.equal(transitModeTier(undefined), 'transit_unknown');
assert.deepEqual([...tiers].sort(), [...TRANSIT_TIER_KEYS].sort());
});
test('all styled tuples are valid rgba: channels 0–255 ints, alpha in (0, 1]', () => {
for (const style of ['thermal', 'retro']) {
for (const mode of TRANSIT_MODES) {
const [r, g, b, a] = presetSpriteRgba(style, mode);
for (const c of [r, g, b])
assert.ok(Number.isInteger(c) && c >= 0 && c <= 255);
assert.ok(a > 0 && a <= 1);
}
}
});