From c2f7e64ef8a3539a740ede97e5173fba2297f167 Mon Sep 17 00:00:00 2001
From: Gabby Getz <gabby@cesium.com>
Date: Tue, 30 May 2023 14:53:36 -0400
Subject: [PATCH] Fix for zoom on tracked entity

---
 CHANGES.md                                    |  1 +
 .../Scene/ScreenSpaceCameraController.js      |  3 +-
 .../Scene/ScreenSpaceCameraControllerSpec.js  | 68 ++++++++++++++++++-
 3 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 6e8e01a1515c..21f3524b2808 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -6,6 +6,7 @@
 
 ##### Fixes :wrench:
 
+- Fixed tracked entity camera controls. [#11286](https://github.com/CesiumGS/cesium/issues/11286)
 - Fixed color creation from CSS color string with modern "space-separated" syntax. [#11271](https://github.com/CesiumGS/cesium/pull/11271)
 - Fixed a race condition when loading cut-out terrain. [#11296](https://github.com/CesiumGS/cesium/pull/11296)
 - Fixed async behavior for custom terrain and imagery providers. [#11274](https://github.com/CesiumGS/cesium/issues/11274)
diff --git a/packages/engine/Source/Scene/ScreenSpaceCameraController.js b/packages/engine/Source/Scene/ScreenSpaceCameraController.js
index d83065068c25..38fda58fde80 100644
--- a/packages/engine/Source/Scene/ScreenSpaceCameraController.js
+++ b/packages/engine/Source/Scene/ScreenSpaceCameraController.js
@@ -613,6 +613,7 @@ function handleZoom(
       object._zoomMouseStart
     );
 
+    // When camera transform is set, such as tracking an entity, object._globe will be undefined, and no position should be picked
     if (defined(object._globe) && mode === SceneMode.SCENE2D) {
       pickedPosition = camera.getPickRay(startPosition, scratchZoomPickRay)
         .origin;
@@ -621,7 +622,7 @@ function handleZoom(
         pickedPosition.z,
         pickedPosition.x
       );
-    } else {
+    } else if (defined(object._globe)) {
       pickedPosition = pickPosition(
         object,
         startPosition,
diff --git a/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js
index 4e784b06d7e7..f4d3fadca566 100644
--- a/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js
+++ b/packages/engine/Specs/Scene/ScreenSpaceCameraControllerSpec.js
@@ -929,7 +929,7 @@ describe("Scene/ScreenSpaceCameraController", function () {
     );
   });
 
-  it("rotates in Columus view with camera transform set", function () {
+  it("rotates in Columbus view with camera transform set", function () {
     setUpCV();
 
     const origin = Cartesian3.fromDegrees(-72.0, 40.0);
@@ -983,7 +983,7 @@ describe("Scene/ScreenSpaceCameraController", function () {
     expect(camera.position).not.toEqual(position);
   });
 
-  it("zooms in Columus view with camera transform set", function () {
+  it("zooms in Columbus view with camera transform set", function () {
     setUpCV();
 
     const origin = Cartesian3.fromDegrees(-72.0, 40.0);
@@ -1198,6 +1198,70 @@ describe("Scene/ScreenSpaceCameraController", function () {
     );
   });
 
+  it("zooms in on an object in 3D", function () {
+    setUp3D();
+
+    scene.globe = new MockGlobe(scene.mapProjection.ellipsoid);
+
+    updateController();
+
+    const origin = Cartesian3.fromDegrees(-72.0, 40.0, 1.0);
+    camera.setView({
+      destination: origin,
+    });
+
+    updateController();
+
+    scene.pickPositionSupported = true;
+    scene.pickPositionWorldCoordinates = () =>
+      Cartesian3.fromDegrees(-72.0, 40.0, -10.0);
+
+    const position = Cartesian3.clone(camera.position);
+    const startPosition = new Cartesian2(0, 0);
+    const endPosition = new Cartesian2(0, canvas.clientHeight / 2);
+
+    moveMouse(MouseButtons.RIGHT, startPosition, endPosition);
+
+    updateController();
+
+    expect(Cartesian3.magnitude(position)).toBeGreaterThan(
+      Cartesian3.magnitude(camera.position)
+    );
+  });
+
+  it("zooms in on an object in 3D when transform is set", function () {
+    setUp3D();
+
+    scene.globe = new MockGlobe(scene.mapProjection.ellipsoid);
+
+    updateController();
+
+    const origin = Cartesian3.fromDegrees(-72.0, 40.0, 1.0);
+    camera.lookAtTransform(Transforms.eastNorthUpToFixedFrame(origin), {
+      heading: 0,
+      pitch: 0,
+      range: 10,
+    });
+
+    updateController();
+
+    scene.pickPositionSupported = true;
+    scene.pickPositionWorldCoordinates = () =>
+      Cartesian3.fromDegrees(-72.0, 40.0, -10.0);
+
+    const position = Cartesian3.clone(camera.position);
+    const startPosition = new Cartesian2(0, 0);
+    const endPosition = new Cartesian2(0, canvas.clientHeight / 2);
+
+    moveMouse(MouseButtons.RIGHT, startPosition, endPosition);
+
+    updateController();
+
+    expect(Cartesian3.magnitude(position)).toBeGreaterThan(
+      Cartesian3.magnitude(camera.position)
+    );
+  });
+
   it("zoom in 3D to point 0,0", function () {
     setUp3D();
     scene.globe = new MockGlobe(scene.mapProjection.ellipsoid);