fix(camera): orbit on drags from the stars above 1,000 km

Below Cesium's trackball height (7,500 km by default) a drag that starts
on the stars turns the camera in place, whatever enableLook says, which
was the drag path to a view with no planet in it. Lower that height to
the free-look height with the free-look limit, never raising it, and
restore it on dispose. Above 1,000 km such a drag now orbits the globe,
as it already did above 7,500 km, at a similar rate, and a tilt drag
from the stars there tilts about the planet's edge or does nothing
instead of turning the camera. Drags that start on the planet are
unchanged.
This commit is contained in:
Manjunath committed 2026-11-05 12:20:26 +05:30
1 parent 64377db91a
commit 2f9d13f39d
3 files changed
+38 -11

No files matched your search

+5 -4
View File
@@ -67,7 +67,10 @@ 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. Orbit, tilt and frame and leave only stars, so Shift+drag does nothing there. Cesium also
turns the camera in place for a drag that starts on the stars below its
trackball height (7,500 km by default); that height is lowered to 1,000 km, so
such a drag orbits the globe above it, as it does higher up. 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
@@ -96,9 +99,7 @@ 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 zoom-outs in view; this catches the other ways a move can end looking pinch zoom-outs in view; this catches the other ways a move can end looking
away, such as a saved or shared view that did, or a drag away, such as a saved or shared view that did.
that Cesium turns into an in-place look below its 7,500 km trackball height (one
that starts on the stars, or a tilt drag with the screen centre off the planet).
Views with more surface than that, such as the oblique orbital Director shots, Views with more surface than that, such as the oblique orbital Director shots,
are left as they are, and so are tracked or locked cameras, a camera flight in are left as they are, and so are tracked or locked cameras, a camera flight in
progress, a Director scene while it plays (holds included) and the 2D and progress, a Director scene while it plays (holds included) and the 2D and
+22 -1
View File
@@ -359,7 +359,7 @@ test('trackpad pinch falls back to the original event when relay construction fa
function freeLookFixture({ enableLook = true, height = 0 } = {}) { function freeLookFixture({ enableLook = true, height = 0 } = {}) {
const listeners = new Set(); const listeners = new Set();
const controller = { enableLook }; const controller = { enableLook, minimumTrackBallHeight: 7_500_000 };
const camera = { positionCartographic: { height } }; const camera = { positionCartographic: { height } };
const viewer = { const viewer = {
camera, camera,
@@ -399,6 +399,27 @@ test('free look follows camera height and is restored on dispose', () => {
assert.equal(f.listeners.size, 0); assert.equal(f.listeners.size, 0);
}); });
test('drags that start on the stars orbit above the free-look height', () => {
// Below Cesium's trackball height such a drag turns the camera in place.
const f = freeLookFixture({ height: 500 });
const dispose = installFreeLookHeightLimit(f.viewer);
assert.equal(
f.controller.minimumTrackBallHeight,
FREE_LOOK_MAX_HEIGHT_METERS,
);
dispose();
assert.equal(f.controller.minimumTrackBallHeight, 7_500_000);
const lower = freeLookFixture({ height: 500 });
lower.controller.minimumTrackBallHeight = 500_000;
installFreeLookHeightLimit(lower.viewer)();
assert.equal(
lower.controller.minimumTrackBallHeight,
500_000,
'never raised',
);
});
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', () => {
const f = freeLookFixture({ height: 500 }); const f = freeLookFixture({ height: 500 });
const dispose = installFreeLookHeightLimit(f.viewer); const dispose = installFreeLookHeightLimit(f.viewer);
+11 -6
View File
@@ -112,10 +112,10 @@ 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 * dispose. `enableLook` gates the Shift+drag binding only: below its trackball
* Shift+drag binding only; a drag that starts on the stars below Cesium's * height Cesium also turns the camera in place for a drag that starts on the
* trackball height still turns the camera in place, which * stars. That height is lowered to `maxHeight` too, so above it such a drag
* `installGlobeInViewRecovery` catches. * orbits the globe, as it does above Cesium's own 7,500 km.
*/ */
export function installFreeLookHeightLimit( export function installFreeLookHeightLimit(
viewer, viewer,
@@ -127,6 +127,9 @@ export function installFreeLookHeightLimit(
if (!controller || !camera || !scene?.preUpdate) if (!controller || !camera || !scene?.preUpdate)
throw new TypeError('A complete Cesium viewer is required'); throw new TypeError('A complete Cesium viewer is required');
const originalTrackBallHeight = controller.minimumTrackBallHeight;
const trackBallHeight = Math.min(originalTrackBallHeight, maxHeight);
controller.minimumTrackBallHeight = trackBallHeight;
let wanted = controller.enableLook; let wanted = controller.enableLook;
let written; let written;
const sync = () => { const sync = () => {
@@ -145,6 +148,8 @@ export function installFreeLookHeightLimit(
active = false; active = false;
removeListener(); removeListener();
if (controller.enableLook === written) controller.enableLook = wanted; if (controller.enableLook === written) controller.enableLook = wanted;
if (controller.minimumTrackBallHeight === trackBallHeight)
controller.minimumTrackBallHeight = originalTrackBallHeight;
}; };
} }
@@ -162,8 +167,8 @@ const SETTLE_GIVE_UP_MS = 5 * 60_000;
* no surface on screen (`globeOutOfFrame`: limb glow and slivers up to about * no surface on screen (`globeOutOfFrame`: limb glow and slivers up to about
* one sample cell thick can count as none), and return the disposer. Views with * one sample cell thick can count as none), and return the disposer. Views with
* more surface, such as an oblique orbital shot, are left as they are. This * more surface, such as an oblique orbital shot, are left as they are. This
* catches a saved or shared view that looked away, and a drag that Cesium turns * catches a saved or shared view that looked away, or any other move that ends
* into an in-place look below its trackball height whatever `enableLook` says. * 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.