Files
gods-eye-view/scripts/qa-transit-scenes.mjs
T
German Khokhlov aa6959c97b Restore ground, picking and horizon parity through the globe contract
Draw launch-pad zones as radial ground discs with the original fill, rim,
transitions, emission and depth bias. Drape Transit history and its backing
on ground corridors. Keep the current age bands and once-per-second updates;
advance the active head with a reveal fraction and rebuild only at segment
crossings. Its wider dark edge and colour core blend once, as on Cesium.
Navara composes these looks after tone mapping.

Build reveal corridors and dynamic Transit history synchronously once shared
terrain bounds are loaded. Keep requesting frames until the visible globe
tiles and committed primitives are ready: classification needs current
surface depth after a camera move. Retain the reference radial material,
its alpha envelope and the head's material-only reveal updates.

Remove the empty Navara ground effect from the render chain when the last
shown set is hidden or destroyed. It otherwise remains as the insertion
anchor for later pulse effects after its packed textures have been disposed.
Cover reveal, destruction and replacement with fresh-state comparisons for
corridors, polygons, discs and cells. Cesium drawing is unchanged by this fix.

Keep the radial pixel check at a 500 metre radius and 4 kilometre view,
sample the brightest of six inner points around the native fill gap, and
sample across the rim width. Restore its view during arrival flights and
run it last among ground checks. Both suite launchers wait for the linked
view to arrive before checking. Keep these checks with the radial look.

Pass rectangular pick windows and drill limits through input subscriptions.
Street Level uses its 9 pixel window and CCTV uses its 14 pixel window and
six-result drill. Down picks stay synchronous, moves coalesce per frame and
Navara clicks keep the GPU answer from their gesture. Add a projector
ellipsoid option and use WGS84 radii reduced by 1,000 metres for Street Level.

These changes restore the original Cesium pad fill and ground trail rather
than the temporary ring and raised lines. Navara follows the same appearance
and horizon policy, and now honours explicit CPU pick windows. Existing
callers without these options retain their defaults. The four debts share
the contract, suite and engine implementations, so keep them together.

No coupling count grew or fell: renderer lines remain 0 and 189, external
Cesium imports 3, external viewer lines 891, engine-kind checks 1, engine
resolution calls 0, extension reads 3, ground passes 1, SDK loads 3, screen
handler files 4 and engine-name lines 32.

The contract and ground suites now wait until the camera has reached the
view in their link before checking, because the app's restore flight could
land in the middle of a check; that was also the cause of the intermittent
camera-events failure. The radial check uses a launch pad's size and samples
the fill at several inner points: Cesium's own old pad leaves the same gap in
a small ground disc's fill. The pulse-band check takes its base frame once
the surface has settled.

Checked in the browser, keyless: the contract suite passes on both engines
(84 and 85 checks), the ground suite passes on both, CCTV v2 passes 58
checks, and the space, transit, Street Level and CCTV engine switches pass.
The transit and Street Level scripts fail as they do before this change.
2026-11-08 00:30:36 +04:00

