mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
refactor: extract application construction and lifecycle
This commit is contained in:
1 parent
092f28e301
commit
4d37760314
36 files changed
+1363
-382
No files matched your search
@@ -101,6 +101,7 @@ export function createAnnotationEngine({
|
||||
}) {
|
||||
/** @type {Map<string, object>} live annotations keyed by id */
|
||||
const annotations = new Map();
|
||||
let destroyed = false;
|
||||
let tickHandle = null;
|
||||
let assistFlightUntil = 0; // camera-assist debounce (see ensureMarksVisible)
|
||||
// Generation token + in-flight fetch controllers guard against clear / new-topic
|
||||
@@ -174,7 +175,7 @@ export function createAnnotationEngine({
|
||||
}
|
||||
|
||||
function ensureTicking() {
|
||||
if (tickHandle != null) return;
|
||||
if (destroyed || tickHandle != null) return;
|
||||
const tick = () => {
|
||||
const now = performance.now();
|
||||
let changed = false;
|
||||
@@ -222,6 +223,7 @@ export function createAnnotationEngine({
|
||||
* @returns {Promise<{ok, drawn, failed, ids, results}>}
|
||||
*/
|
||||
async function annotate(requests, opts = {}) {
|
||||
if (destroyed) return { ok: false, drawn: 0, failed: 0, ids: [], results: [], error: 'destroyed' };
|
||||
const list = Array.isArray(requests) ? requests : [requests];
|
||||
if (opts.clearPrevious) clear(); // bumps generation + aborts older pending work
|
||||
|
||||
@@ -865,6 +867,17 @@ export function createAnnotationEngine({
|
||||
const engine = {
|
||||
annotate,
|
||||
clear,
|
||||
destroy() {
|
||||
if (destroyed) return;
|
||||
destroyed = true;
|
||||
try { clear(); } finally {
|
||||
if (tickHandle != null) cancelAnimationFrame(tickHandle);
|
||||
tickHandle = null;
|
||||
outlineListeners.clear();
|
||||
releaseContinuousRender('annotations');
|
||||
renderer.destroy();
|
||||
}
|
||||
},
|
||||
fadeOutAll,
|
||||
count: () => annotations.size,
|
||||
list: () => Array.from(annotations.values()),
|
||||
@@ -875,7 +888,7 @@ export function createAnnotationEngine({
|
||||
* upgrade task, AFTER the mark was mutated in place. Returns an unsubscribe fn.
|
||||
*/
|
||||
onOutlineEvent(listener) {
|
||||
if (typeof listener !== 'function') return () => {};
|
||||
if (destroyed || typeof listener !== 'function') return () => {};
|
||||
outlineListeners.add(listener);
|
||||
return () => outlineListeners.delete(listener);
|
||||
},
|
||||
@@ -901,16 +914,22 @@ export function createAnnotationEngine({
|
||||
async tour() {
|
||||
clear();
|
||||
flyTo({ lon: -122.4486, lat: 37.7960, height: 520, heading: 0, pitch: -26, duration: 3 });
|
||||
if (destroyed) return { ok: false };
|
||||
await wait(3200);
|
||||
await annotate([{ type: 'highlight', target: 'Palace of Fine Arts, San Francisco', label: 'Palace of Fine Arts', color: 'amber' }], { persist: true });
|
||||
if (destroyed) return { ok: false };
|
||||
await wait(2600);
|
||||
await annotate([{ type: 'arrow', target: 'Palace of Fine Arts, San Francisco', toTarget: 'Marina Green, San Francisco', label: 'next to the Marina', color: 'cyan' }], { persist: true });
|
||||
if (destroyed) return { ok: false };
|
||||
await wait(2600);
|
||||
flyTo({ lon: -122.4545, lat: 37.7880, height: 1500, heading: 18, pitch: -32, duration: 3 });
|
||||
if (destroyed) return { ok: false };
|
||||
await wait(3200);
|
||||
await annotate([{ type: 'area', target: 'Presidio of San Francisco', label: 'The Presidio — a former Army base', color: 'green', footprint: true }], { persist: true });
|
||||
if (destroyed) return { ok: false };
|
||||
await wait(2800);
|
||||
await annotate([{ type: 'pin', target: 'Letterman Digital Arts Center, San Francisco', label: 'ILM / Lucasfilm', color: 'red' }], { persist: true });
|
||||
if (destroyed) return { ok: false };
|
||||
await wait(2600);
|
||||
await annotate([{ type: 'route', color: 'amber', label: 'Crissy Field shoreline', points: [
|
||||
{ target: 'Palace of Fine Arts, San Francisco' },
|
||||
@@ -922,6 +941,7 @@ export function createAnnotationEngine({
|
||||
};
|
||||
|
||||
function flyTo({ lon, lat, height, heading = 0, pitch = -30, duration = 2.5 }) {
|
||||
if (destroyed) return;
|
||||
try {
|
||||
viewer.camera.flyTo({
|
||||
destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
|
||||
|
||||
@@ -668,3 +668,44 @@ test('duplicate-replacement: a throw AFTER partial renderer state leaves nothing
|
||||
assert.equal(retry.drawn, 1);
|
||||
assert.equal(live.size, 1, 'the recoloured mark redraws once, with no orphan underneath');
|
||||
});
|
||||
|
||||
test('destroy releases the renderer and prevents subsequent annotation work', async (t) => {
|
||||
installAnimationFrameStubs(t);
|
||||
const { renderer, calls } = fakeRenderer();
|
||||
let destroyed = 0;
|
||||
renderer.destroy = () => { destroyed++; };
|
||||
const engine = createAnnotationEngine({ viewer: {}, renderer });
|
||||
const drawn = await engine.annotate([{ type: 'pin', longitude: -97.74, latitude: 30.27 }]);
|
||||
assert.equal(drawn.drawn, 1);
|
||||
engine.destroy();
|
||||
const additions = calls.add;
|
||||
const result = await engine.annotate([{ type: 'pin', target: 'Must not resolve' }]);
|
||||
assert.equal(result.error, 'destroyed');
|
||||
assert.equal(calls.add, additions);
|
||||
assert.equal(engine.count(), 0);
|
||||
engine.destroy();
|
||||
assert.equal(destroyed, 1);
|
||||
assert.ok(!getRenderGovernorDiagnostics().holds.includes('annotations'));
|
||||
});
|
||||
|
||||
test('a late annotation resolver cannot redraw after destruction', async (t) => {
|
||||
installAnimationFrameStubs(t);
|
||||
const { renderer, calls } = fakeRenderer();
|
||||
renderer.destroy = () => {};
|
||||
let release;
|
||||
let signal;
|
||||
const engine = createAnnotationEngine({
|
||||
viewer: {}, renderer,
|
||||
resolveTarget: (options) => {
|
||||
signal = options.signal;
|
||||
return new Promise((resolve) => { release = resolve; });
|
||||
},
|
||||
});
|
||||
const pending = engine.annotate([{ type: 'pin', target: 'Pending place' }]);
|
||||
engine.destroy();
|
||||
assert.equal(signal.aborted, true);
|
||||
release({ lon: -97.74, lat: 30.27 });
|
||||
await pending;
|
||||
assert.equal(calls.add, 0);
|
||||
assert.equal(engine.count(), 0);
|
||||
});
|
||||
Reference in new issue
Block a user