mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 19:34:01 +00:00
fix(camera): run a recovery check a scene deferred once it ends
A check skipped because a flight or a playing Director scene was in progress used to be dropped, so a run that ended or held on a stars-only shot stayed there until the next camera move. Keep watching instead: the settle check retries once the flight or scene ends, within its existing five-minute bound, and a moveEnd that arrives while it is deferred keeps the check armed rather than stopping it. Also test that the free-look limit never raises Cesium's trackball height and that dispose keeps a value set after install, note in its JSDoc the enableLook case it cannot tell apart, and document the trackball change and the retry in CURRENT-STATE and the changelog.
This commit is contained in:
1 parent
2f9d13f39d
commit
29935cc094
4 files changed
+76
-31
No files matched your search
@@ -1,8 +1,9 @@
|
|||||||
- Keep the globe in view from orbit. Cesium's free look (Shift+drag) turned
|
- Keep the globe in view from orbit. Cesium's free look (Shift+drag) turned
|
||||||
the camera in place, so a few drags from the whole-globe view left only
|
the camera in place, so a few drags from the whole-globe view left only
|
||||||
stars, and the pose stayed in the share link. Free look now works only below
|
stars, and the pose stayed in the share link. Free look now works only below
|
||||||
1,000 km; a camera move that ends above it with no planet surface in view
|
1,000 km, and above it a drag that starts on the stars orbits the globe
|
||||||
(inside the scope keyhole when the scope is on) turns straight down; and
|
instead of turning the camera; a camera move that ends above it with no
|
||||||
voice tilt and turns from orbit stop while the planet is still on screen and
|
planet surface in view (inside the scope keyhole when the scope is on) turns
|
||||||
say so instead of reporting a turn they could not make (Manjunath,
|
straight down; and voice tilt and turns from orbit stop while the planet is
|
||||||
@manjunath22466, #1532).
|
still on screen and say so instead of reporting a turn they could not make
|
||||||
|
(Manjunath, @manjunath22466, #1532).
|
||||||
+17
-15
@@ -67,10 +67,11 @@ the real browser controller at desktop and phone widths with keyless fixtures.
|
|||||||
|
|
||||||
Cesium's free look (Shift+drag, which turns the camera in place) works only
|
Cesium's free look (Shift+drag, which turns the camera in place) works only
|
||||||
below 1,000 km. Above that height a few drags could turn the whole planet out of
|
below 1,000 km. Above that height a few drags could turn the whole planet out of
|
||||||
frame and leave only stars, so Shift+drag does nothing there. Cesium also
|
frame and leave only stars, so Shift+drag does nothing there. Cesium also turns
|
||||||
turns the camera in place for a drag that starts on the stars below its
|
the camera in place for a drag that starts on the stars below its trackball
|
||||||
trackball height (7,500 km by default); that height is lowered to 1,000 km, so
|
height (7,500 km by default); that height is lowered to 1,000 km, so such a drag
|
||||||
such a drag orbits the globe above it, as it does higher up. Orbit, tilt and
|
orbits the globe above it, as it does higher up, and a tilt drag from the stars
|
||||||
|
there tilts about the planet's edge or does nothing. Otherwise orbit, tilt and
|
||||||
zoom are unchanged at every height. How much of the frame shows the planet's
|
zoom are unchanged at every height. How much of the frame shows the planet's
|
||||||
surface is measured by casting a 32×18 grid of view rays at the ellipsoid; while
|
surface is measured by casting a 32×18 grid of view rays at the ellipsoid; while
|
||||||
the scope mask is on (the default) only the rays inside its keyhole count, since
|
the scope mask is on (the default) only the rays inside its keyhole count, since
|
||||||
@@ -97,17 +98,18 @@ rounding error every frame and Cesium never sees it stop. That check keeps
|
|||||||
watching until move-end arrives, recovering once after each move that comes to
|
watching until move-end arrives, recovering once after each move that comes to
|
||||||
rest, and gives up five minutes after the camera last moved. Limb glow, and a
|
rest, and gives up five minutes after the camera last moved. Limb glow, and a
|
||||||
surface sliver up to about one grid cell thick (some 50 px on a 900 px tall
|
surface sliver up to about one grid cell thick (some 50 px on a 900 px tall
|
||||||
frame), can count as out of view. The zoom-out guard above keeps wheel and
|
frame), can count as out of view. The zoom-out guard above keeps wheel and pinch
|
||||||
pinch zoom-outs in view; this catches the other ways a move can end looking
|
zoom-outs in view; this catches the other ways a move can end looking away, such
|
||||||
away, such as a saved or shared view that did.
|
as a saved or shared view that did. Views with more surface than that, such as
|
||||||
Views with more surface than that, such as the oblique orbital Director shots,
|
the oblique orbital Director shots, are left as they are, and so are tracked or
|
||||||
are left as they are, and so are tracked or locked cameras, a camera flight in
|
locked cameras, a camera flight in progress, a Director scene while it plays
|
||||||
progress, a Director scene while it plays (holds included) and the 2D and
|
(holds included) and the 2D and Columbus modes; a check a flight or scene defers
|
||||||
Columbus modes. A view that loses its surface without a camera move (a resize,
|
runs once it ends. A Director shot loaded or replayed outside a run is not a
|
||||||
or turning the scope on) is left as it is until the next move. A voice turn
|
playing scene. A view that loses its surface without a camera move (a resize, or
|
||||||
requested while the camera is tracked is judged
|
turning the scope on) is left as it is until the next move. A voice turn
|
||||||
by the pose voice's release of the follow camera will leave, before that
|
requested while the camera is tracked is judged by the pose voice's release of
|
||||||
release, so a refused turn keeps the tracking.
|
the follow camera will leave, before that release, so a refused turn keeps the
|
||||||
|
tracking.
|
||||||
|
|
||||||
Analyst region queries resolve unique Natural Earth names offline without
|
Analyst region queries resolve unique Natural Earth names offline without
|
||||||
geocoding, so Gold Coast and Ivory Coast name the West African natural regions.
|
geocoding, so Gold Coast and Ivory Coast name the West African natural regions.
|
||||||
|
|||||||
@@ -412,12 +412,24 @@ test('drags that start on the stars orbit above the free-look height', () => {
|
|||||||
|
|
||||||
const lower = freeLookFixture({ height: 500 });
|
const lower = freeLookFixture({ height: 500 });
|
||||||
lower.controller.minimumTrackBallHeight = 500_000;
|
lower.controller.minimumTrackBallHeight = 500_000;
|
||||||
installFreeLookHeightLimit(lower.viewer)();
|
const disposeLower = installFreeLookHeightLimit(lower.viewer);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
lower.controller.minimumTrackBallHeight,
|
lower.controller.minimumTrackBallHeight,
|
||||||
500_000,
|
500_000,
|
||||||
'never raised',
|
'never raised',
|
||||||
);
|
);
|
||||||
|
disposeLower();
|
||||||
|
assert.equal(lower.controller.minimumTrackBallHeight, 500_000);
|
||||||
|
|
||||||
|
const later = freeLookFixture({ height: 500 });
|
||||||
|
const disposeLater = installFreeLookHeightLimit(later.viewer);
|
||||||
|
later.controller.minimumTrackBallHeight = 2_000_000;
|
||||||
|
disposeLater();
|
||||||
|
assert.equal(
|
||||||
|
later.controller.minimumTrackBallHeight,
|
||||||
|
2_000_000,
|
||||||
|
'dispose keeps a value set after install',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('free look keeps a setting other code makes after it is installed', () => {
|
test('free look keeps a setting other code makes after it is installed', () => {
|
||||||
@@ -739,6 +751,31 @@ test('the recovery leaves a camera flight and an authored scene alone', () => {
|
|||||||
assert.ok(Cesium.Math.equalsEpsilon(camera.pitch, -Math.PI / 2, 1e-6));
|
assert.ok(Cesium.Math.equalsEpsilon(camera.pitch, -Math.PI / 2, 1e-6));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a check a scene or flight defers runs once it ends', () => {
|
||||||
|
for (const viaMoveEnd of [false, true]) {
|
||||||
|
let playing = true;
|
||||||
|
const timers = fakeTimers();
|
||||||
|
const { camera, viewer } = realCamera({ width: 1225, height: 923 });
|
||||||
|
installGlobeInViewRecovery(viewer, {
|
||||||
|
paused: () => playing,
|
||||||
|
...timers,
|
||||||
|
});
|
||||||
|
aim(camera, reportedStuckView);
|
||||||
|
camera.moveStart.raiseEvent();
|
||||||
|
// A hold on a stars-only shot: quiet, with or without Cesium's moveEnd.
|
||||||
|
for (let i = 0; i < 4; i += 1) timers.tick();
|
||||||
|
if (viaMoveEnd) camera.moveEnd.raiseEvent();
|
||||||
|
assert.equal(globeOutOfFrame(camera), true, 'left alone while it plays');
|
||||||
|
assert.equal(timers.pending(), 1, 'still watching');
|
||||||
|
playing = false;
|
||||||
|
timers.tick();
|
||||||
|
assert.ok(
|
||||||
|
Cesium.Math.equalsEpsilon(camera.pitch, -Math.PI / 2, 1e-6),
|
||||||
|
`recovered once the scene ends (${viaMoveEnd ? 'after moveEnd' : 'no moveEnd'})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function fakeTimers() {
|
function fakeTimers() {
|
||||||
let next = 1;
|
let next = 1;
|
||||||
const pending = new Map();
|
const pending = new Map();
|
||||||
|
|||||||
+15
-10
@@ -112,7 +112,9 @@ export function installTrackpadPinchZoom(
|
|||||||
* Allow Cesium's free look only below `maxHeight` and return its disposer.
|
* Allow Cesium's free look only below `maxHeight` and return its disposer.
|
||||||
* Orbit, tilt and zoom stay available at every height. A value other code
|
* Orbit, tilt and zoom stay available at every height. A value other code
|
||||||
* sets later is kept as the one to allow below that height, and restored on
|
* sets later is kept as the one to allow below that height, and restored on
|
||||||
* dispose. `enableLook` gates the Shift+drag binding only: below its trackball
|
* dispose; a `false` written while the camera is above `maxHeight` cannot be
|
||||||
|
* told apart from this limit's own write, so dispose restores the earlier
|
||||||
|
* value then. `enableLook` gates the Shift+drag binding only: below its trackball
|
||||||
* height Cesium also turns the camera in place for a drag that starts on the
|
* height Cesium also turns the camera in place for a drag that starts on the
|
||||||
* stars. That height is lowered to `maxHeight` too, so above it such a drag
|
* stars. That height is lowered to `maxHeight` too, so above it such a drag
|
||||||
* orbits the globe, as it does above Cesium's own 7,500 km.
|
* orbits the globe, as it does above Cesium's own 7,500 km.
|
||||||
@@ -171,7 +173,8 @@ const SETTLE_GIVE_UP_MS = 5 * 60_000;
|
|||||||
* looking away from the planet.
|
* looking away from the planet.
|
||||||
* Heading and position are kept. Tracked cameras, locked ones (non-identity
|
* Heading and position are kept. Tracked cameras, locked ones (non-identity
|
||||||
* transform), a camera flight in progress, the 2D and Columbus modes, and any
|
* transform), a camera flight in progress, the 2D and Columbus modes, and any
|
||||||
* time `paused` returns true (an authored scene playing) are left alone.
|
* time `paused` returns true (an authored scene playing) are left alone; a
|
||||||
|
* check they defer runs again once they end, within the settle check's bound.
|
||||||
* `keyholeRadius`
|
* `keyholeRadius`
|
||||||
* returns the radius of the visible circle when a scope mask hides the rest of
|
* returns the radius of the visible circle when a scope mask hides the rest of
|
||||||
* the frame (see `globeFrameShare`), or 0; it is read at every check.
|
* the frame (see `globeFrameShare`), or 0; it is read at every check.
|
||||||
@@ -202,16 +205,18 @@ export function installGlobeInViewRecovery(
|
|||||||
if (!scene || !camera?.moveEnd || !camera?.moveStart)
|
if (!scene || !camera?.moveEnd || !camera?.moveStart)
|
||||||
throw new TypeError('A complete Cesium viewer is required');
|
throw new TypeError('A complete Cesium viewer is required');
|
||||||
|
|
||||||
|
// Returns false while a flight or a paused scene defers the check, so the
|
||||||
|
// caller can try again once it ends.
|
||||||
const recover = () => {
|
const recover = () => {
|
||||||
if (!isFreeGlobeCamera(viewer)) return;
|
if (!isFreeGlobeCamera(viewer)) return true;
|
||||||
// Cesium has no public running-flight flag.
|
// Cesium has no public running-flight flag.
|
||||||
if (camera._currentFlight || paused()) return;
|
if (camera._currentFlight || paused()) return false;
|
||||||
const height = camera.positionCartographic?.height;
|
const height = camera.positionCartographic?.height;
|
||||||
if (
|
if (
|
||||||
!(height > minHeight) ||
|
!(height > minHeight) ||
|
||||||
!globeOutOfFrame(camera, { keyholeRadius: keyholeRadius() })
|
!globeOutOfFrame(camera, { keyholeRadius: keyholeRadius() })
|
||||||
)
|
)
|
||||||
return;
|
return true;
|
||||||
camera.setView({
|
camera.setView({
|
||||||
orientation: {
|
orientation: {
|
||||||
heading: camera.heading,
|
heading: camera.heading,
|
||||||
@@ -220,6 +225,7 @@ export function installGlobeInViewRecovery(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
scene.requestRender?.();
|
scene.requestRender?.();
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const lastPosition = new Cesium.Cartesian3();
|
const lastPosition = new Cesium.Cartesian3();
|
||||||
@@ -255,10 +261,8 @@ export function installGlobeInViewRecovery(
|
|||||||
}
|
}
|
||||||
// Two quiet samples in a row after a move: at rest for half a second, as
|
// Two quiet samples in a row after a move: at rest for half a second, as
|
||||||
// for moveEnd.
|
// for moveEnd.
|
||||||
if (restingSamples >= 2 && movedSinceCheck) {
|
if (restingSamples >= 2 && movedSinceCheck && recover())
|
||||||
movedSinceCheck = false;
|
movedSinceCheck = false;
|
||||||
recover();
|
|
||||||
}
|
|
||||||
if (settleElapsed >= SETTLE_GIVE_UP_MS) return;
|
if (settleElapsed >= SETTLE_GIVE_UP_MS) return;
|
||||||
Cesium.Cartesian3.clone(camera.positionWC, lastPosition);
|
Cesium.Cartesian3.clone(camera.positionWC, lastPosition);
|
||||||
Cesium.Cartesian3.clone(camera.directionWC, lastDirection);
|
Cesium.Cartesian3.clone(camera.directionWC, lastDirection);
|
||||||
@@ -274,8 +278,9 @@ export function installGlobeInViewRecovery(
|
|||||||
settleTimer = schedule(sample, SETTLE_SAMPLE_MS);
|
settleTimer = schedule(sample, SETTLE_SAMPLE_MS);
|
||||||
};
|
};
|
||||||
const removeMoveEnd = camera.moveEnd.addEventListener(() => {
|
const removeMoveEnd = camera.moveEnd.addEventListener(() => {
|
||||||
stopSettling();
|
if (recover()) stopSettling();
|
||||||
recover();
|
// Deferred: keep sampling so the check runs once the flight or scene ends.
|
||||||
|
else if (settleTimer === null) startSettling();
|
||||||
});
|
});
|
||||||
const removeMoveStart = camera.moveStart.addEventListener(startSettling);
|
const removeMoveStart = camera.moveStart.addEventListener(startSettling);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user