mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
fix(street-level): sequence loads, playback and nearest-photo edge cases
From gekh's review of #768, each bug with a test that fails before: - P2: a failed sequence load clears the previous sequence's cones, resets the selection and can be retried; a second click while loading starts no second lookup. sequences.js had no tests: sequences.test.mjs now covers failure and retry, a superseded late answer, the cache and clearSelection. - P2: closing a photo stops MapillaryJS sequence playback, which kept the hidden viewer stepping and downloading images. - openNearest without the panel reports it instead of loading forever, and stops if the layer (or its provider) went off during the lookup; the mesh sampler's misses are bounded and forgotten with its samples; the sequence count is the union of ids across tiles; a provider withdraws only its own error, so a sequence error cannot mask KEY REJECTED; hover stays on (throttled) under an orbiting camera and re-picks when it stops; "since N days" follows the clock in a long-open tab. - A prewarm waits for one in flight and then builds if it gave up. - New coverage for the layer core (credits, KEY REJECTED vs REQUIRED, switching a provider off), the refresh wiring of the tilted-view fix and the draped-to-cast swap completion. - Cleanup: loading is derived from pending; freshStreet(); tile ranking computes distances once and shares one path for the date line; shared metresBetween / whenIdle / redrawTile; one NEAREST_RADIUS_M and one source method list; unused contract fields, exports, getImage and the decoder's image branch removed; PROVIDER_COLORS lists only Mapillary. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
48276450ec
commit
5f98b871e8
31 files changed
+1129
-470
No files matched your search
@@ -52,7 +52,6 @@ export function createSelection({ state, parts }) {
|
||||
let hoverPosition = null;
|
||||
let hoverLastPickAt = -Infinity;
|
||||
let hoverTimer = null;
|
||||
let cameraMoving = false;
|
||||
let pointerButtons = 0;
|
||||
let removeHoverWatchers = null;
|
||||
|
||||
@@ -71,8 +70,8 @@ export function createSelection({ state, parts }) {
|
||||
const viewer = state.viewer;
|
||||
// Switched off (or torn down) since the frame was queued.
|
||||
if (!viewer || !state.enabled || !state.clickHandler) return;
|
||||
// Orbiting, or a drag in progress: the pointer is the camera's.
|
||||
if (cameraMoving || pointerButtons !== 0) return;
|
||||
// A drag in progress: the pointer is the camera's.
|
||||
if (pointerButtons !== 0) return;
|
||||
const canvas = viewer.scene?.canvas;
|
||||
if (!canvas || viewer.isDestroyed?.()) return;
|
||||
hoverLastPickAt = Date.now();
|
||||
@@ -98,6 +97,11 @@ export function createSelection({ state, parts }) {
|
||||
const viewer = state.viewer;
|
||||
if (!viewer || !state.enabled) return;
|
||||
hoverPosition = movement.endPosition;
|
||||
queueHoverPick();
|
||||
}
|
||||
|
||||
/** Pick `hoverPosition` on a coming frame, within the throttle. */
|
||||
function queueHoverPick() {
|
||||
if (hoverQueued) return;
|
||||
hoverQueued = true;
|
||||
const wait = hoverLastPickAt + HOVER_PICK_INTERVAL_MS - Date.now();
|
||||
@@ -111,7 +115,12 @@ export function createSelection({ state, parts }) {
|
||||
}, wait);
|
||||
}
|
||||
|
||||
/** Track what makes a hover pick pointless: camera motion, held buttons. */
|
||||
/**
|
||||
* Track held buttons (a drag picks nothing) and camera stops. A moving
|
||||
* camera does not stop hover: an orbiting or tracking camera never stops,
|
||||
* and the throttle already bounds the cost. When the camera comes to rest
|
||||
* the pointer is picked once more, since what was under it has moved.
|
||||
*/
|
||||
function watchHover(viewer) {
|
||||
const canvas = viewer.scene.canvas;
|
||||
const camera = viewer.camera;
|
||||
@@ -120,16 +129,12 @@ export function createSelection({ state, parts }) {
|
||||
};
|
||||
for (const type of ['pointerdown', 'pointermove', 'pointerup'])
|
||||
canvas.addEventListener?.(type, onButtons);
|
||||
const removeStart = camera?.moveStart?.addEventListener(() => {
|
||||
cameraMoving = true;
|
||||
});
|
||||
const removeEnd = camera?.moveEnd?.addEventListener(() => {
|
||||
cameraMoving = false;
|
||||
if (hoverPosition) queueHoverPick();
|
||||
});
|
||||
removeHoverWatchers = () => {
|
||||
for (const type of ['pointerdown', 'pointermove', 'pointerup'])
|
||||
canvas.removeEventListener?.(type, onButtons);
|
||||
removeStart?.();
|
||||
removeEnd?.();
|
||||
};
|
||||
}
|
||||
@@ -140,8 +145,8 @@ export function createSelection({ state, parts }) {
|
||||
clearTimeout(hoverTimer);
|
||||
hoverTimer = null;
|
||||
hoverQueued = false;
|
||||
hoverPosition = null;
|
||||
hoverLastPickAt = -Infinity;
|
||||
cameraMoving = false;
|
||||
pointerButtons = 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user