mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-08 20:34:02 +00:00
Set the field of view and read the matrices through the camera port
setFov holds the vertical angle it is given across resizes (Cesium keeps the wider one), waits for a frustum with a field of view, and rejects angles outside (0, pi). matrices returns Cesium's view and projection matrices, ECEF to eye in 3D, the only mode Core runs. The conformance run checks both on Cesium.
This commit is contained in:
1 parent
c6f043d3fd
commit
764b40950f
4 files changed
+60
-4
No files matched your search
@@ -333,8 +333,6 @@ const EXPECTED_MISSING = [
|
||||
'camera.controls.suspend',
|
||||
'camera.controls.zoomFactor',
|
||||
'camera.controls.zoomInputs',
|
||||
'camera.matrices',
|
||||
'camera.setFov',
|
||||
'draw.atlas',
|
||||
'draw.clampState',
|
||||
'draw.on',
|
||||
|
||||
@@ -91,7 +91,10 @@ export function createCesiumCamera({ scene, camera, report, widget }) {
|
||||
report,
|
||||
});
|
||||
removers.push(
|
||||
scene.postUpdate.addEventListener(() => cameraEvents.stepChanged()),
|
||||
scene.postUpdate.addEventListener(() => {
|
||||
applyFov();
|
||||
cameraEvents.stepChanged();
|
||||
}),
|
||||
camera.moveStart.addEventListener(() => cameraEvents.emit('moveStart')),
|
||||
camera.moveEnd.addEventListener(() => cameraEvents.emit('moveEnd')),
|
||||
);
|
||||
@@ -176,6 +179,23 @@ export function createCesiumCamera({ scene, camera, report, widget }) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* The vertical field of view `setFov` asked for, held across resizes:
|
||||
* Cesium's `fov` is the wider of the two angles (the horizontal one when
|
||||
* the view is wider than tall), and it keeps that one when the aspect
|
||||
* changes. Before the first resize, and on a frustum without a field of
|
||||
* view, it waits.
|
||||
*/
|
||||
let heldFovy = null;
|
||||
function applyFov() {
|
||||
const frustum = camera.frustum;
|
||||
const aspect = frustum?.aspectRatio;
|
||||
if (heldFovy === null || !('fov' in frustum) || !(aspect > 0)) return;
|
||||
const fov =
|
||||
aspect > 1 ? 2 * Math.atan(Math.tan(heldFovy / 2) * aspect) : heldFovy;
|
||||
if (frustum.fov !== fov) frustum.fov = fov;
|
||||
}
|
||||
|
||||
const cameraPort = {
|
||||
on: (event, cb, options) => cameraEvents.on(event, cb, options),
|
||||
onCommand: (cb) => commandHooks.on(cb),
|
||||
@@ -288,6 +308,24 @@ export function createCesiumCamera({ scene, camera, report, widget }) {
|
||||
else camera.zoomIn(metres);
|
||||
scene.requestRender();
|
||||
},
|
||||
setFov({ vertical }) {
|
||||
// Cesium would take a bad angle and draw a garbage projection.
|
||||
if (!(vertical > 0 && vertical < Math.PI))
|
||||
throw new RangeError(`no vertical field of view of ${vertical}`);
|
||||
heldFovy = vertical;
|
||||
applyFov();
|
||||
scene.requestRender();
|
||||
},
|
||||
// The scene's own frame: ECEF in 3D, the only mode Core runs (Cesium's
|
||||
// 2D and Columbus view frames are projected map coordinates).
|
||||
matrices() {
|
||||
return {
|
||||
view: Float64Array.from(Cesium.Matrix4.toArray(camera.viewMatrix)),
|
||||
projection: Float64Array.from(
|
||||
Cesium.Matrix4.toArray(camera.frustum.projectionMatrix),
|
||||
),
|
||||
};
|
||||
},
|
||||
viewRectangle() {
|
||||
const rect = camera.computeViewRectangle(Cesium.Ellipsoid.WGS84);
|
||||
if (!rect) return null;
|
||||
|
||||
@@ -25,7 +25,7 @@ const QUERY_RECORDS = new Set([
|
||||
'occluded',
|
||||
]);
|
||||
/** Commands and records the port does not have yet. */
|
||||
const MISSING = new Set(['matrices']);
|
||||
const MISSING = new Set([]);
|
||||
|
||||
const runnable = CAMERA_SCRIPTS.filter((script) =>
|
||||
script.steps.every(
|
||||
@@ -232,3 +232,21 @@ test('a Cesium viewer with no renderer bound gets a camera port over its own cam
|
||||
assert.throws(() => cameraOf({ scene, camera: {} }), /no renderer camera/);
|
||||
});
|
||||
});
|
||||
|
||||
test('setFov holds the vertical angle across resizes, and waits for a first one', () => {
|
||||
const { scene, camera } = headlessCamera();
|
||||
scene.postUpdate = new Cesium.Event();
|
||||
scene.requestRender = () => {};
|
||||
camera.frustum.aspectRatio = NaN;
|
||||
const { port } = createCesiumCamera({ scene, camera, report: () => {} });
|
||||
port.setFov({ vertical: 0.5 });
|
||||
for (const aspect of [16 / 9, 0.6, 4 / 3]) {
|
||||
camera.frustum.aspectRatio = aspect;
|
||||
scene.postUpdate.raiseEvent(scene);
|
||||
assert.ok(Math.abs(camera.frustum.fovy - 0.5) < 1e-12, `aspect ${aspect}`);
|
||||
}
|
||||
camera.switchToOrthographicFrustum();
|
||||
assert.doesNotThrow(() => port.setFov({ vertical: 0.4 }));
|
||||
assert.throws(() => port.setFov({ vertical: Math.PI }), RangeError);
|
||||
assert.throws(() => port.setFov({ vertical: NaN }), RangeError);
|
||||
});
|
||||
@@ -27,6 +27,8 @@ export const CESIUM_CHECKS = new Set([
|
||||
'camera.trace.flyTo with a duration of 0 completes inside the call',
|
||||
'camera.trace.flyTo where the camera already is completes inside the call',
|
||||
'camera.viewRectangle',
|
||||
'camera.fov',
|
||||
'camera.trace.view and projection matrices',
|
||||
'draw.drillPick',
|
||||
'draw.ids',
|
||||
'draw.models',
|
||||
|
||||
Reference in new issue
Block a user