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
|
# 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
|
Shared distance checks use `src/geoDistance.js` with `{ lat, lon }` points and
|
||||||
the IUGG mean spherical Earth radius, 6371.0088 km. `greatCircleKm` is
|
the IUGG mean spherical Earth radius, 6371.0088 km. `greatCircleKm` is
|
||||||
canonical; `greatCircleMeters` is its exact metre conversion. Missing,
|
canonical; `greatCircleMeters` is its exact metre conversion. Missing,
|
||||||
|
|||||||
@@ -20,6 +20,20 @@ export function createAnimation({
|
|||||||
parts,
|
parts,
|
||||||
source,
|
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.
|
* Spawn animated dot primitives along a single road.
|
||||||
*
|
*
|
||||||
@@ -188,6 +202,7 @@ export function createAnimation({
|
|||||||
: null,
|
: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
syncRenderDemand();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -206,6 +221,8 @@ export function createAnimation({
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
|
syncRenderDemand();
|
||||||
|
if (!layerState._renderHeld) return;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
// Delta time in seconds, capped to avoid jumps when returning from background tab
|
// Delta time in seconds, capped to avoid jumps when returning from background tab
|
||||||
const dt = layerState._lastAnimTime
|
const dt = layerState._lastAnimTime
|
||||||
@@ -339,6 +356,13 @@ export function createAnimation({
|
|||||||
layerState._count = 0;
|
layerState._count = 0;
|
||||||
layerState._bucketCounts = { free: 0, slow: 0, jam: 0, sim: 0 };
|
layerState._bucketCounts = { free: 0, slow: 0, jam: 0, sim: 0 };
|
||||||
layerState._closedRoads = 0;
|
layerState._closedRoads = 0;
|
||||||
|
syncRenderDemand();
|
||||||
}
|
}
|
||||||
return { spawnDotsForRoad, animate, maybeStopLight, clearDots };
|
return {
|
||||||
|
spawnDotsForRoad,
|
||||||
|
animate,
|
||||||
|
maybeStopLight,
|
||||||
|
clearDots,
|
||||||
|
syncRenderDemand,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,6 @@ export function createLifecycle({
|
|||||||
parts,
|
parts,
|
||||||
source,
|
source,
|
||||||
}) {
|
}) {
|
||||||
const { holdContinuousRender, releaseContinuousRender } = services.render;
|
|
||||||
const { resetFlowTileCache } = source;
|
const { resetFlowTileCache } = source;
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
@@ -81,7 +80,7 @@ export function createLifecycle({
|
|||||||
layerState._enabled = true;
|
layerState._enabled = true;
|
||||||
layerState._surfaceFrameRemover = observeTrafficSurface(viewer.scene);
|
layerState._surfaceFrameRemover = observeTrafficSurface(viewer.scene);
|
||||||
if (layerState._roadMode !== 'tomtom') source.prefetch?.();
|
if (layerState._roadMode !== 'tomtom') source.prefetch?.();
|
||||||
holdContinuousRender('traffic'); // per-frame animator (perf wave 2)
|
parts.animation.syncRenderDemand();
|
||||||
layerState._lastAnimTime = 0;
|
layerState._lastAnimTime = 0;
|
||||||
layerState._pointCollection.show = true;
|
layerState._pointCollection.show = true;
|
||||||
|
|
||||||
@@ -142,7 +141,7 @@ export function createLifecycle({
|
|||||||
disable(viewer) {
|
disable(viewer) {
|
||||||
layerState._enabled = false;
|
layerState._enabled = false;
|
||||||
services.credits?.hideOsmCredit?.(layerState._viewer, 'traffic');
|
services.credits?.hideOsmCredit?.(layerState._viewer, 'traffic');
|
||||||
releaseContinuousRender('traffic');
|
parts.animation.syncRenderDemand();
|
||||||
clearTimeout(layerState._fetchTimeout);
|
clearTimeout(layerState._fetchTimeout);
|
||||||
clearInterval(layerState._enableKickTimer);
|
clearInterval(layerState._enableKickTimer);
|
||||||
layerState._enableKickTimer = null;
|
layerState._enableKickTimer = null;
|
||||||
|
|||||||
@@ -37,7 +37,14 @@ function setup(t) {
|
|||||||
visibleRoadsForAltitude: (roads) => roads,
|
visibleRoadsForAltitude: (roads) => roads,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const ctx = { state, services: {}, parts, source: {} };
|
const ctx = {
|
||||||
|
state,
|
||||||
|
services: {
|
||||||
|
render: { holdContinuousRender() {}, releaseContinuousRender() {} },
|
||||||
|
},
|
||||||
|
parts,
|
||||||
|
source: {},
|
||||||
|
};
|
||||||
parts.style = createStyle(ctx);
|
parts.style = createStyle(ctx);
|
||||||
parts.model = createModel(ctx);
|
parts.model = createModel(ctx);
|
||||||
parts.animation = createAnimation(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._heatSlowPrim = null;
|
||||||
}
|
}
|
||||||
layerState._heatLineCount = 0;
|
layerState._heatLineCount = 0;
|
||||||
|
parts.animation.syncRenderDemand?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,6 +153,7 @@ export function createRendering({
|
|||||||
}
|
}
|
||||||
|
|
||||||
layerState._heatLineCount = kept.length;
|
layerState._heatLineCount = kept.length;
|
||||||
|
parts.animation.syncRenderDemand?.();
|
||||||
if (candidates.length > kept.length) {
|
if (candidates.length > kept.length) {
|
||||||
console.log(
|
console.log(
|
||||||
`[Data:Traffic] Heat-lines capped at ${HEAT_LINE_CAP} (${candidates.length} congested roads in view)`,
|
`[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++;
|
count++;
|
||||||
}
|
}
|
||||||
state._count = count;
|
state._count = count;
|
||||||
|
parts.animation.syncRenderDemand?.();
|
||||||
}
|
}
|
||||||
function reconcile(roads, height, replace) {
|
function reconcile(roads, height, replace) {
|
||||||
altitude = height;
|
altitude = height;
|
||||||
|
|||||||
@@ -3576,7 +3576,7 @@ test('actual Traffic, Bikeshare and Transit lifecycles retain sensitivity in eve
|
|||||||
source: {},
|
source: {},
|
||||||
parts: {
|
parts: {
|
||||||
flow: { ensureFlowStatus: noop },
|
flow: { ensureFlowStatus: noop },
|
||||||
animation: { animate: noop, clearDots: noop },
|
animation: { animate: noop, clearDots: noop, syncRenderDemand: noop },
|
||||||
viewport: { onCameraChanged: noop },
|
viewport: { onCameraChanged: noop },
|
||||||
ingestion: { cancelActiveFetch: noop },
|
ingestion: { cancelActiveFetch: noop },
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in new issue
Block a user