mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
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.
This commit is contained in:
1 parent
cd22ca3559
commit
0bb61f53c6
24 files changed
+1945
-205
No files matched your search
@@ -18,7 +18,9 @@ const MODE_COLOR = {
|
||||
ferry: '#5FD6FF',
|
||||
unknown: '#D8DDE5',
|
||||
};
|
||||
export const TRAIL_VERTEX_LIMIT = 2048;
|
||||
// Final Cesium batching uses 3,152 bytes per two-lane edge.
|
||||
// The 1,278-instance body plus 64 bytes/instance reserve stays below 2 MiB.
|
||||
export const TRAIL_VERTEX_LIMIT = 640;
|
||||
const STEP_M = 25;
|
||||
|
||||
/** Shared prepared paths. Floors are aligned to work with Google 3D tiles. */
|
||||
@@ -63,6 +65,7 @@ export function createTrails({ state, services, parts, source }) {
|
||||
entry.trailSegments = [];
|
||||
entry.trailVertices = 0;
|
||||
}
|
||||
const draped = entry === selected && ensureRenderer()?.diagnostics().ground;
|
||||
const paths = new Map(),
|
||||
all = [],
|
||||
a = {},
|
||||
@@ -113,16 +116,28 @@ export function createTrails({ state, services, parts, source }) {
|
||||
if (entry === selected)
|
||||
for (let i = activeIndex - 1; i >= 0; i--) order.push(i);
|
||||
entry.trailTruncated = false;
|
||||
let markerVertices = 0;
|
||||
let markerIntervals = Math.max(0, entry.track.count - 1 - activeIndex);
|
||||
for (const i of order) {
|
||||
readFix(entry.track, i, a);
|
||||
readFix(entry.track, i + 1, b);
|
||||
const markerCorridor = i >= activeIndex;
|
||||
if (markerCorridor) markerIntervals--;
|
||||
if (a.epoch !== b.epoch || b.flags & FIX_FLAGS.BREAK) continue;
|
||||
if (entry !== selected && b.t < entry.sample.displayT) continue;
|
||||
const n = Math.max(1, Math.ceil(fixDistanceM(a, b) / STEP_M));
|
||||
if (vertices + n + 1 > TRAIL_VERTEX_LIMIT) {
|
||||
entry.trailTruncated = true;
|
||||
continue;
|
||||
}
|
||||
const desired = Math.max(1, Math.ceil(fixDistanceM(a, b) / STEP_M));
|
||||
// Reserve endpoints for every future marker interval. Sparse reports
|
||||
// coarsen the active path instead of losing the vehicle to the body cap.
|
||||
const n = markerCorridor
|
||||
? Math.min(
|
||||
desired,
|
||||
TRAIL_VERTEX_LIMIT - markerVertices - 2 * markerIntervals - 1,
|
||||
)
|
||||
: desired;
|
||||
const retainBody = vertices + n + 1 <= TRAIL_VERTEX_LIMIT;
|
||||
if (!retainBody || n < desired) entry.trailTruncated = true;
|
||||
if (!markerCorridor && !retainBody) continue;
|
||||
if (markerCorridor) markerVertices += n + 1;
|
||||
const positions = [],
|
||||
heights = [];
|
||||
let valid = true;
|
||||
@@ -136,17 +151,21 @@ export function createTrails({ state, services, parts, source }) {
|
||||
heights.push(h);
|
||||
if (h === null) {
|
||||
valid = false;
|
||||
positions.push(null);
|
||||
// Ground polylines consume longitude/latitude and drape themselves.
|
||||
// Unknown floors still disqualify the marker corridor.
|
||||
positions.push(
|
||||
draped ? Cesium.Cartesian3.fromDegrees(point.lon, point.lat) : null,
|
||||
);
|
||||
} else
|
||||
positions.push(
|
||||
Cesium.Cartesian3.fromDegrees(point.lon, point.lat, h),
|
||||
);
|
||||
}
|
||||
signature += `${a.seq}/${b.seq}:${heights.join(',')};`;
|
||||
vertices += n + 1;
|
||||
if (!valid) continue;
|
||||
paths.set(a.seq, { positions, toSeq: b.seq });
|
||||
if (entry === selected && fixDistanceM(a, b) > 0) {
|
||||
if (retainBody) vertices += n + 1;
|
||||
if (valid) paths.set(a.seq, { positions, toSeq: b.seq });
|
||||
if (!valid && !draped) continue;
|
||||
if (entry === selected && retainBody && fixDistanceM(a, b) > 0) {
|
||||
for (let k = 0; k < n; k++)
|
||||
all.push({
|
||||
fromSeq: a.seq,
|
||||
@@ -216,7 +235,9 @@ export function createTrails({ state, services, parts, source }) {
|
||||
}
|
||||
function update() {
|
||||
if (!selected || !renderer) return;
|
||||
renderer.setVisible(state._enabled && selected.marker?.show !== false);
|
||||
renderer.setVisible(
|
||||
state._enabled && state._vehicles.get(selected.key) === selected,
|
||||
);
|
||||
renderer.setDisplaySample(selected.sample, selected.marker?.position);
|
||||
}
|
||||
function select(entry) {
|
||||
|
||||
Reference in new issue
Block a user