1099 lines
37 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 { captureQaScreenshot } from './qa-chrome.mjs';
import { getRegisteredTransitFeed } from '../src/data/transitFeeds.js';
// Not registered or active: camera discovery and live polls never own this feed.
/** CapMetro metadata under a private QA identity for selected-trail pixels. */
export const TRAIL_VISIBILITY_FEED = Object.freeze({
...getRegisteredTransitFeed('capmetro-austin'),
id: 'qa-trail-capmetro',
});
import { reportTransitVisibility } from './qa-transit-browser.mjs';
import { writeFile } from 'node:fs/promises';
import {
reduceFleet,
reduceMotion,
reduceBackground,
reduceSensorContrast,
reduceTrailPixels,
} from '../src/layers/transit/qaMetrics.js';
/** CDP includes objects collected during the sample, not just surviving objects. */
export function allocationTotals(profile) {
let frameBytes = 0,
layerFrameBytes = 0,
sharedOverlayBytes = 0,
qaBytes = 0;
const allocators = new Map();
function visit(node, inFrame = false, inQa = false, inOverlay = false) {
const frame = node.callFrame || {},
name = frame.functionName || '',
url = frame.url || '';
const nested =
inFrame ||
name === 'onPreRender' ||
/^render\d*$/.test(name) ||
/^Scene\d*\.render$/.test(name);
// QA wraps the real frame callback: re-enter production attribution at its
// onPreRender boundary, but exclude QA's own Math/Cesium helper descendants.
const qa =
name === 'onPreRender'
? false
: inQa || /\/transit\/testing\.js(?:\?|$)/.test(url);
const overlay = inOverlay || name === '_drawOverlay';
const bytes = node.selfSize || 0;
if (nested) {
if (qa) qaBytes += bytes;
else {
frameBytes += bytes;
const allocator = `${name || '(anonymous)'} ${url.split('?')[0]}:${(frame.lineNumber ?? -1) + 1}`;
allocators.set(allocator, (allocators.get(allocator) || 0) + bytes);
if (overlay) sharedOverlayBytes += bytes;
if (
/\/layers\/transit\/|\/data\/(?:contactPlayback|contactTrailRenderer|transit[^/]*)\.js/.test(
url,
)
)
layerFrameBytes += bytes;
}
}
for (const child of node.children || []) visit(child, nested, qa, overlay);
}
visit(profile.head);
return {
frameBytes,
layerFrameBytes,
sharedOverlayBytes,
qaBytes,
topAllocators: [...allocators]
.sort((a, b) => b[1] - a[1])
.slice(0, 3)
.map(([allocator, bytes]) => ({ allocator, bytes })),
};
}
export async function runFleetBudgets({
page,
check,
wait,
shots,
tag,
detectOn,
selectStyle,
}) {
console.log(
'\n== moving fleet budgets: 1920×1080 CSS, warmed view, DETECT dense ==',
);
await selectStyle(page, 'normal');
await detectOn(page);
const cdp = await page.createCDPSession();
await cdp.send('HeapProfiler.enable');
for (const count of [800, 3000]) {
await page.evaluate(async (count) => {
const app = window.__godsEyeView;
await app.dataManager.setEnabled('transit', true, { source: 'qa' });
const camera = app.viewer.camera,
C = camera.positionCartographic.constructor;
camera.setView({
destination: C.toCartesian(
C.fromDegrees(-71.0605, 42.3554, count === 800 ? 600 : 30000),
),
orientation: { heading: 0, pitch: -Math.PI / 2, roll: 0 },
});
const layer = app.dataManager.layers.get('transit').module;
layer._transitPartsForTest().ingestion.abortAllInFlight();
layer._transitStateForTest()._activeFeeds.clear();
for (const key of layer._transitStateForTest()._vehicles.keys())
layer._transitPartsForTest().ingestion.removeVehicle(key);
}, count);
await wait(8000);
// Camera proximity can finish a live poll during tile warm-up. Remove it
// before the heap baseline, or its memory would discount the fixture fleet.
await page.evaluate(() => {
const layer =
window.__godsEyeView.dataManager.layers.get('transit').module;
const state = layer._transitStateForTest(),
parts = layer._transitPartsForTest();
parts.ingestion.abortAllInFlight();
state._activeFeeds.clear();
clearTimeout(state._cameraDebounceTimer);
state._cameraDebounceTimer = null;
for (const key of state._vehicles.keys())
parts.ingestion.removeVehicle(key);
parts.rendering.syncRenderHold();
window.__godsEyeView.viewer.scene.requestRender();
});
await wait(500);
await cdp.send('HeapProfiler.collectGarbage');
const before = await cdp.send('Runtime.getHeapUsage');
const loaded = await page.evaluate(
(count) =>
window.__godsEyeView.dataManager.layers
.get('transit')
.module._loadTransitFleetForTest(
count,
{ lat: 42.3554, lon: -71.0605 },
count === 800 ? 5 : 120,
),
count,
);
await reportTransitVisibility(
page,
`${count} budget visibility after load`,
);
await wait(2000);
await cdp.send('HeapProfiler.collectGarbage');
// The retained delta is conservative: it includes concurrent page growth.
const retained = await cdp.send('Runtime.getHeapUsage');
// Timing/upload acceptance runs without the allocation profiler attached.
const trace = await page.evaluate(() =>
window.__godsEyeView.dataManager.layers
.get('transit')
.module._measureTransitFramesForTest(8000),
);
await page.evaluate(() =>
window.__godsEyeView.dataManager.layers
.get('transit')
.module._resetTransitFixtureClockForTest(),
);
await wait(1500);
await cdp.send('HeapProfiler.startSampling', {
samplingInterval: 128,
includeObjectsCollectedByMajorGC: true,
includeObjectsCollectedByMinorGC: true,
});
await reportTransitVisibility(
page,
`${count} budget visibility before allocation trace`,
);
const allocationTrace = await page.evaluate(() =>
window.__godsEyeView.dataManager.layers
.get('transit')
.module._measureTransitFramesForTest(6000),
);
const { profile } = await cdp.send('HeapProfiler.stopSampling');
const allocations = allocationTotals(profile);
const retainedBytes = retained.usedSize - before.usedSize;
const evidence = reduceFleet(
trace.rows,
count,
allocations.layerFrameBytes / allocationTrace.rows.length,
retainedBytes,
loaded.fixBytes,
);
const details = {
...evidence,
...allocations,
wholeFrameBytesPerFrame:
allocations.frameBytes / allocationTrace.rows.length,
topAllocatorsPerFrame: allocations.topAllocators.map((a) => ({
...a,
bytesPerFrame: a.bytes / allocationTrace.rows.length,
})),
allocationFrames: allocationTrace.rows.length,
loaded,
dpr: trace.dpr,
viewport: [trace.width, trace.height],
heapMethod:
'GC-bracketed whole-page growth after fleet load (conservative)',
allocationMethod:
'CDP 128-byte sampling including collected objects; transit source self allocation gates, whole render stacks reported, QA scaffolding excluded',
};
await writeFile(
`${shots}/${tag}-fleet-${count}.json`,
JSON.stringify(details, null, 2),
);
await writeFile(
`${shots}/${tag}-fleet-${count}-allocations.json`,
JSON.stringify(profile),
);
check(
`${count} moving vehicles meet CPU, frame interval, billboard upload, heap and allocation budgets`,
evidence.pass && trace.width === 1920 && trace.height === 1080,
JSON.stringify(details),
);
check(
`${count}: transit-authored frame allocation <= 8 KiB`,
allocationTrace.rows.length >= 100 &&
reduceMotion(allocationTrace.rows).pass &&
allocationTrace.rows.every((r) => r.moving === count) &&
allocations.layerFrameBytes / allocationTrace.rows.length <= 8192,
`${allocations.layerFrameBytes / allocationTrace.rows.length} transit-authored bytes/frame; whole frame ${allocations.frameBytes / allocationTrace.rows.length}; top three ${JSON.stringify(details.topAllocatorsPerFrame)}; collected objects included. Full profile saved.`,
);
await captureQaScreenshot(page, {
path: `${shots}/${tag}-fleet-${count}.jpg`,
type: 'jpeg',
quality: 85,
});
const settled = await page.evaluate(async () => {
const layer =
window.__godsEyeView.dataManager.layers.get('transit').module;
const state = layer._transitStateForTest();
const { seek } = await import('/src/data/contactPlayback.js');
const { updatePlayback } =
await import('/src/layers/transit/movement.js');
for (const entry of state._vehicles.values()) {
entry.clocks.wallNowMs = Date.now();
entry.clocks.monoNowMs = performance.now();
seek(entry.track, entry.fixes.at(-1).t, entry.clocks);
updatePlayback(entry, Date.now(), performance.now());
layer._transitPartsForTest().rendering.schedulePlayback(entry);
}
layer._transitPartsForTest().rendering.syncRenderHold();
return { moving: state._moving.size, held: state._renderHeld };
});
check(
`${count}: settling releases the transit hold`,
settled.moving === 0 && !settled.held,
JSON.stringify(settled),
);
}
await cdp.detach();
}
export async function runBostonMatrix({
page,
check,
wait,
shots,
tag,
selectStyle,
sampleRendered,
detectOn,
}) {
console.log(
'\n== Boston matrix: scripted Red, 741, 742, Green-E; actual map backgrounds ==',
);
await detectOn(page);
const routes = ['Red', '741', '742', 'Green-E'];
const looks = [
{ name: 'normal', style: 'normal', params: {} },
{ name: 'nvg', style: 'surveillance', params: {} },
{ name: 'noir', style: 'noir', params: {} },
{
name: 'white-hot',
style: 'thermal',
params: { 'WHOT/BHOT': 0, Ironbow: 0 },
},
{
name: 'black-hot',
style: 'thermal',
params: { 'WHOT/BHOT': 1, Ironbow: 0 },
},
{
name: 'ironbow',
style: 'thermal',
params: { 'WHOT/BHOT': 0, Ironbow: 1 },
},
];
// Actual Boston views. Acceptance checks the measured background; a tile/key
// failure or an unsuitable view is explicitly unexercised, never assumed bright/dark.
const backgrounds = [
{
name: 'bright',
candidates: [
{ lat: 42.3458, lon: -71.0464 },
{ lat: 42.3659, lon: -71.018 },
{ lat: 42.3526, lon: -71.0461 },
{ lat: 42.3486, lon: -71.0825 },
],
},
{
name: 'dark',
candidates: [
{ lat: 42.3574, lon: -71.0375 },
{ lat: 42.3554, lon: -71.0655 },
{ lat: 42.3588, lon: -71.0738 },
{ lat: 42.348, lon: -71.032 },
],
},
];
const evidence = [];
for (const group of backgrounds) {
let background, backgroundResult;
const attempts = [];
await selectStyle(page, 'normal');
for (const candidate of group.candidates) {
background = { ...candidate, name: group.name };
await page.evaluate((b) => {
const camera = window.__godsEyeView.viewer.camera,
C = camera.positionCartographic.constructor;
camera.setView({
destination: C.toCartesian(C.fromDegrees(b.lon, b.lat, 600)),
orientation: { heading: 0, pitch: -Math.PI / 2, roll: 0 },
});
}, background);
await wait(8000);
await page.evaluate(
({ background, routes }) => {
const layer =
window.__godsEyeView.dataManager.layers.get('transit').module;
layer._loadTransitFleetForTest(8, background, 45, routes);
for (const entry of layer._transitStateForTest()._vehicles.values()) {
entry.track.rate = 0;
layer._transitPartsForTest().rendering.schedulePlayback(entry);
}
},
{ background, routes },
);
await wait(1000);
backgroundResult = reduceBackground(
group.name,
await sampleRendered(page),
);
attempts.push({ background, ...backgroundResult });
if (backgroundResult.pass) break;
}
check(
`Boston ${background.name}: all four routes over measured ${background.name} background`,
backgroundResult.pass,
JSON.stringify({ attempts, chosen: background }),
{ unexercised: !backgroundResult.pass },
);
for (const look of looks) {
await selectStyle(page, look.style, look.params);
await wait(1200);
await reportTransitVisibility(
page,
`Boston ${background.name}/${look.name} visibility`,
);
const pixels = await sampleRendered(page);
const modes = await page.evaluate(() => {
const layer =
window.__godsEyeView.dataManager.layers.get('transit').module;
return [...layer._transitStateForTest()._vehicles.values()].map(
(e) => ({
key: e.key,
route: e.record.routeId,
image: e.marker.image,
width: e.marker.width,
color: e.marker.color.toCssColorString(),
}),
);
});
const contrast =
look.name === 'normal'
? null
: reduceSensorContrast(
pixels,
look.name === 'black-hot' ? 'black' : 'white',
look.style,
);
check(
`Boston ${background.name}/${look.name}: all four silhouettes and mode brackets readable`,
pixels.length >= 6 &&
pixels.every(
(p) =>
p.bracket &&
p.bracketExpected &&
Math.hypot(...p.bracket.map((v, i) => v - p.bracketExpected[i])) <
48,
) &&
(!contrast || contrast.pass),
JSON.stringify({ pixels, contrast }),
{ unexercised: pixels.length < 6 },
);
await captureQaScreenshot(page, {
path: `${shots}/${tag}-matrix-${background.name}-${look.name}-fleet.jpg`,
type: 'jpeg',
quality: 90,
});
for (const target of modes) {
await page.evaluate(
(key) =>
window.__godsEyeView.dataManager.layers
.get('transit')
.module._transitPartsForTest()
.selection.selectVehicle(key),
target.key,
);
await wait(500);
await captureQaScreenshot(page, {
path: `${shots}/${tag}-matrix-${background.name}-${look.name}-${target.route}-selected.jpg`,
type: 'jpeg',
quality: 90,
});
const restored = await page.evaluate((key) => {
const layer =
window.__godsEyeView.dataManager.layers.get('transit').module;
layer._transitPartsForTest().selection.clearSelection();
const e = layer._transitStateForTest()._vehicles.get(key);
return {
image: e.marker.image,
width: e.marker.width,
color: e.marker.color.toCssColorString(),
};
}, target.key);
check(
`Boston ${background.name}/${look.name}/${target.route}: deselect restores fleet styling`,
restored.image === target.image &&
restored.width === target.width &&
restored.color === target.color,
);
}
await wait(300);
await captureQaScreenshot(page, {
path: `${shots}/${tag}-matrix-${background.name}-${look.name}-restored.jpg`,
type: 'jpeg',
quality: 90,
});
evidence.push({
background,
look,
pixels,
routes: modes.map((m) => m.route),
});
}
}
await selectStyle(page, 'normal');
await writeFile(
`${shots}/${tag}-boston-matrix.json`,
JSON.stringify(evidence, null, 2),
);
}
/** Real MBTA observations at oblique street views, separate from matrix fixtures. */
export async function runBostonLive({
page,
check,
wait,
shots,
tag,
detectOn,
selectStyle,
sampleRendered,
}) {
console.log('\n== Boston live oblique streets ==');
await page.evaluate(async () => {
const app = window.__godsEyeView;
await app.dataManager.setEnabled('transit', false, { source: 'qa' });
const camera = app.viewer.camera,
C = camera.positionCartographic.constructor;
camera.setView({
destination: C.toCartesian(C.fromDegrees(-71.0605, 42.3554, 600)),
orientation: { heading: 0, pitch: -Math.PI / 4, roll: 0 },
});
await app.dataManager.setEnabled('transit', true, { source: 'qa' });
const layer = app.dataManager.layers.get('transit').module;
layer._setTransitFixtureFloorsForTest([], 0);
await layer.update();
});
await wait(20000);
await detectOn(page);
const evidence = [];
for (const view of [
{
name: 'white-hot',
style: 'thermal',
altitude: 120,
heading: 0,
pitch: -20,
params: { 'WHOT/BHOT': 0, Ironbow: 0 },
},
{
name: 'nvg',
style: 'surveillance',
altitude: 600,
heading: 30,
pitch: -45,
params: {},
},
{
name: 'noir',
style: 'noir',
altitude: 400,
heading: 60,
pitch: -35,
params: {},
},
]) {
await page.evaluate((view) => {
const app = window.__godsEyeView,
camera = app.viewer.camera,
C = camera.positionCartographic.constructor;
app.dataManager.layers
.get('transit')
.module._transitPartsForTest()
.selection.clearSelection();
camera.setView({
destination: C.toCartesian(
C.fromDegrees(-71.0605, 42.3554, view.altitude),
),
orientation: {
heading: (view.heading * Math.PI) / 180,
pitch: (view.pitch * Math.PI) / 180,
roll: 0,
},
});
}, view);
await selectStyle(page, view.style, view.params);
await wait(8000);
const fleet = await page.evaluate(() => {
const state = window.__godsEyeView.dataManager.layers
.get('transit')
.module._transitStateForTest();
const entries = [...state._vehicles.values()];
return {
total: entries.length,
visible: entries.filter((e) => e.marker?.show).length,
fixtures: entries.filter((e) => e.qaFixture).length,
routes: [
...new Set(
entries.filter((e) => e.marker?.show).map((e) => e.record.routeId),
),
],
};
});
check(
`Boston live/${view.name}: observed vehicles in the oblique street view`,
fleet.total > 0 && fleet.visible > 0 && fleet.fixtures === 0,
JSON.stringify({ view, fleet }),
{ unexercised: fleet.visible === 0 },
);
const pixels = await sampleRendered(page);
const contrast = reduceSensorContrast(pixels, 'white', view.style);
check(
`Boston live/${view.name}: unobscured sensor contrast`,
contrast.pass,
JSON.stringify(contrast),
{ unexercised: contrast.unexercised },
);
const target = pixels[0];
let selection = null;
if (target) {
await page.mouse.click(target.x, target.y);
await wait(250);
selection = await page.evaluate((key) => {
const state = window.__godsEyeView.dataManager.layers
.get('transit')
.module._transitStateForTest();
return {
expected: key,
actual: state._selectedKey,
pick: state._lastPickForTest ?? null,
};
}, target.key);
}
check(
`Boston live/${view.name}: real mouse selects an observed vehicle`,
selection?.actual === target?.key && !!target,
JSON.stringify(selection),
{ unexercised: !target },
);
await captureQaScreenshot(page, {
path: `${shots}/${tag}-boston-live-${view.name}.jpg`,
type: 'jpeg',
quality: 90,
});
evidence.push({ view, fleet, pixels, contrast, selection });
}
await writeFile(
`${shots}/${tag}-boston-live.json`,
JSON.stringify(evidence, null, 2),
);
}
/** Google 3D proof: paired framebuffer reads reject coincidental road colours. */
export async function runTrailVisibility({
page,
check,
wait,
shots,
tag,
selectStyle,
altitude = 600,
pitch = 45,
}) {
const name = 'Google 3D selected trail visible at 6 of 8 projected samples';
await selectStyle(page, 'normal');
await page.evaluate(
async ({ altitude, pitch }) => {
const app = window.__godsEyeView;
await app.dataManager.setEnabled('transit', true, { source: 'qa' });
if (app.mapStackController.isStackAvailable('photoreal'))
await app.mapStackController.setStack('photoreal');
const camera = app.viewer.camera,
C = camera.positionCartographic.constructor;
camera.setView({
destination: C.toCartesian(
C.fromDegrees(
-97.7431,
30.26725 - altitude / Math.tan((pitch * Math.PI) / 180) / 111320,
altitude,
),
),
orientation: { heading: 0, pitch: (-pitch * Math.PI) / 180, roll: 0 },
});
},
{ altitude, pitch },
);
await wait(8000);
const setup = await page.evaluate(
async ({ altitude, pitch, feed }) => {
const app = window.__godsEyeView,
scene = app.viewer.scene;
const layer = app.dataManager.layers.get('transit').module;
const state = layer._transitStateForTest(),
parts = layer._transitPartsForTest();
const google =
!!app.tileset &&
app.tileset.show !== false &&
app.mapStackController.getActiveId() === 'photoreal';
if (!google)
return { google: false, reason: 'visible Google 3D tileset required' };
const C = scene.camera.positionCartographic.constructor;
const centreLat = 30.267 + 90 / 111320;
const floor = scene.sampleHeight(C.fromDegrees(-97.7431, centreLat));
if (Number.isFinite(floor))
scene.camera.setView({
destination: C.toCartesian(
C.fromDegrees(
-97.7431,
centreLat - altitude / Math.tan((pitch * Math.PI) / 180) / 111320,
floor + altitude,
),
),
orientation: { heading: 0, pitch: (-pitch * Math.PI) / 180, roll: 0 },
});
// The trail draws through engine.ground (render/display.js); keep the
// renderer the selection below creates, the handle that shows and
// hides the whole trail.
const createLine = app.engine.ground.createSet.bind(app.engine.ground);
window.__qaTransitLineKinds = new Map();
app.engine.ground.createSet = (options) => {
const line = createLine(options);
if (options.id.startsWith('transit-trail-'))
window.__qaTransitLineKinds.set(
options.id,
options.order >= 2 ? 'head' : 'body',
);
return line;
};
window.__restoreTransitLines = () => {
app.engine.ground.createSet = createLine;
};
const createTrailRenderer = parts.renderer.createTrailRenderer;
parts.renderer.createTrailRenderer = (...args) => {
parts.renderer.createTrailRenderer = createTrailRenderer;
return (window.__qaTransitTrail = createTrailRenderer.apply(
parts.renderer,
args,
));
};
parts.selection.clearSelection();
parts.ingestion.abortAllInFlight();
state._activeFeeds.clear();
for (const key of state._vehicles.keys())
parts.ingestion.removeVehicle(key);
clearTimeout(state._cameraDebounceTimer);
state._cameraDebounceTimer = null;
const { seek, setRate } = await import('/src/data/contactPlayback.js');
const now = Date.now();
// Nine straight street fixes, 30 m apart. Use the production surface path.
for (let i = 0; i < 9; i++) {
const timestamp = (now - 120000 + i * 15000) / 1000;
parts.ingestion.applySnapshot(
feed,
{
fetchedAt: timestamp * 1000,
vehicles: [
{
id: 'trail-visible',
routeId: '1',
tripId: 'trail-visible',
lat: 30.267 + (i * 30) / 111320,
lon: -97.7431,
timestamp,
timestampSource: 'vehicle',
bearing: 0,
},
],
},
{ stale: false },
);
}
const entry = state._vehicles.get(`${feed.id}:trail-visible`);
entry.qaFixture = true;
const clocks = { wallNowMs: now, monoNowMs: performance.now() };
seek(entry.track, now - 45000, clocks);
setRate(entry.track, 1, clocks);
parts.rendering.sampleIdle(entry);
parts.height.anchorFloors();
parts.rendering.refreshVisibility();
parts.rendering.maintainPresentation();
parts.selection.selectVehicle(entry.key);
return {
google,
key: entry.key,
fixes: entry.track.count,
startDisplayT: now - 45000,
startLat: entry.sample.lat,
};
},
{ altitude, pitch, feed: TRAIL_VISIBILITY_FEED },
);
if (!setup.google) {
check(name, false, JSON.stringify(setup), { unexercised: true });
return;
}
// Measure the engine's native uploads, attributing its line primitives to
// the head or body by the draw order supplied at contract creation.
const cost = await page.evaluate(async (key) => {
const app = window.__godsEyeView,
scene = app.viewer.scene;
const layer = app.dataManager.layers.get('transit').module;
const parts = layer._transitPartsForTest(),
entry = layer._transitStateForTest()._vehicles.get(key);
const gl = scene.context._gl,
originals = new Map(),
wrapped = new Map();
let owner = null,
headBytes = 0,
bodyBytes = 0,
headUploadMax = 0,
bodyUploadMax = 0,
frames = 0;
const headCpu = [];
const wrap = (primitive, kind) => {
if (!primitive || wrapped.has(primitive)) return;
const update = primitive.update;
wrapped.set(primitive, update);
primitive.update = function (frameState) {
const before = performance.now();
owner = kind;
try {
return update.call(this, frameState);
} finally {
owner = null;
if (kind === 'head') headCpu.push(performance.now() - before);
}
};
};
for (const name of ['bufferData', 'bufferSubData']) {
const original = gl[name];
originals.set(name, original);
gl[name] = function (...args) {
if (owner) {
const value = args[name === 'bufferData' ? 1 : 2],
offset = args[3] || 0,
length = args[4];
const bytes =
typeof value === 'number'
? value
: value
? length !== undefined
? length * (value.BYTES_PER_ELEMENT || 1)
: value.byteLength - offset * (value.BYTES_PER_ELEMENT || 1)
: 0;
if (owner === 'head') headBytes += bytes;
else bodyBytes += bytes;
}
return original.apply(this, args);
};
}
const pre = scene.preRender.addEventListener(() => {
headBytes = bodyBytes = 0;
const walk = (collection) => {
for (let i = 0; i < collection.length; i++) {
const primitive = collection.get(i);
const kind = window.__qaTransitLineKinds.get(primitive?.gevGroundSet);
if (kind) wrap(primitive, kind);
else if (typeof primitive?.get === 'function') walk(primitive);
}
};
walk(scene.primitives);
walk(scene.groundPrimitives);
});
const post = scene.postRender.addEventListener(() => {
frames++;
headUploadMax = Math.max(headUploadMax, headBytes);
bodyUploadMax = Math.max(bodyUploadMax, bodyBytes);
});
try {
// Include a full history upload, even if the first selection already rendered.
entry.pathRevision = -1;
parts.trails.prepareEntry(entry);
scene.requestRender();
await new Promise((resolve) => setTimeout(resolve, 20000));
headCpu.sort((a, b) => a - b);
return {
frames,
headUploadMax,
bodyUploadMax,
headCpuP95Ms: headCpu[Math.floor(headCpu.length * 0.95)] ?? null,
headCpuMaxMs: headCpu.at(-1) ?? null,
};
} finally {
pre();
post();
window.__restoreTransitLines();
for (const [name, original] of originals) gl[name] = original;
for (const [primitive, update] of wrapped)
if (!primitive.isDestroyed()) primitive.update = update;
}
}, setup.key);
const result = await page.evaluate(async (setup) => {
const app = window.__godsEyeView,
scene = app.viewer.scene;
const layer = app.dataManager.layers.get('transit').module;
const state = layer._transitStateForTest(),
parts = layer._transitPartsForTest();
const entry = state._vehicles.get(setup.key);
const trail = parts.trails.diagnostics();
if (!entry || !trail)
return { ...setup, present: 0, reason: 'selection disappeared' };
const { setRate } = await import('/src/data/contactPlayback.js');
setRate(entry.track, 0, {
wallNowMs: Date.now(),
monoNowMs: performance.now(),
});
parts.rendering.sampleIdle(entry);
parts.rendering.schedulePlayback(entry);
parts.rendering.syncRenderHold();
const ready = () =>
app.tileset?.show !== false &&
app.tileset?.tilesLoaded === true &&
(scene.globe?.show === false || scene.globe?.tilesLoaded === true) &&
trail.body?.ready === true &&
(!trail.headPrimitive || trail.headPrimitive.ready === true);
// Require sustained readiness after freezing playback, before projecting
// onto the refined surface. Repeated captures still check pixel stability.
const tilesReady = await new Promise((resolve) => {
const started = performance.now();
let readySince = null;
const timer = setInterval(() => {
const now = performance.now();
readySince = ready() ? (readySince ?? now) : null;
scene.requestRender();
if (
(readySince !== null && now - readySince >= 500) ||
now - started >= 30000
) {
clearInterval(timer);
resolve(readySince !== null && now - readySince >= 500);
}
}, 80);
});
if (!tilesReady)
return {
...setup,
present: 0,
reason: 'tiles or trail geometry did not become ready',
};
const canvas = scene.canvas,
gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
const scale = canvas.width / canvas.clientWidth;
const points = entry.trailSegments
.filter((s) => s.toT <= entry.sample.displayT)
.flatMap((s) => s.positions);
if (trail.head?.show) points.push(...trail.head.positions);
if (points.length < 2)
return { ...setup, present: 0, reason: 'no completed path' };
const C = scene.camera.positionCartographic.constructor;
// Marker positions are plain { x, y, z } now (the display's handles), not
// Cesium Cartesian3 instances with static helpers.
const V = {
distance: (a, b) => Math.hypot(a.x - b.x, a.y - b.y, a.z - b.z),
lerp: (a, b, t) => ({
x: a.x + (b.x - a.x) * t,
y: a.y + (b.y - a.y) * t,
z: a.z + (b.z - a.z) * t,
}),
};
// What the floor samples leave out: the Cesium primitives the engine
// draws the transit mover sets and trail lines with.
const drawnByTransit = [];
const walk = (collection) => {
for (let i = 0; i < collection.length; i++) {
const item = collection.get(i);
if (
String(item?.gevMoverSet ?? '').startsWith('transit') ||
String(item?.gevGroundSet ?? '').startsWith('transit-trail-')
)
drawnByTransit.push(item);
else if (typeof item?.get === 'function' && 'length' in item)
walk(item);
}
};
walk(scene.primitives);
walk(scene.groundPrimitives);
const lengths = [0];
for (let i = 1; i < points.length; i++)
lengths.push(lengths.at(-1) + V.distance(points[i - 1], points[i]));
const mul = (m, v) =>
[0, 1, 2, 3].map(
(i) =>
m[i] * v[0] + m[i + 4] * v[1] + m[i + 8] * v[2] + m[i + 12] * v[3],
);
const samples = Array.from({ length: 8 }, (_, i) => {
const d = (lengths.at(-1) * (i + 1)) / 9;
let j = 1;
while (j < lengths.length - 1 && lengths[j] < d) j++;
const p = V.lerp(
points[j - 1],
points[j],
(d - lengths[j - 1]) / Math.max(0.001, lengths[j] - lengths[j - 1]),
);
const cart = C.fromCartesian(p);
const height = scene.sampleHeight(cart, drawnByTransit);
if (Number.isFinite(height)) cart.height = height;
const surface = C.toCartesian(cart);
const q = mul(
scene.camera.frustum.projectionMatrix,
mul(scene.camera.viewMatrix, [surface.x, surface.y, surface.z, 1]),
);
return {
x: ((q[0] / q[3]) * 0.5 + 0.5) * canvas.clientWidth,
y: (0.5 - (q[1] / q[3]) * 0.5) * canvas.clientHeight,
inFront: q[3] > 0,
radius: (Math.SQRT2 * Math.ceil(3 * scale)) / scale,
};
});
const marker = entry.marker.position;
const markerClip = mul(
scene.camera.frustum.projectionMatrix,
mul(scene.camera.viewMatrix, [marker.x, marker.y, marker.z, 1]),
);
const sprite = {
x: ((markerClip[0] / markerClip[3]) * 0.5 + 0.5) * canvas.clientWidth,
y: (0.5 - (markerClip[1] / markerClip[3]) * 0.5) * canvas.clientHeight,
// Include the padded, rotated sprite and its selection bracket.
radius: Math.hypot(entry.marker.width, entry.marker.height) / 2 + 4,
};
const read = () =>
samples.map(({ x, y, inFront }) => {
const r = Math.ceil(3 * scale),
px = Math.round(x * scale),
py = Math.round(canvas.height - 1 - y * scale);
if (
!inFront ||
px < r ||
py < r ||
px + r >= canvas.width ||
py + r >= canvas.height
)
return null;
const data = new Uint8Array((2 * r + 1) ** 2 * 4);
gl.readPixels(
px - r,
py - r,
2 * r + 1,
2 * r + 1,
gl.RGBA,
gl.UNSIGNED_BYTE,
data,
);
return data;
});
const frame = () =>
new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
remove();
reject(new Error('Trail framebuffer did not render within 5 s'));
}, 5000);
const remove = scene.postRender.addEventListener(() => {
remove();
clearTimeout(timeout);
resolve(read());
});
scene.requestRender();
});
const trailRenderer = window.__qaTransitTrail;
if (!trailRenderer)
return { ...setup, present: 0, reason: 'no trail renderer captured' };
let showTrail = false;
// Body and head together, as the trail's own visibility switch does.
const applyVisibility = () => trailRenderer.setVisible(showTrail);
// Override the layer's frame update in both states; selection stays fixed.
const removeVisibility = scene.preRender.addEventListener(applyVisibility);
const captures = [];
let captureReady = true;
try {
for (const visible of [false, true, false, true]) {
showTrail = visible;
// Let post-processing settle before each measured framebuffer.
for (let i = 0; i < 3; i++) await frame();
captures.push(await frame());
const current = parts.trails.diagnostics();
captureReady &&=
ready() &&
current?.body === trail.body &&
current?.headPrimitive === trail.headPrimitive;
}
} finally {
removeVisibility();
trailRenderer.setVisible(true);
await frame();
}
const [off, on, offAgain, onAgain] = captures.map((patches) =>
patches.map((p) => (p ? Array.from(p) : null)),
);
return {
...setup,
fixes: entry.track.count,
selected: state._selectedKey === setup.key,
pathMetres: lengths.at(-1),
samples,
on,
off,
onAgain,
offAgain,
sprite,
tilesReady: captureReady,
elapsedDisplayMs: entry.sample.displayT - setup.startDisplayT,
movedMetres: Math.abs(entry.sample.lat - setup.startLat) * 111320,
lines: trail.body?.lines ?? 0,
headStrategy: trail.headStrategy,
headRebuilds: trail.headRebuilds ?? null,
};
}, setup);
const pixels = reduceTrailPixels(result.on, result.off, result);
result.cost = cost;
result.altitude = altitude;
result.pitch = pitch;
result.present = pixels.present;
result.total = pixels.total;
result.samples = result.samples?.map((s, i) => ({
...s,
hit: pixels.hits[i],
stable: pixels.stable[i],
eligible: pixels.eligible[i],
}));
await captureQaScreenshot(page, {
path: `${shots}/${tag}-${altitude}m-trail-visible.jpg`,
type: 'jpeg',
quality: 95,
});
await writeFile(
`${shots}/${tag}-${altitude}m-trail-visible.json`,
JSON.stringify(result, null, 2),
);
for (const key of ['on', 'off', 'onAgain', 'offAgain']) delete result[key];
check(
name,
result.google &&
result.selected &&
result.fixes >= 5 &&
result.pathMetres > 15 &&
result.elapsedDisplayMs >= 19000 &&
result.movedMetres >= 5 &&
pixels.pass,
JSON.stringify(result),
);
check(
'selected trail stays within 2 MiB rebuild and 2 KiB/frame head uploads',
cost.frames > 30 &&
cost.bodyUploadMax > 0 &&
cost.bodyUploadMax <= 2 * 1024 * 1024 &&
cost.headUploadMax > 0 &&
cost.headUploadMax <= 2048,
JSON.stringify({ altitude, ...cost }),
);
await page.evaluate(() =>
window.__godsEyeView.dataManager.layers
.get('transit')
.module._transitPartsForTest()
.selection.clearSelection(),
);
}