From d143143e1d03910250dbe0278a7fe63dd24a461a Mon Sep 17 00:00:00 2001 From: Albin Ekberg Date: Thu, 29 Aug 2024 10:42:47 +0200 Subject: [PATCH 1/5] Added ScreenSpaceCameraController.maximumTiltAngle to limit how much the camera can tilt --- CHANGES.md | 1 + CONTRIBUTORS.md | 1 + .../Scene/ScreenSpaceCameraController.js | 16 +++- .../Scene/ScreenSpaceCameraControllerSpec.js | 73 +++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index b5103f0b189f..c27d460d968c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -33,6 +33,7 @@ This is an npm-only release to extra source maps included in 1.121 - Added `WaterMask` globe material, which visualizes areas of water or land based on the terrain's water mask. [#12149](https://github.com/CesiumGS/cesium/pull/12149) - Made the `time` parameter optional for `Property`, using `JulianDate.now()` as default. [#12099](https://github.com/CesiumGS/cesium/pull/12099) - Exposes `ScreenSpaceCameraController.zoomFactor` to allow adjusting the zoom factor (speed). [#9145](https://github.com/CesiumGS/cesium/pull/9145) +- Added `ScreenSpaceCameraController.maximumTiltAngle` to limit how much the camera can tilt. ##### Fixes :wrench: diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index a2f79f8461f9..1a1f2a83e18c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -186,6 +186,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu - [Tang Xiaofei](https://github.com/vtxf) - [Maxar](https://www.maxar.com) - [Erik Dahlström](https://github.com/erikdahlstrom) + - [Albin Ekberg](https://github.com/albek446) - [Novatron Oy](https://novatron.fi/en/) - [Jussi Hirvonen](https://github.com/VsKatshuma) - [Sierra Nevada Corp](https://www.sncorp.com/) diff --git a/packages/engine/Source/Scene/ScreenSpaceCameraController.js b/packages/engine/Source/Scene/ScreenSpaceCameraController.js index 44493e3def42..69182b184d12 100644 --- a/packages/engine/Source/Scene/ScreenSpaceCameraController.js +++ b/packages/engine/Source/Scene/ScreenSpaceCameraController.js @@ -271,6 +271,11 @@ function ScreenSpaceCameraController(scene) { * @default true */ this.enableCollisionDetection = true; + /** + * If set, the camera will not be able to tilt past this angle, expressed in radians. + * @type {number} + */ + this.maximumTiltAngle = undefined; this._scene = scene; this._globe = undefined; @@ -2058,7 +2063,16 @@ function rotate3D( ); const deltaPhi = rotateRate * phiWindowRatio * Math.PI * 2.0; - const deltaTheta = rotateRate * thetaWindowRatio * Math.PI; + let deltaTheta = rotateRate * thetaWindowRatio * Math.PI; + + if (defined(constrainedAxis) && defined(controller.maximumTiltAngle)) { + const maximumTiltAngle = controller.maximumTiltAngle; + const dotProduct = Cartesian3.dot(camera.direction, constrainedAxis); + const tilt = Math.PI - Math.acos(dotProduct) + deltaTheta; + if (tilt > maximumTiltAngle) { + deltaTheta -= tilt - maximumTiltAngle; + } + } if (!rotateOnlyVertical) { camera.rotateRight(deltaPhi); diff --git a/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js index d5367349c33a..bdc056ad318a 100644 --- a/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -1566,6 +1566,79 @@ describe("Scene/ScreenSpaceCameraController", function () { expect(camera.direction).not.toEqual(direction); }); + it("maximum tilt angle is 0 in 3D", function () { + setUp3D(); + const originalCameraPosition = Cartesian3.clone(camera.position); + const originalCameraDirection = Cartesian3.clone(camera.direction); + controller.maximumTiltAngle = 0; + const startPosition = new Cartesian2( + canvas.clientWidth / 2, + canvas.clientHeight / 2 + ); + const endPosition = new Cartesian2( + canvas.clientWidth / 2, + canvas.clientHeight / 4 + ); + + moveMouse(MouseButtons.MIDDLE, startPosition, endPosition); + updateController(); + + expect(camera.position).toEqualEpsilon( + originalCameraPosition, + CesiumMath.EPSILON8 + ); + expect(camera.position).not.toEqualEpsilon( + originalCameraDirection, + CesiumMath.EPSILON8 + ); + + const dotProduct = Cartesian3.dot( + camera.direction, + originalCameraDirection + ); + const acos = Math.acos(dotProduct); + const angle = (acos * 180) / Math.PI; + expect(angle).toEqual(0); + }); + + it("maximum tilt angle is 45 degrees in 3D", function () { + setUp3D(); + const originalCameraPosition = Cartesian3.clone(camera.position); + const originalCameraDirection = Cartesian3.clone(camera.direction); + controller.maximumTiltAngle = (45 * Math.PI) / 180; + const startPosition = new Cartesian2( + canvas.clientWidth / 2, + canvas.clientHeight / 2 + ); + const endPosition = new Cartesian2( + canvas.clientWidth / 2, + canvas.clientHeight / 4 + ); + + moveMouse(MouseButtons.MIDDLE, startPosition, endPosition); + updateController(); + moveMouse(MouseButtons.MIDDLE, startPosition, endPosition); + updateController(); + + expect(camera.position).not.toEqualEpsilon( + originalCameraPosition, + CesiumMath.EPSILON8 + ); + expect(camera.position).not.toEqualEpsilon( + originalCameraDirection, + CesiumMath.EPSILON8 + ); + + const dotProduct = Cartesian3.dot( + camera.direction, + originalCameraDirection + ); + const acos = Math.acos(dotProduct); + const angle = (acos * 180) / Math.PI; + expect(angle).toBeLessThanOrEqual(45); + expect(angle).toBeGreaterThan(44); + }); + it("looks in 3D", function () { setUp3D(); const position = Cartesian3.clone(camera.position); From b8649d0c3258864e048a4427ebf3fb7740a2ae95 Mon Sep 17 00:00:00 2001 From: Albin Ekberg Date: Tue, 1 Oct 2024 08:44:09 +0200 Subject: [PATCH 2/5] Added an example for the maximumTiltAngle property --- packages/engine/Source/Scene/ScreenSpaceCameraController.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/engine/Source/Scene/ScreenSpaceCameraController.js b/packages/engine/Source/Scene/ScreenSpaceCameraController.js index 69182b184d12..1aa0a852723a 100644 --- a/packages/engine/Source/Scene/ScreenSpaceCameraController.js +++ b/packages/engine/Source/Scene/ScreenSpaceCameraController.js @@ -274,6 +274,10 @@ function ScreenSpaceCameraController(scene) { /** * If set, the camera will not be able to tilt past this angle, expressed in radians. * @type {number} + * + * @example + * // prevent camera from tilting below ellipsoid surface + * controller.maximumTiltAngle = Math.PI / 2 */ this.maximumTiltAngle = undefined; From 4ce83e94c8d0e5b6ffa61abaa96de949cb542dce Mon Sep 17 00:00:00 2001 From: Albin Ekberg Date: Thu, 3 Oct 2024 16:25:18 +0200 Subject: [PATCH 3/5] Prettier Merge --- .../Scene/ScreenSpaceCameraControllerSpec.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js index 1caee97c1688..f46ed5410c4b 100644 --- a/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -1575,11 +1575,11 @@ describe("Scene/ScreenSpaceCameraController", function () { controller.maximumTiltAngle = 0; const startPosition = new Cartesian2( canvas.clientWidth / 2, - canvas.clientHeight / 2 + canvas.clientHeight / 2, ); const endPosition = new Cartesian2( canvas.clientWidth / 2, - canvas.clientHeight / 4 + canvas.clientHeight / 4, ); moveMouse(MouseButtons.MIDDLE, startPosition, endPosition); @@ -1587,16 +1587,16 @@ describe("Scene/ScreenSpaceCameraController", function () { expect(camera.position).toEqualEpsilon( originalCameraPosition, - CesiumMath.EPSILON8 + CesiumMath.EPSILON8, ); expect(camera.position).not.toEqualEpsilon( originalCameraDirection, - CesiumMath.EPSILON8 + CesiumMath.EPSILON8, ); const dotProduct = Cartesian3.dot( camera.direction, - originalCameraDirection + originalCameraDirection, ); const acos = Math.acos(dotProduct); const angle = (acos * 180) / Math.PI; @@ -1610,11 +1610,11 @@ describe("Scene/ScreenSpaceCameraController", function () { controller.maximumTiltAngle = (45 * Math.PI) / 180; const startPosition = new Cartesian2( canvas.clientWidth / 2, - canvas.clientHeight / 2 + canvas.clientHeight / 2, ); const endPosition = new Cartesian2( canvas.clientWidth / 2, - canvas.clientHeight / 4 + canvas.clientHeight / 4, ); moveMouse(MouseButtons.MIDDLE, startPosition, endPosition); @@ -1624,16 +1624,16 @@ describe("Scene/ScreenSpaceCameraController", function () { expect(camera.position).not.toEqualEpsilon( originalCameraPosition, - CesiumMath.EPSILON8 + CesiumMath.EPSILON8, ); expect(camera.position).not.toEqualEpsilon( originalCameraDirection, - CesiumMath.EPSILON8 + CesiumMath.EPSILON8, ); const dotProduct = Cartesian3.dot( camera.direction, - originalCameraDirection + originalCameraDirection, ); const acos = Math.acos(dotProduct); const angle = (acos * 180) / Math.PI; From c52823befe3f7a8a52fffe3c4c1560ac18e50752 Mon Sep 17 00:00:00 2001 From: ggetz Date: Wed, 9 Oct 2024 13:49:19 -0400 Subject: [PATCH 4/5] Adjust documentation typing and wording --- Apps/Sandcastle/gallery/Bathymetry.html | 3 +++ .../engine/Source/Scene/ScreenSpaceCameraController.js | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Apps/Sandcastle/gallery/Bathymetry.html b/Apps/Sandcastle/gallery/Bathymetry.html index 3a98117ed331..edca677a751a 100644 --- a/Apps/Sandcastle/gallery/Bathymetry.html +++ b/Apps/Sandcastle/gallery/Bathymetry.html @@ -100,6 +100,9 @@ const scene = viewer.scene; + // Prevent the user from tilting beyond the ellipsoid surface + scene.screenSpaceCameraController.maximumTiltAngle = Math.PI / 2.0; + const globe = scene.globe; globe.enableLighting = true; globe.maximumScreenSpaceError = 1.0; // Load higher resolution tiles for better seafloor shading diff --git a/packages/engine/Source/Scene/ScreenSpaceCameraController.js b/packages/engine/Source/Scene/ScreenSpaceCameraController.js index db07c9b2fa71..869bb9ac1c93 100644 --- a/packages/engine/Source/Scene/ScreenSpaceCameraController.js +++ b/packages/engine/Source/Scene/ScreenSpaceCameraController.js @@ -272,12 +272,13 @@ function ScreenSpaceCameraController(scene) { */ this.enableCollisionDetection = true; /** - * If set, the camera will not be able to tilt past this angle, expressed in radians. - * @type {number} + * The angle, relative to the ellipsoid normal, restricting the maximum amount that the user can tilt the camera. If undefined, the angle of the camera tilt is unrestricted. + * @type {number|undefined} + * @default undefined * * @example - * // prevent camera from tilting below ellipsoid surface - * controller.maximumTiltAngle = Math.PI / 2 + * // Prevent the camera from tilting below the ellipsoid surface + * viewer.scene.screenSpaceCameraController.maximumTiltAngle = Math.PI / 2.0; */ this.maximumTiltAngle = undefined; From e13bf626e0b89c04b64373aa0ac85203db239ff0 Mon Sep 17 00:00:00 2001 From: ggetz Date: Wed, 9 Oct 2024 13:51:47 -0400 Subject: [PATCH 5/5] Move changenotes to appropriate release --- CHANGES.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 3e454d134665..806d9b219076 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ # Change Log +### 1.123 - 2024-11-01 + +#### @cesium/engine + +##### Additions :tada: + +- Added `ScreenSpaceCameraController.maximumTiltAngle` to limit how much the camera can tilt. [#12169](https://github.com/CesiumGS/cesium/pull/12169) + ### 1.122 - 2024-10-01 #### @cesium/engine @@ -40,7 +48,6 @@ This is an npm-only release to extra source maps included in 1.121 - Added `WaterMask` globe material, which visualizes areas of water or land based on the terrain's water mask. [#12149](https://github.com/CesiumGS/cesium/pull/12149) - Made the `time` parameter optional for `Property`, using `JulianDate.now()` as default. [#12099](https://github.com/CesiumGS/cesium/pull/12099) - Exposes `ScreenSpaceCameraController.zoomFactor` to allow adjusting the zoom factor (speed). [#9145](https://github.com/CesiumGS/cesium/pull/9145) -- Added `ScreenSpaceCameraController.maximumTiltAngle` to limit how much the camera can tilt. ##### Fixes :wrench: