mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix(traffic): hold rendering only for animated content
This commit is contained in:
1 parent
de2138e84c
commit
122b511d1e
8 files changed
+86
-6
No files matched your search
@@ -1,5 +1,7 @@
|
||||
# God's Eye View Current State
|
||||
|
||||
Traffic holds continuous rendering only while enabled with animated dots or a pulsing jam heat-line. Empty, failed, and high-altitude views release the hold; population admission reacquires it and resets animation time. Clear, disable, and destroy also release it. Retained fading dots keep animation alive until they are removed.
|
||||
|
||||
Shared distance checks use `src/geoDistance.js` with `{ lat, lon }` points and
|
||||
the IUGG mean spherical Earth radius, 6371.0088 km. `greatCircleKm` is
|
||||
canonical; `greatCircleMeters` is its exact metre conversion. Missing,
|
||||
|
||||
@@ -20,6 +20,20 @@ export function createAnimation({
|
||||
parts,
|
||||
source,
|
||||
}) {
|
||||
function syncRenderDemand() {
|
||||
const needed = Boolean(
|
||||
layerState._enabled &&
|
||||
(layerState._dots.length || layerState._heatJamPrim),
|
||||
);
|
||||
if (needed === Boolean(layerState._renderHeld)) return;
|
||||
layerState._renderHeld = needed;
|
||||
if (needed) {
|
||||
layerState._lastAnimTime = 0;
|
||||
services.render.holdContinuousRender('traffic');
|
||||
layerState._viewer?.scene?.requestRender?.();
|
||||
} else services.render.releaseContinuousRender('traffic');
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn animated dot primitives along a single road.
|
||||
*
|
||||
@@ -188,6 +202,7 @@ export function createAnimation({
|
||||
: null,
|
||||
});
|
||||
}
|
||||
syncRenderDemand();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,6 +221,8 @@ export function createAnimation({
|
||||
*/
|
||||
|
||||
function animate() {
|
||||
syncRenderDemand();
|
||||
if (!layerState._renderHeld) return;
|
||||
const now = Date.now();
|
||||
// Delta time in seconds, capped to avoid jumps when returning from background tab
|
||||
const dt = layerState._lastAnimTime
|
||||
@@ -339,6 +356,13 @@ export function createAnimation({
|
||||
layerState._count = 0;
|
||||
layerState._bucketCounts = { free: 0, slow: 0, jam: 0, sim: 0 };
|
||||
layerState._closedRoads = 0;
|
||||
syncRenderDemand();
|
||||
}
|
||||
return { spawnDotsForRoad, animate, maybeStopLight, clearDots };
|
||||
return {
|
||||
spawnDotsForRoad,
|
||||
animate,
|
||||
maybeStopLight,
|
||||
clearDots,
|
||||
syncRenderDemand,
|
||||
};
|
||||
}
|
||||
@@ -12,7 +12,6 @@ export function createLifecycle({
|
||||
parts,
|
||||
source,
|
||||
}) {
|
||||
const { holdContinuousRender, releaseContinuousRender } = services.render;
|
||||
const { resetFlowTileCache } = source;
|
||||
|
||||
const methods = {
|
||||
@@ -81,7 +80,7 @@ export function createLifecycle({
|
||||
layerState._enabled = true;
|
||||
layerState._surfaceFrameRemover = observeTrafficSurface(viewer.scene);
|
||||
if (layerState._roadMode !== 'tomtom') source.prefetch?.();
|
||||
holdContinuousRender('traffic'); // per-frame animator (perf wave 2)
|
||||
parts.animation.syncRenderDemand();
|
||||
layerState._lastAnimTime = 0;
|
||||
layerState._pointCollection.show = true;
|
||||
|
||||
@@ -142,7 +141,7 @@ export function createLifecycle({
|
||||
disable(viewer) {
|
||||
layerState._enabled = false;
|
||||
services.credits?.hideOsmCredit?.(layerState._viewer, 'traffic');
|
||||
releaseContinuousRender('traffic');
|
||||
parts.animation.syncRenderDemand();
|
||||
clearTimeout(layerState._fetchTimeout);
|
||||
clearInterval(layerState._enableKickTimer);
|
||||
layerState._enableKickTimer = null;
|
||||
|
||||
@@ -37,7 +37,14 @@ function setup(t) {
|
||||
visibleRoadsForAltitude: (roads) => roads,
|
||||
},
|
||||
};
|
||||
const ctx = { state, services: {}, parts, source: {} };
|
||||
const ctx = {
|
||||
state,
|
||||
services: {
|
||||
render: { holdContinuousRender() {}, releaseContinuousRender() {} },
|
||||
},
|
||||
parts,
|
||||
source: {},
|
||||
};
|
||||
parts.style = createStyle(ctx);
|
||||
parts.model = createModel(ctx);
|
||||
parts.animation = createAnimation(ctx);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createAnimation } from './animation.js';
|
||||
|
||||
test('traffic render demand follows animated population and jam lines across empty, disable and re-enable', () => {
|
||||
const calls = [];
|
||||
const state = { _enabled: true, _dots: [], _motion: { rebuilds: 0 } };
|
||||
const animation = createAnimation({
|
||||
state,
|
||||
services: {
|
||||
render: {
|
||||
holdContinuousRender: (id) => calls.push(['hold', id]),
|
||||
releaseContinuousRender: (id) => calls.push(['release', id]),
|
||||
},
|
||||
},
|
||||
parts: {
|
||||
retention: { clear() {} },
|
||||
rendering: {
|
||||
removeHeatLines() {
|
||||
state._heatJamPrim = null;
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
animation.syncRenderDemand();
|
||||
assert.deepEqual(calls, []);
|
||||
state._dots.push({});
|
||||
animation.syncRenderDemand();
|
||||
animation.syncRenderDemand();
|
||||
assert.deepEqual(calls, [['hold', 'traffic']]);
|
||||
animation.clearDots();
|
||||
assert.deepEqual(calls.at(-1), ['release', 'traffic']);
|
||||
state._heatJamPrim = {};
|
||||
animation.syncRenderDemand();
|
||||
assert.deepEqual(calls.at(-1), ['hold', 'traffic']);
|
||||
state._enabled = false;
|
||||
animation.syncRenderDemand();
|
||||
assert.deepEqual(calls.at(-1), ['release', 'traffic']);
|
||||
state._enabled = true;
|
||||
animation.clearDots();
|
||||
assert.equal(state._renderHeld, false);
|
||||
state._dots.push({});
|
||||
animation.syncRenderDemand();
|
||||
assert.equal(state._renderHeld, true);
|
||||
});
|
||||
@@ -50,6 +50,7 @@ export function createRendering({
|
||||
layerState._heatSlowPrim = null;
|
||||
}
|
||||
layerState._heatLineCount = 0;
|
||||
parts.animation.syncRenderDemand?.();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,6 +153,7 @@ export function createRendering({
|
||||
}
|
||||
|
||||
layerState._heatLineCount = kept.length;
|
||||
parts.animation.syncRenderDemand?.();
|
||||
if (candidates.length > kept.length) {
|
||||
console.log(
|
||||
`[Data:Traffic] Heat-lines capped at ${HEAT_LINE_CAP} (${candidates.length} congested roads in view)`,
|
||||
|
||||
@@ -125,6 +125,7 @@ export function createRetention({ state, parts }) {
|
||||
count++;
|
||||
}
|
||||
state._count = count;
|
||||
parts.animation.syncRenderDemand?.();
|
||||
}
|
||||
function reconcile(roads, height, replace) {
|
||||
altitude = height;
|
||||
|
||||
@@ -3576,7 +3576,7 @@ test('actual Traffic, Bikeshare and Transit lifecycles retain sensitivity in eve
|
||||
source: {},
|
||||
parts: {
|
||||
flow: { ensureFlowStatus: noop },
|
||||
animation: { animate: noop, clearDots: noop },
|
||||
animation: { animate: noop, clearDots: noop, syncRenderDemand: noop },
|
||||
viewport: { onCameraChanged: noop },
|
||||
ingestion: { cancelActiveFetch: noop },
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user