mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-12 14:22:21 +00:00
Keep the tilt clearance through a zoom in
A zoom in during a pivoting tilt stops at the range that keeps the camera 150 m above the pivot at its current pitch. A zoom that moves the range by less than half a metre (at the clearance, the 80 m floor or the 5,000 km cap) now answers not zoomed instead of ok, and a tilt reports tiltRangeAdjusted rather than orbitRadiusAdjusted.
This commit is contained in:
1 parent
9d8db97a33
commit
fdecc8c581
4 files changed
+78
-15
No files matched your search
+46
-3
@@ -8,6 +8,7 @@ import {
|
||||
interruptCameraMotion,
|
||||
moveCamera,
|
||||
} from './cameraVerbs.js';
|
||||
import { createGevActionRunner } from './voice/gevActions.js';
|
||||
|
||||
const radians = Cesium.Math.toRadians;
|
||||
const degrees = Cesium.Math.toDegrees;
|
||||
@@ -238,7 +239,11 @@ test('a zoom during a pivoting tilt changes the range it holds', (t) => {
|
||||
);
|
||||
h.tick();
|
||||
const before = pivotRange(h);
|
||||
assert.equal(adjustOrbitRange(0.5), true, 'the tilt must take the zoom');
|
||||
assert.deepEqual(
|
||||
adjustOrbitRange(0.5),
|
||||
{ kind: 'tilt', changed: true },
|
||||
'the tilt must take the zoom',
|
||||
);
|
||||
for (let i = 0; i < 5; i += 1) h.tick();
|
||||
// The next frame's lookAt must not put the old range back.
|
||||
assert.ok(
|
||||
@@ -250,7 +255,7 @@ test('a zoom during a pivoting tilt changes the range it holds', (t) => {
|
||||
test('a zoom with no pivot to hold is left to the camera', (t) => {
|
||||
harness(t, { pivot: false });
|
||||
moveCamera({ motion: 'tilt', direction: 'up', mode: 'continuous' });
|
||||
assert.equal(adjustOrbitRange(0.5), false);
|
||||
assert.equal(adjustOrbitRange(0.5), null);
|
||||
});
|
||||
|
||||
test('a pivot tilt toward the horizon keeps its clearance above the ground', (t) => {
|
||||
@@ -364,11 +369,49 @@ test('a zoom out during a far orbit never pulls the camera in', (t) => {
|
||||
h.tick();
|
||||
const before = pivotRange(h);
|
||||
assert.ok(before > 5_000_000, `range ${before} m`);
|
||||
assert.equal(adjustOrbitRange(2.4), true);
|
||||
assert.deepEqual(adjustOrbitRange(2.4), { kind: 'orbit', changed: false });
|
||||
h.tick();
|
||||
assert.ok(pivotRange(h) >= before - 1, `range fell to ${pivotRange(h)} m`);
|
||||
});
|
||||
|
||||
test('a zoom in during a pivot tilt stops at the clearance', (t) => {
|
||||
// 400 m of range at -40°, then "zoom in a lot" twice: unfloored, that left
|
||||
// the camera about 107 m up, then at the 80 m zoom floor.
|
||||
const h = harness(t, { height: 400 });
|
||||
moveCamera({ motion: 'tilt', direction: 'up', mode: 'continuous' });
|
||||
while (pitchDeg(h.camera) < -40) h.tick();
|
||||
assert.deepEqual(adjustOrbitRange(1 / 2.4), { kind: 'tilt', changed: true });
|
||||
// At -40° the floor is 150 / sin 40° ≈ 233 m, which the first zoom reached.
|
||||
assert.deepEqual(adjustOrbitRange(1 / 2.4), { kind: 'tilt', changed: false });
|
||||
for (let i = 0; i < 600 && getActiveCameraMotion(); i += 1) {
|
||||
h.tick();
|
||||
assert.ok(heightM(h.camera) > 149, `camera at ${heightM(h.camera)} m`);
|
||||
}
|
||||
assert.ok(Math.abs(pitchDeg(h.camera) - -5) < 0.5);
|
||||
});
|
||||
|
||||
test('a voice zoom during a pivot tilt says what it changed', async (t) => {
|
||||
const h = harness(t, { height: 400 });
|
||||
const runner = createGevActionRunner({
|
||||
viewer: h.viewer,
|
||||
styleManager: {},
|
||||
dataManager: { layers: new Map(), getAll: () => [] },
|
||||
});
|
||||
moveCamera({ motion: 'tilt', direction: 'up', mode: 'continuous' });
|
||||
h.tick();
|
||||
const zoom = { direction: 'in', amount: 'lot' };
|
||||
const first = await runner('adjust_camera_zoom', zoom);
|
||||
assert.equal(first.ok, true);
|
||||
assert.equal(first.tiltRangeAdjusted, true);
|
||||
assert.equal('orbitRadiusAdjusted' in first, false);
|
||||
// 400 m → 167 m → the 150 m clearance; a third zoom in moves nothing, and
|
||||
// says so.
|
||||
assert.equal((await runner('adjust_camera_zoom', zoom)).ok, true);
|
||||
const third = await runner('adjust_camera_zoom', zoom);
|
||||
assert.equal(third.ok, false);
|
||||
assert.match(third.error, /tilt is already at its closest range/);
|
||||
});
|
||||
|
||||
test('from globe range a tilt happens in place', (t) => {
|
||||
// A pivot 2,000 km out would swing the camera thousands of km.
|
||||
const h = harness(t, { height: 2_000_000 });
|
||||
|
||||
+16
-8
@@ -1932,19 +1932,27 @@ export function moveCamera(args = {}, runNavigation = null) {
|
||||
* fight the per-frame lookAt (field test: "zoom out did literally nothing").
|
||||
* A tilt swinging about its pivot holds a range the same way.
|
||||
* @param {number} factor >1 zooms out (radius grows), <1 zooms in.
|
||||
* @returns {boolean} true when an orbit or pivoting tilt consumed the zoom.
|
||||
* @returns {{kind: string, changed: boolean}|null} the motion that consumed
|
||||
* the zoom, and whether its range moved; null when none did.
|
||||
*/
|
||||
export function adjustOrbitRange(factor) {
|
||||
if (!_active || !['orbit', 'tilt'].includes(_active.kind) || !_active.hpr)
|
||||
return false;
|
||||
// The limits never move a view the wrong way: a zoom in leaves a closer
|
||||
// view in place, and a zoom out leaves a farther one.
|
||||
const range = _active.hpr.range;
|
||||
_active.hpr.range = Math.max(
|
||||
Math.min(80, range),
|
||||
return null;
|
||||
// A tilt's zoom in stops at the range that keeps its clearance. The limits
|
||||
// never move a view the wrong way: a zoom in leaves a closer view in place,
|
||||
// and a zoom out leaves a farther one.
|
||||
const { kind, hpr } = _active;
|
||||
const floor =
|
||||
kind === 'tilt'
|
||||
? Math.max(80, TILT_MIN_CLEARANCE_M / Math.abs(Math.sin(hpr.pitch)))
|
||||
: 80;
|
||||
const range = hpr.range;
|
||||
hpr.range = Math.max(
|
||||
Math.min(floor, range),
|
||||
Math.min(Math.max(5_000_000, range), range * factor),
|
||||
);
|
||||
return true;
|
||||
// Under half a metre counts as not moved, as for a plain zoom.
|
||||
return { kind, changed: Math.abs(hpr.range - range) >= 0.5 };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+15
-3
@@ -411,13 +411,25 @@ export function createGevActionRunner({
|
||||
const zoomOut = String(args.direction || '').toLowerCase() === 'out';
|
||||
const amt = String(args.amount || 'medium').toLowerCase();
|
||||
const factor = { little: 1.25, medium: 1.6, lot: 2.4 }[amt] || 1.6;
|
||||
if (adjustOrbitRange(zoomOut ? factor : 1 / factor)) {
|
||||
const held = adjustOrbitRange(zoomOut ? factor : 1 / factor);
|
||||
if (held) {
|
||||
const direction = zoomOut ? 'out' : 'in';
|
||||
if (!held.changed) {
|
||||
return {
|
||||
ok: false,
|
||||
action: 'adjust_camera_zoom',
|
||||
direction,
|
||||
amount: amt,
|
||||
error: `Not zoomed: the ${held.kind} is already at its ${zoomOut ? 'farthest' : 'closest'} range.`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
action: 'adjust_camera_zoom',
|
||||
direction: zoomOut ? 'out' : 'in',
|
||||
direction,
|
||||
amount: amt,
|
||||
orbitRadiusAdjusted: true,
|
||||
[held.kind === 'tilt' ? 'tiltRangeAdjusted' : 'orbitRadiusAdjusted']:
|
||||
true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user