mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
fix(globe): document Cesium's default view angles and check them
FlightOptions and setView now state Cesium's defaults for missing angles (heading 0, pitch -pi/2, roll 0), enforced by a contract-suite check. The suite also checks a horizon-bounded view rectangle and an undefined one when the camera looks away from the Earth.
This commit is contained in:
1 parent
708c96b6f6
commit
cd94a26b9f
2 files changed
+90
-1
No files matched your search
@@ -46,7 +46,9 @@
|
|||||||
*
|
*
|
||||||
* @typedef {object} FlightOptions
|
* @typedef {object} FlightOptions
|
||||||
* @property {Cartesian3} destination
|
* @property {Cartesian3} destination
|
||||||
* @property {Orientation} [orientation] Missing angles keep the current ones.
|
* @property {Orientation} [orientation] As in Cesium's setView and flyTo,
|
||||||
|
* missing angles default to heading 0, pitch −π/2 and roll 0 (they do not
|
||||||
|
* keep the current ones).
|
||||||
* @property {number} [duration] Seconds. 0 (or less) sets the view and calls
|
* @property {number} [duration] Seconds. 0 (or less) sets the view and calls
|
||||||
* `complete` synchronously, before flyTo returns, as Cesium does.
|
* `complete` synchronously, before flyTo returns, as Cesium does.
|
||||||
* @property {'cubicInOut'|'sinusoidalInOut'|'linear'} [easing]
|
* @property {'cubicInOut'|'sinusoidalInOut'|'linear'} [easing]
|
||||||
@@ -70,6 +72,7 @@
|
|||||||
* @typedef {object} GlobeCamera
|
* @typedef {object} GlobeCamera
|
||||||
* @property {() => CameraPose} pose Fresh values on every call.
|
* @property {() => CameraPose} pose Fresh values on every call.
|
||||||
* @property {(view: { destination: Cartesian3, orientation?: Orientation }) => void} setView
|
* @property {(view: { destination: Cartesian3, orientation?: Orientation }) => void} setView
|
||||||
|
* Missing angles default to heading 0, pitch −π/2, roll 0.
|
||||||
* @property {(options: FlightOptions) => void} flyTo Cancels a running flight first.
|
* @property {(options: FlightOptions) => void} flyTo Cancels a running flight first.
|
||||||
* @property {(sphere: { center: Cartesian3, radius: number }, options?: SphereFlightOptions) => void} flyToSphere
|
* @property {(sphere: { center: Cartesian3, radius: number }, options?: SphereFlightOptions) => void} flyToSphere
|
||||||
* Ends unlocked (identity transform).
|
* Ends unlocked (identity transform).
|
||||||
|
|||||||
@@ -1064,6 +1064,92 @@ export function createContractSuite(
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'camera.setView and flyTo: missing angles default to heading 0, pitch −π/2, roll 0',
|
||||||
|
async () => {
|
||||||
|
const destination = cartesian3.fromDegrees(40, 30, 20000);
|
||||||
|
const expectDefaults = (label) => {
|
||||||
|
const pose = camera.pose();
|
||||||
|
near(pose.pitch, -math.PI_OVER_TWO, 1e-6, `${label} pitch`);
|
||||||
|
near(angleBetween(pose.heading, 0), 0, 1e-6, `${label} heading`);
|
||||||
|
near(pose.roll, 0, 1e-6, `${label} roll`);
|
||||||
|
};
|
||||||
|
const tilted = { heading: 1, pitch: -0.3, roll: 0.2 };
|
||||||
|
camera.setView({ destination, orientation: tilted });
|
||||||
|
camera.setView({ destination });
|
||||||
|
await settle();
|
||||||
|
expectDefaults('setView without orientation');
|
||||||
|
camera.setView({ destination, orientation: tilted });
|
||||||
|
camera.setView({ destination, orientation: {} });
|
||||||
|
expectDefaults('setView with empty orientation');
|
||||||
|
camera.setView({ destination, orientation: tilted });
|
||||||
|
camera.flyTo({ destination, duration: 0 });
|
||||||
|
expectDefaults('flyTo without orientation');
|
||||||
|
camera.setView({ destination, orientation: tilted });
|
||||||
|
camera.setView({ destination, orientation: { heading: 1 } });
|
||||||
|
const partial = camera.pose();
|
||||||
|
near(angleBetween(partial.heading, 1), 0, 1e-6, 'partial heading');
|
||||||
|
near(partial.pitch, -math.PI_OVER_TWO, 1e-6, 'partial pitch default');
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'camera.viewRectangle: a horizontal view is bounded by the horizon',
|
||||||
|
async () => {
|
||||||
|
camera.setView({
|
||||||
|
destination: cartesian3.fromDegrees(20, 10, 100000),
|
||||||
|
orientation: { heading: 0, pitch: 0, roll: 0 },
|
||||||
|
});
|
||||||
|
await settle();
|
||||||
|
const rectangle = camera.viewRectangle();
|
||||||
|
ensure(rectangle, 'no view rectangle looking at the horizon');
|
||||||
|
for (const side of ['west', 'south', 'east', 'north'])
|
||||||
|
ensure(Number.isFinite(rectangle[side]), `${side} is not finite`);
|
||||||
|
// Looking north, Cesium's rectangle is built from the canvas corners
|
||||||
|
// that hit the globe (the bottom ones) and horizon points for the
|
||||||
|
// rest; it need not contain the camera's own position.
|
||||||
|
const { width, height } = engine.host.size();
|
||||||
|
for (const x of [0, width]) {
|
||||||
|
const ground = camera.pickEllipsoid(cartesian2.create(x, height));
|
||||||
|
ensure(
|
||||||
|
isCartesian3(ground),
|
||||||
|
`the bottom corner at x=${x} missed the globe`,
|
||||||
|
);
|
||||||
|
const seen = cartographic.fromCartesian(ground, WGS84);
|
||||||
|
ensure(
|
||||||
|
rectangle.west <= seen.longitude + 1e-9 &&
|
||||||
|
seen.longitude - 1e-9 <= rectangle.east &&
|
||||||
|
rectangle.south <= seen.latitude + 1e-9 &&
|
||||||
|
seen.latitude - 1e-9 <= rectangle.north,
|
||||||
|
`the visible corner at x=${x} lies outside the rectangle`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const lon = math.toRadians(20);
|
||||||
|
ensure(
|
||||||
|
rectangle.west < lon && lon < rectangle.east,
|
||||||
|
`camera longitude outside ${rectangle.west}..${rectangle.east}`,
|
||||||
|
);
|
||||||
|
ensure(
|
||||||
|
rectangle.east - rectangle.west < math.toRadians(30) &&
|
||||||
|
rectangle.north - rectangle.south < math.toRadians(30),
|
||||||
|
'the rectangle is not bounded by the horizon',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'camera.viewRectangle: undefined when looking away from the Earth',
|
||||||
|
async () => {
|
||||||
|
camera.setView({
|
||||||
|
destination: cartesian3.fromDegrees(20, 10, 1e6),
|
||||||
|
orientation: { heading: 0, pitch: math.PI_OVER_TWO, roll: 0 },
|
||||||
|
});
|
||||||
|
await settle();
|
||||||
|
const rectangle = camera.viewRectangle();
|
||||||
|
ensure(
|
||||||
|
rectangle === undefined,
|
||||||
|
`looking away from the Earth gave ${JSON.stringify(rectangle)}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'camera.viewRectangle: spans all longitudes over a pole',
|
'camera.viewRectangle: spans all longitudes over a pole',
|
||||||
async () => {
|
async () => {
|
||||||
|
|||||||
Reference in new issue
Block a user