mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
## 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.
97 lines
3.1 KiB
JavaScript
97 lines
3.1 KiB
JavaScript
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);
|
||
}
|
||
}
|
||
});
|