Release hidden CCTV geometry and excuse level-of-detail path counts

CCTV kept the coverage and projection geometry of every camera the
opening view passed on its way to the restored one; after each visibility
update it now releases the coverage of cameras out of view and the planes
of inactive cameras, keeping decoded media for reselection.

The cold-start comparison accepts a field entry in the ledger allowlist
that excuses one field of one set with a reason, and uses it for the
infrastructure footprint counts: that level of detail keeps features
chosen on the way to a view so they do not flicker while panning, which a
cold start cannot reproduce. Presence checks ignore field entries. The
fixtures give recent imagery a matching scene and date and traffic its
deterministic starts, and traffic counts as drawn without every loaded
vehicle visible.
This commit is contained in:
German Khokhlov committed 2026-11-10 22:31:13 +04:00
1 parent ab41c765cc
commit 34b4fdce57
10 files changed
+312 -44

No files matched your search

+14 -1
View File
@@ -1 +1,14 @@
[]
[
{
"id": "local-dams-footprints",
"family": "ground",
"field": "count",
"reason": "Infrastructure level of detail keeps features chosen on the way to a view (incumbents and eviction grace, so footprints do not flicker while panning); a cold start reaches the view without that path."
},
{
"id": "local-datacenters-footprints",
"family": "ground",
"field": "count",
"reason": "Infrastructure level of detail keeps features chosen on the way to a view (incumbents and eviction grace, so footprints do not flicker while panning); a cold start reaches the view without that path."
}
]
+25 -11
View File
@@ -108,7 +108,7 @@ export function coldStartReady(layers, cold = false) {
stats.count > 0 &&
movers.some(
(row) =>
row.id === 'traffic-fill' && row.count === stats.count && row.shown,
row.id === 'traffic-fill' && row.count > 0 && row.shown,
)
);
if (['flights', 'military', 'transit'].includes(id))
@@ -317,16 +317,30 @@ export function compareColdStart(
const next = cold.ledger.find(
(other) => other.family === row.family && other.id === row.id,
);
if (next)
add(
layerOf(row.family, row.id),
row.family,
row.id,
compareProbe(
{ kind: row.kind, count: row.count },
{ kind: next.kind, count: next.count },
),
);
if (next) {
// A field entry excuses one field of one set, with its reason.
const rows = compareProbe(
{ kind: row.kind, count: row.count },
{ kind: next.kind, count: next.count },
).filter((difference) => {
const exception = allow.find(
(entry) =>
entry.field === difference.field &&
entry.id === row.id &&
(!entry.family || entry.family === row.family),
);
if (exception)
skipped.push({
layer: layerOf(row.family, row.id),
family: row.family,
id: row.id,
...difference,
reason: exception.reason,
});
return !exception;
});
add(layerOf(row.family, row.id), row.family, row.id, rows);
}
}
for (const layer of new Set([
...Object.keys(switched.layers),
+42
View File
@@ -406,5 +406,47 @@ test('readiness waits for fixture publications, LOD and the locator reveal', ()
assert.equal(ready('transit'), false);
bus.set(0, { show: true });
assert.equal(ready('transit'), true);
entry('traffic');
assert.equal(ready('traffic'), false);
const cars = engine.movers.createSet({
id: 'traffic-fill',
kind: 'point',
capacity: 10,
point: { sizePx: 4 },
});
cars.setCount(9);
assert.equal(ready('traffic'), true, 'fading vehicles need not all draw');
for (let i = 0; i < 9; i++) cars.set(i, { show: false });
assert.equal(ready('traffic'), false, 'hidden traffic is not ready');
for (let i = 0; i < 9; i++) cars.set(i, { show: true });
dataManager.layers.get('traffic').module.getStats = () => ({
count: 10,
loading: true,
});
assert.equal(ready('traffic'), false, 'population admission must finish');
engine.destroy();
});
test('a field entry excuses only that field of that set and never a missing set', () => {
const a = snapshot(),
b = snapshot();
b.ledger[0].count = 2;
const allow = [
{ id: 'radio-selected', family: 'movers', field: 'count', reason: 'path' },
];
const excused = compareColdStart(a, b, { ...options, allow });
assert.deepEqual(
excused.differences.filter((row) => row.family === 'movers'),
[],
);
assert.ok(excused.skipped.some((row) => row.reason === 'path'));
// The radio marker is declared session state; compare it as plain content.
b.ledger = [];
assert.ok(
compareColdStart(a, b, {
...options,
allow,
sessionState: false,
}).differences.some((row) => row.field === 'present'),
);
});
+25 -3
View File
@@ -25,9 +25,11 @@ export async function coldStartFixtures(origin, group) {
const epoch =
group === 'space'
? SATELLITE_FIXTURE_EPOCH
: group === 'urban'
? TRANSIT_EPOCH
: Date.now();
: group === 'imagery'
? Date.parse('2026-10-29T12:00:00Z')
: group === 'urban'
? TRANSIT_EPOCH
: Date.now();
const png = await sharp({
create: { width: 256, height: 256, channels: 4, background: '#40506a' },
})
@@ -298,6 +300,9 @@ function installFeeds({ group, epoch, groups, ghost, launches, firms }) {
return Math.floor(now());
}
};
// Mid-road starts keep the traffic population visible through birth fades
// without random road-end recycling during the comparison.
if (group === 'traffic') Math.random = () => 0.5;
localStorage.setItem('gev:first-run-mission:v1', 'suppressed');
window.__coldStartUnexpectedApi = [];
const original = window.fetch.bind(window);
@@ -315,6 +320,23 @@ function installFeeds({ group, epoch, groups, ghost, launches, firms }) {
window.fetch = (input, options) => {
const url = new URL(input.url ?? input, location.href),
path = url.pathname;
// The image fixture also needs the snapshot API's explicit pixel proof.
if (
group === 'imagery' &&
url.hostname === 'wvs.earthdata.nasa.gov' &&
path.startsWith('/api/v1/snapshot')
)
return original(input, options).then((response) =>
response.ok
? new Response(response.body, {
status: response.status,
headers: {
...Object.fromEntries(response.headers),
'Data-Present': 'true',
},
})
: response,
);
if (path === '/api/flights')
return json({
time: epoch / 1000,
+69 -25
View File
@@ -8,6 +8,8 @@ import { parseSatcatCsv } from '../../src/layers/satellites/classes.js';
import { createFakeEngine } from '../../src/globe/testing/fakeEngine.js';
import { createTrafficDisplay } from '../../src/layers/traffic/render/display.js';
import { createAnimation } from '../../src/layers/traffic/animation.js';
import { createRecentImagerySource } from '../../src/layers/recentImagery/source.js';
import { createThumbnailLoader } from '../../src/layers/recentImagery/thumbnails.js';
const origin = 'http://127.0.0.1:4309';
test('every group has valid strict fixture routes', async () => {
@@ -221,48 +223,81 @@ test('traffic uses deterministic road and flow tiles', async () => {
assert.ok((await (await page.fetch(url)).arrayBuffer()).byteLength > 0);
});
test('traffic dots finish their birth fade with the fixture clock', async (t) => {
test('imagery fixtures admit a recent S30 scene through the real thumbnail loader', async (t) => {
const page = await installedPage('imagery');
const source = createRecentImagerySource({ fetchImpl: page.fetch });
const box = { west: -97.77, east: -97.71, south: 30.25, north: 30.29 };
const now = new Date(page.fixture.epoch);
const { candidates } = await source.searchHls({ box, now });
const candidate = candidates.find((row) => row.product === 'S30');
assert.ok(candidate);
assert.ok(now - Date.parse(candidate.day) < 3 * 86_400_000);
const loader = createThumbnailLoader({
requestThumbnail: source.getThumbnail,
createObjectUrl: () => 'blob:fixture',
revokeObjectUrl() {},
});
t.after(() => loader.destroy());
const finished = new Promise((resolve) => {
loader.subscribe((key) => {
const state = loader.get(key);
if (!state.loading && state.status !== 'unknown') resolve(state);
});
});
loader.request(candidate, box);
const state = await finished;
assert.equal(state.status, 'present');
assert.equal(state.objectUrl, 'blob:fixture');
});
test('traffic fixture dots finish their birth fade and stay visible during comparison', async (t) => {
const page = await installedPage('traffic');
t.mock.method(Date, 'now', () => vm.runInContext('Date.now()', page.context));
const engine = createFakeEngine();
t.mock.method(Math, 'random', () =>
vm.runInContext('Math.random()', page.context),
);
const engine = createFakeEngine({ altitude: 1800 });
const display = createTrafficDisplay(engine);
t.after(() => {
display.destroy();
engine.destroy();
});
const point = {
position: { x: 6378137, y: 0, z: 0 },
show: true,
pixelSize: 6,
color: { red: 1, green: 1, blue: 1, alpha: 1 },
outlineWidth: 0,
};
const dot = {
point,
born: Date.now(),
nominalSize: 6,
direction: 1,
segIdx: 0,
numSegments: 1,
t: 0.5,
mps: 1,
stoppedUntil: 0,
segmentDist: [1000],
waypoints: [point.position, { x: 6378137, y: 1000, z: 0 }],
};
const animation = createAnimation({
state: {
_enabled: true,
_renderHeld: true,
_engine: engine,
_display: display,
_dots: [dot],
_dots: [],
_scratchLerp: {},
_nextDotId: 1,
_speedScale: 1,
_fadeScaleFar: 100000,
_fadeTransFar: 100000,
_bucketCounts: { sim: 0 },
_motion: { added: 0, recycled: 0 },
_lastAnimTime: Date.now(),
_animFrame: 0,
},
services: {},
parts: { rendering: {}, retention: { easeHeights() {} } },
parts: {
rendering: {},
retention: { easeHeights() {} },
style: { baseDotSize: () => 6, activeSizeDelta: () => 0 },
},
});
animation.spawnDotsForRoad(
{
type: 'primary',
segmentDist: [10000],
waypoints: [
{ x: 6378137, y: 0, z: 0 },
{ x: 6378137, y: 10000, z: 0 },
],
},
1800,
10,
);
animation.animate();
animation.drawDots();
assert.equal(
@@ -274,6 +309,15 @@ test('traffic dots finish their birth fade with the fixture clock', async (t) =>
animation.drawDots();
assert.equal(
engine.movers.list().find(({ id }) => id === 'traffic-fill').count,
1,
10,
);
for (let i = 0; i < 300; i++) {
page.advance(100);
animation.animate();
animation.drawDots();
assert.equal(
engine.movers.list().find(({ id }) => id === 'traffic-fill').count,
10,
);
}
});
+3 -1
View File
@@ -42,7 +42,9 @@ export function compareLedgers(before, after, allow = []) {
if (b.has(key)) return [];
const exception = allow.find(
(entry) =>
entry.id === row.id && (!entry.family || entry.family === row.family),
!entry.field &&
entry.id === row.id &&
(!entry.family || entry.family === row.family),
);
if (exception) {
allowed.push({
+6 -3
View File
@@ -818,8 +818,11 @@ test('real active monitor plane owns one protected host label and no native labe
showProjection: false,
});
refreshCoverageStyles();
assert.equal(runtime.planeEntity.show, false);
assert.equal(primitive(runtime.planeEntity.id).show, false);
assert.equal(runtime.planeEntity, null);
assert.equal(
engine.lines.list().some((line) => line.id.endsWith('-plane-outline')),
false,
);
assert.deepEqual(calls.slice(-2), [
['clear', 'cctv-projection'],
['visible', 'cctv-projection', false],
@@ -1210,7 +1213,7 @@ test('CCTV deselect publishes one null state and leaves the complete camera pose
assert.equal(publications.at(-1).activeCameraId, null);
assert.equal(publications.at(-1).activeCamera, null);
assert.equal(publications.at(-1).enabled, true);
assert.equal(runtime.planeEntity.show, false);
assert.equal(runtime.planeEntity, null);
assert.equal(record.probeClampRangeM, null);
assert.equal(record.activationDone, false);
assert.deepEqual(engine.camera.pose(), pose);
+27
View File
@@ -744,6 +744,32 @@ export function createGeometry({ state: layerState, services, parts, source }) {
);
}
function releaseInactiveGeometry(activeId, visibleIds) {
const removed = new Set();
for (const record of layerState._records) {
if (record.camera.id === activeId) continue;
if (!visibleIds.has(record.camera.id)) {
for (const entity of record.coverageEntities || []) {
parts.renderer.removeCoverageEntity(entity);
removed.add(entity);
}
record.coverageEntities = [];
}
const runtime = record.projection;
if (runtime?.planeEntity) {
// Keep decoded media for reselection, but only the active plane draws.
parts.renderer.removePlane(runtime);
runtime.planeEntity = runtime.planeMaterial = null;
runtime.planeVisible = false;
runtime.needsPlane = true;
}
}
if (removed.size)
layerState._coverageEntities = layerState._coverageEntities.filter(
(entity) => !removed.has(entity),
);
}
/**
* Removes all coverage entities and projection runtimes from the scene.
*/
@@ -790,6 +816,7 @@ export function createGeometry({ state: layerState, services, parts, source }) {
registerCoverageEntities,
ensureActiveCoverageEntities,
ensureVisibleCoverageEntities,
releaseInactiveGeometry,
destroyCoverageEntities,
};
}
+1
View File
@@ -426,6 +426,7 @@ export function createRendering({
}
}
parts.geometry.releaseInactiveGeometry(activeId, coverageVisible);
parts.renderer?.setMarkersVisible(!!layerState._enabled);
parts.projection.pauseInactiveProjectionFeeds(activeId);
}
+100
View File
@@ -5,6 +5,7 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createRendering } from './rendering.js';
import { createGeometry } from './geometry.js';
import {
ACTIVE_CAMERA_COLOR,
IDLE_CAMERA_COLOR,
@@ -77,3 +78,102 @@ test('focus de-emphasis fades each icon from its own color', () => {
);
assert.equal(state._activeFocusStyleCount, 3);
});
test('coverage refresh releases departed geometry and preserves media for reselection', () => {
const records = ['opening', 'final', 'neighbor'].map((id) => ({
camera: { id, headingConfidence: 'high' },
frustumGeometry: {},
frustumPositions: {},
coverageEntities: [],
}));
const state = {
_enabled: true,
_coverageMode: 'on',
_showProjection: true,
_records: records,
_coverageEntities: [],
};
let active = records[0];
let visible = new Set(['opening']);
const lines = new Set();
const planes = new Set();
const services = { ground: {}, focus: {} };
const parts = {
renderer: {
ready: true,
createCoverage(id) {
const line = { id, show: false };
lines.add(line);
return [line];
},
removeCoverageEntity: (line) => lines.delete(line),
styleLine: (line, { visible }) => {
line.show = visible;
},
removePlane: (runtime) => planes.delete(runtime.planeEntity),
setMarkersVisible() {},
},
selection: { getActiveRecord: () => active },
model: { isBrowserOverlayCamera: () => false },
projection: {
ensureProjectionRuntime(record) {
record.projection ??= { canvas: {} };
const runtime = record.projection;
if (!runtime.planeEntity) {
runtime.planeEntity = {};
runtime.needsPlane = false;
planes.add(runtime.planeEntity);
}
},
setPlaneVisible: (runtime, value) => {
runtime.planeVisible = value;
},
pauseInactiveProjectionFeeds() {},
},
};
parts.geometry = createGeometry({ state, services, parts });
parts.geometry.buildCoverageVisibleSet = () => visible;
const rendering = createRendering({ state, services, parts });
rendering.refreshCoverageStyles();
const openingLine = records[0].coverageEntities[0];
const media = records[0].projection.canvas;
assert.equal(
openingLine.show,
true,
'startup without a restore draws immediately',
);
active = records[1];
visible = new Set(['final', 'neighbor']);
rendering.refreshCoverageStyles();
assert.equal(lines.has(openingLine), false);
assert.deepEqual(records[0].coverageEntities, []);
assert.equal(records[0].projection.planeEntity, null);
assert.equal(records[0].projection.needsPlane, true);
assert.equal(records[0].projection.canvas, media);
assert.deepEqual(
[...lines].map((line) => line.id),
['final', 'neighbor'],
);
assert.ok([...lines].every((line) => line.show));
assert.deepEqual(state._coverageEntities, [...lines]);
assert.equal(planes.size, 1);
const finalLine = records[1].coverageEntities[0];
rendering.refreshCoverageStyles();
assert.equal(
records[1].coverageEntities[0],
finalLine,
'stationary geometry is reused',
);
active = records[0];
visible = new Set();
rendering.refreshCoverageStyles();
assert.equal(
lines.size,
1,
'active projection keeps its coverage outside the visible cohort',
);
assert.equal(records[0].coverageEntities[0].show, true);
assert.notEqual(records[0].coverageEntities[0], openingLine);
assert.equal(records[0].projection.canvas, media);
assert.equal(planes.size, 1);
});