mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
refactor(street-level): no sequence image cache; one pose listener
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
1db3290e0b
commit
09e2fd96a4
4 files changed
+11
-51
No files matched your search
@@ -11,9 +11,6 @@ import {
|
|||||||
|
|
||||||
const SPRITE_ID = 'street-level:mapillary-cones';
|
const SPRITE_ID = 'street-level:mapillary-cones';
|
||||||
|
|
||||||
/** How many recently viewed sequences keep their image list in memory. */
|
|
||||||
const SEQUENCE_CACHE_SIZE = 40;
|
|
||||||
|
|
||||||
/** Normalize a graph image record into the shape the cones use. */
|
/** Normalize a graph image record into the shape the cones use. */
|
||||||
function normalizeSequenceImage(record) {
|
function normalizeSequenceImage(record) {
|
||||||
const coordinates = record?.geometry?.coordinates;
|
const coordinates = record?.geometry?.coordinates;
|
||||||
@@ -115,14 +112,6 @@ export function createSequences({ state, source, parts }) {
|
|||||||
requestRender();
|
requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
function remember(sequenceId, images) {
|
|
||||||
const { cache } = state.sequence;
|
|
||||||
cache.delete(sequenceId);
|
|
||||||
cache.set(sequenceId, images);
|
|
||||||
while (cache.size > SEQUENCE_CACHE_SIZE)
|
|
||||||
cache.delete(cache.keys().next().value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Select a sequence: highlight its line and load its image cones. */
|
/** Select a sequence: highlight its line and load its image cones. */
|
||||||
async function select(sequenceId) {
|
async function select(sequenceId) {
|
||||||
if (!sequenceId || !state.viewer) return;
|
if (!sequenceId || !state.viewer) return;
|
||||||
@@ -135,16 +124,6 @@ export function createSequences({ state, source, parts }) {
|
|||||||
state.sequence.abort?.abort();
|
state.sequence.abort?.abort();
|
||||||
state.sequence.selectedId = sequenceId;
|
state.sequence.selectedId = sequenceId;
|
||||||
parts.coverage.highlight(sequenceId);
|
parts.coverage.highlight(sequenceId);
|
||||||
const cached = state.sequence.cache.get(sequenceId);
|
|
||||||
if (cached) {
|
|
||||||
state.sequence.abort = null;
|
|
||||||
state.sequence.loading = false;
|
|
||||||
state.sequence.images = cached;
|
|
||||||
renderCones(cached);
|
|
||||||
state.sequence.error = null;
|
|
||||||
notify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The previous sequence's cones must not stay clickable under the new
|
// The previous sequence's cones must not stay clickable under the new
|
||||||
// highlight while this one loads, nor after its load fails.
|
// highlight while this one loads, nor after its load fails.
|
||||||
clearCones();
|
clearCones();
|
||||||
@@ -164,7 +143,6 @@ export function createSequences({ state, source, parts }) {
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.sort((a, b) => a.capturedAt - b.capturedAt),
|
.sort((a, b) => a.capturedAt - b.capturedAt),
|
||||||
);
|
);
|
||||||
remember(sequenceId, images);
|
|
||||||
state.sequence.images = images;
|
state.sequence.images = images;
|
||||||
renderCones(images);
|
renderCones(images);
|
||||||
state.sequence.error = null;
|
state.sequence.error = null;
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ function setup() {
|
|||||||
sequence: {
|
sequence: {
|
||||||
selectedId: null,
|
selectedId: null,
|
||||||
images: [],
|
images: [],
|
||||||
cache: new Map(),
|
|
||||||
collection: cones(),
|
collection: cones(),
|
||||||
loading: false,
|
loading: false,
|
||||||
error: null,
|
error: null,
|
||||||
@@ -112,7 +111,7 @@ test('a failed sequence load leaves no stale cones and can be retried', async ()
|
|||||||
assert.equal(state.sequence.error, null, 'cleared once B loads');
|
assert.equal(state.sequence.error, null, 'cleared once B loads');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('a late answer for a superseded sequence neither draws nor is cached', async () => {
|
test('a late answer for a superseded sequence draws nothing', async () => {
|
||||||
const { state, source, sequences, drawn } = setup();
|
const { state, source, sequences, drawn } = setup();
|
||||||
sequences.select('A');
|
sequences.select('A');
|
||||||
sequences.select('B');
|
sequences.select('B');
|
||||||
@@ -123,7 +122,6 @@ test('a late answer for a superseded sequence neither draws nor is cached', asyn
|
|||||||
await settle();
|
await settle();
|
||||||
assert.deepEqual(drawn(), ['B-0', 'B-1']);
|
assert.deepEqual(drawn(), ['B-0', 'B-1']);
|
||||||
assert.equal(state.sequence.selectedId, 'B');
|
assert.equal(state.sequence.selectedId, 'B');
|
||||||
assert.equal(state.sequence.cache.has('A'), false);
|
|
||||||
assert.equal(state.sequence.loading, false);
|
assert.equal(state.sequence.loading, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -135,21 +133,6 @@ test('a second click on a loading sequence does not start another lookup', async
|
|||||||
assert.equal(source.calls[0].signal.aborted, false);
|
assert.equal(source.calls[0].signal.aborted, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('a cached sequence is re-selected without a lookup', async () => {
|
|
||||||
const { state, source, sequences, drawn } = setup();
|
|
||||||
sequences.select('A');
|
|
||||||
source.last('A').resolve(records('A'));
|
|
||||||
await settle();
|
|
||||||
sequences.select('B');
|
|
||||||
source.last('B').resolve(records('B'));
|
|
||||||
await settle();
|
|
||||||
sequences.select('A');
|
|
||||||
assert.equal(source.calls.length, 2, 'A came from the cache');
|
|
||||||
assert.deepEqual(drawn(), ['A-0', 'A-1'], 'drawn at once');
|
|
||||||
assert.equal(state.sequence.loading, false);
|
|
||||||
assert.equal(state.sequence.selectedId, 'A');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('clearSelection cancels the lookup and drops the highlight', async () => {
|
test('clearSelection cancels the lookup and drops the highlight', async () => {
|
||||||
const { state, source, sequences, highlights, drawn } = setup();
|
const { state, source, sequences, highlights, drawn } = setup();
|
||||||
sequences.select('A');
|
sequences.select('A');
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export function createMapillaryViewer({ source, render } = {}) {
|
|||||||
let renderMode = 'letterbox';
|
let renderMode = 'letterbox';
|
||||||
/** Metadata of the image on screen; pov/position events reuse it. */
|
/** Metadata of the image on screen; pov/position events reuse it. */
|
||||||
let current = null;
|
let current = null;
|
||||||
const listeners = new Set();
|
/** The pose listener (the viewer host); one at a time. */
|
||||||
|
let poseListener = null;
|
||||||
|
|
||||||
function requestRender() {
|
function requestRender() {
|
||||||
render?.governorRequestRender?.('mapillary-viewer');
|
render?.governorRequestRender?.('mapillary-viewer');
|
||||||
@@ -52,12 +53,10 @@ export function createMapillaryViewer({ source, render } = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function emit(pose) {
|
function emit(pose) {
|
||||||
for (const listener of [...listeners]) {
|
try {
|
||||||
try {
|
poseListener?.(pose);
|
||||||
listener(pose);
|
} catch {
|
||||||
} catch {
|
/* listener errors are the core's to log */
|
||||||
/* listener errors are the core's to log */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,8 +200,10 @@ export function createMapillaryViewer({ source, render } = {}) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onPose(listener) {
|
onPose(listener) {
|
||||||
listeners.add(listener);
|
poseListener = listener;
|
||||||
return () => listeners.delete(listener);
|
return () => {
|
||||||
|
if (poseListener === listener) poseListener = null;
|
||||||
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -71,8 +71,6 @@ export function createState({ services }) {
|
|||||||
sequence: {
|
sequence: {
|
||||||
selectedId: null,
|
selectedId: null,
|
||||||
images: [],
|
images: [],
|
||||||
/** Recent sequences' thinned images, by sequence id. */
|
|
||||||
cache: new Map(),
|
|
||||||
collection: null,
|
collection: null,
|
||||||
loading: false,
|
loading: false,
|
||||||
/** Why the selected sequence's images could not load, until it goes. */
|
/** Why the selected sequence's images could not load, until it goes. */
|
||||||
|
|||||||
Reference in new issue
Block a user