From 56c4e8de651ade1aec763b812f060bac258b9b1e Mon Sep 17 00:00:00 2001
From: Eugene Maksymenko <maxigene@gmail.com>
Date: Thu, 4 Aug 2022 22:34:28 +0300
Subject: [PATCH] Remove WorldWindow dependency from Camera.

---
 .../BasicPerformanceBenchmarkActivity.java    |  24 +--
 .../nasa/worldwindx/GeneralGlobeActivity.java |   7 +-
 .../OmnidirectionalSightlineActivity.java     |   2 +-
 .../PlacemarksMilStd2525Activity.java         |   2 +-
 .../PlacemarksSelectDragActivity.java         |   2 +-
 .../main/assets/look_at_view_tutorial.html    |   2 +-
 .../assets/navigator_events_tutorial.html     |   5 +-
 .../assets/placemarks_picking_tutorial.html   |   2 +-
 .../src/main/assets/placemarks_tutorial.html  |   2 +-
 .../worldwindx/CameraControlFragment.java     |  22 +-
 .../nasa/worldwindx/LookAtViewFragment.java   |   2 +-
 .../worldwindx/NavigatorEventFragment.java    |   7 +-
 .../OmnidirectionalSightlineFragment.java     |   2 +-
 .../nasa/worldwindx/PlacemarksFragment.java   |   2 +-
 .../worldwindx/PlacemarksPickingFragment.java |   2 +-
 .../worldwind/BasicWorldWindowController.java |  20 +-
 .../java/gov/nasa/worldwind/Navigator.java    |  51 ++---
 .../java/gov/nasa/worldwind/WorldWindow.java  |   6 +-
 .../java/gov/nasa/worldwind/geom/Camera.java  |  66 +++---
 .../gov/nasa/worldwind/geom/FrustumTest.java  | 190 +++++++++---------
 20 files changed, 214 insertions(+), 204 deletions(-)

diff --git a/worldwind-examples/src/main/java/gov/nasa/worldwindx/BasicPerformanceBenchmarkActivity.java b/worldwind-examples/src/main/java/gov/nasa/worldwindx/BasicPerformanceBenchmarkActivity.java
index 80c0a73ec..2a14281d9 100644
--- a/worldwind-examples/src/main/java/gov/nasa/worldwindx/BasicPerformanceBenchmarkActivity.java
+++ b/worldwind-examples/src/main/java/gov/nasa/worldwindx/BasicPerformanceBenchmarkActivity.java
@@ -67,9 +67,9 @@ public static class AnimateCameraCommand implements Runnable {
         public AnimateCameraCommand(WorldWindow wwd, Camera end, int steps) {
             this.wwd = wwd;
 
-            this.beginCamera = new Camera(wwd);
-            this.endCamera = new Camera(wwd);
-            this.curCamera = new Camera(wwd);
+            this.beginCamera = new Camera();
+            this.endCamera = new Camera();
+            this.curCamera = new Camera();
 
             this.endCamera.set(end);
             this.steps = steps;
@@ -117,7 +117,7 @@ public static SetCameraCommand obtain(WorldWindow wwd, Camera camera) {
 
         private SetCameraCommand set(WorldWindow wwd, Camera camera) {
             this.wwd = wwd;
-            this.camera = new Camera(wwd);
+            this.camera = new Camera();
             this.camera.set(camera);
             return this;
         }
@@ -240,12 +240,12 @@ protected void onStart() {
         exec.execute(new ClearFrameMetricsCommand(wwd));
 
         // After a 1/2 second delay, fly to NASA Ames Research Center over 100 frames.
-        Camera cam = new Camera(this.getWorldWindow()).set(arc.latitude, arc.longitude, 10e3, WorldWind.ABSOLUTE, 0, 0, 0);
+        Camera cam = new Camera().set(arc.latitude, arc.longitude, 10e3, WorldWind.ABSOLUTE, 0, 0, 0);
         exec.execute(new AnimateCameraCommand(wwd, cam, 100));
 
         // After a 1/2 second delay, rotate the camera to look at NASA Goddard Space Flight Center over 50 frames.
         double azimuth = arc.greatCircleAzimuth(gsfc);
-        cam = new Camera(this.getWorldWindow()).set(arc.latitude, arc.longitude, 10e3, WorldWind.ABSOLUTE, azimuth, 70, 0);
+        cam = new Camera().set(arc.latitude, arc.longitude, 10e3, WorldWind.ABSOLUTE, azimuth, 70, 0);
         exec.execute(new SleepCommand(500));
         exec.execute(new AnimateCameraCommand(wwd, cam, 50));
 
@@ -253,27 +253,27 @@ protected void onStart() {
         Location midLoc = arc.interpolateAlongPath(gsfc, WorldWind.GREAT_CIRCLE, 0.5, new Location());
         azimuth = midLoc.greatCircleAzimuth(gsfc);
         exec.execute(new SleepCommand(500));
-        cam = new Camera(this.getWorldWindow()).set(midLoc.latitude, midLoc.longitude, 1000e3, WorldWind.ABSOLUTE, azimuth, 0, 0);
+        cam = new Camera().set(midLoc.latitude, midLoc.longitude, 1000e3, WorldWind.ABSOLUTE, azimuth, 0, 0);
         exec.execute(new AnimateCameraCommand(wwd, cam, 100));
-        cam = new Camera(this.getWorldWindow()).set(gsfc.latitude, gsfc.longitude, 10e3, WorldWind.ABSOLUTE, azimuth, 70, 0);
+        cam = new Camera().set(gsfc.latitude, gsfc.longitude, 10e3, WorldWind.ABSOLUTE, azimuth, 70, 0);
         exec.execute(new AnimateCameraCommand(wwd, cam, 100));
 
         // After a 1/2 second delay, rotate the camera to look at ESA Centre for Earth Observation over 50 frames.
         azimuth = gsfc.greatCircleAzimuth(esrin);
-        cam = new Camera(this.getWorldWindow()).set(gsfc.latitude, gsfc.longitude, 10e3, WorldWind.ABSOLUTE, azimuth, 90, 0);
+        cam = new Camera().set(gsfc.latitude, gsfc.longitude, 10e3, WorldWind.ABSOLUTE, azimuth, 90, 0);
         exec.execute(new SleepCommand(500));
         exec.execute(new AnimateCameraCommand(wwd, cam, 50));
 
         // After a 1/2 second delay, fly the camera to ESA Centre for Earth Observation over 200 frames.
         midLoc = gsfc.interpolateAlongPath(esrin, WorldWind.GREAT_CIRCLE, 0.5, new Location());
         exec.execute(new SleepCommand(500));
-        cam = new Camera(this.getWorldWindow()).set(midLoc.latitude, midLoc.longitude, 1000e3, WorldWind.ABSOLUTE, azimuth, 60, 0);
+        cam = new Camera().set(midLoc.latitude, midLoc.longitude, 1000e3, WorldWind.ABSOLUTE, azimuth, 60, 0);
         exec.execute(new AnimateCameraCommand(wwd, cam, 100));
-        cam = new Camera(this.getWorldWindow()).set(esrin.latitude, esrin.longitude, 100e3, WorldWind.ABSOLUTE, azimuth, 30, 0);
+        cam = new Camera().set(esrin.latitude, esrin.longitude, 100e3, WorldWind.ABSOLUTE, azimuth, 30, 0);
         exec.execute(new AnimateCameraCommand(wwd, cam, 100));
 
         // After a 1/2 second delay, back the camera out to look at ESA Centre for Earth Observation over 100 frames.
-        cam = new Camera(this.getWorldWindow()).set(esrin.latitude, esrin.longitude, 2000e3, WorldWind.ABSOLUTE, 0, 0, 0);
+        cam = new Camera().set(esrin.latitude, esrin.longitude, 2000e3, WorldWind.ABSOLUTE, 0, 0, 0);
         exec.execute(new SleepCommand(500));
         exec.execute(new AnimateCameraCommand(wwd, cam, 100));
 
diff --git a/worldwind-examples/src/main/java/gov/nasa/worldwindx/GeneralGlobeActivity.java b/worldwind-examples/src/main/java/gov/nasa/worldwindx/GeneralGlobeActivity.java
index fd6dbec27..8993f2150 100644
--- a/worldwind-examples/src/main/java/gov/nasa/worldwindx/GeneralGlobeActivity.java
+++ b/worldwind-examples/src/main/java/gov/nasa/worldwindx/GeneralGlobeActivity.java
@@ -16,10 +16,12 @@
 
 import gov.nasa.worldwind.NavigatorEvent;
 import gov.nasa.worldwind.NavigatorListener;
+import gov.nasa.worldwind.PickedObject;
 import gov.nasa.worldwind.WorldWind;
 import gov.nasa.worldwind.WorldWindow;
 import gov.nasa.worldwind.geom.Camera;
 import gov.nasa.worldwind.geom.LookAt;
+import gov.nasa.worldwind.geom.Position;
 
 /**
  * Creates a general purpose globe view with touch navigation, a few layers, and a coordinates overlay.
@@ -79,9 +81,12 @@ public void onNavigatorEvent(WorldWindow wwd, NavigatorEvent event) {
                 // Update the status overlay views whenever the navigator stops moving,
                 // and also it is moving but at an (arbitrary) maximum refresh rate of 20 Hz.
                 if (eventAction == WorldWind.NAVIGATOR_STOPPED || elapsedTime > 50) {
+                    // Pick terrain located behind the viewport center point
+                    PickedObject terrainPickedObject = wwd.pick(wwd.getViewport().width / 2f, wwd.getViewport().height / 2f).terrainPickedObject();
+                    Position terrainPosition = terrainPickedObject != null ? terrainPickedObject.getTerrainPosition() : null;
 
                     // Get the current camera state to apply to the overlays
-                    event.getCamera().getAsLookAt(lookAt);
+                    event.getCamera().getAsLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), terrainPosition, lookAt);
 
                     // Update the overlays
                     updateOverlayContents(lookAt, event.getCamera());
diff --git a/worldwind-examples/src/main/java/gov/nasa/worldwindx/OmnidirectionalSightlineActivity.java b/worldwind-examples/src/main/java/gov/nasa/worldwindx/OmnidirectionalSightlineActivity.java
index 6aa734ed2..14e17f018 100644
--- a/worldwind-examples/src/main/java/gov/nasa/worldwindx/OmnidirectionalSightlineActivity.java
+++ b/worldwind-examples/src/main/java/gov/nasa/worldwindx/OmnidirectionalSightlineActivity.java
@@ -89,7 +89,7 @@ protected void onCreate(Bundle savedInstanceState) {
 
         // And finally, for this demo, position the viewer to look at the sightline position
         LookAt lookAt = new LookAt().set(pos.latitude, pos.longitude, pos.altitude, WorldWind.ABSOLUTE, 2e4 /*range*/, 0 /*heading*/, 45 /*tilt*/, 0 /*roll*/);
-        this.getWorldWindow().getCamera().setFromLookAt(lookAt);
+        this.getWorldWindow().getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), lookAt);
     }
 
     /**
diff --git a/worldwind-examples/src/main/java/gov/nasa/worldwindx/PlacemarksMilStd2525Activity.java b/worldwind-examples/src/main/java/gov/nasa/worldwindx/PlacemarksMilStd2525Activity.java
index c793506df..35beac70f 100644
--- a/worldwind-examples/src/main/java/gov/nasa/worldwindx/PlacemarksMilStd2525Activity.java
+++ b/worldwind-examples/src/main/java/gov/nasa/worldwindx/PlacemarksMilStd2525Activity.java
@@ -39,7 +39,7 @@ protected void onCreate(Bundle savedInstanceState) {
         Position pos = new Position(32.4520, 63.44553, 0);
         LookAt lookAt = new LookAt().set(pos.latitude, pos.longitude, pos.altitude, WorldWind.ABSOLUTE,
             1e5 /*range*/, 0 /*heading*/, 45 /*tilt*/, 0 /*roll*/);
-        this.getWorldWindow().getCamera().setFromLookAt(lookAt);
+        this.getWorldWindow().getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), lookAt);
 
         // The MIL-STD-2525 rendering library takes time initialize, we'll perform this task via the
         // AsyncTask's background thread and then load the symbols in its post execute handler.
diff --git a/worldwind-examples/src/main/java/gov/nasa/worldwindx/PlacemarksSelectDragActivity.java b/worldwind-examples/src/main/java/gov/nasa/worldwindx/PlacemarksSelectDragActivity.java
index 5ebd98a2d..f63a606bc 100644
--- a/worldwind-examples/src/main/java/gov/nasa/worldwindx/PlacemarksSelectDragActivity.java
+++ b/worldwind-examples/src/main/java/gov/nasa/worldwindx/PlacemarksSelectDragActivity.java
@@ -172,7 +172,7 @@ protected void onCreate(Bundle savedInstanceState) {
 
         // And finally, for this demo, position the viewer to look at the placemarks
         LookAt lookAt = new LookAt().set(34.150, -119.150, 0, WorldWind.ABSOLUTE, 2e4 /*range*/, 0 /*heading*/, 45 /*tilt*/, 0 /*roll*/);
-        this.getWorldWindow().getCamera().setFromLookAt(lookAt);
+        this.getWorldWindow().getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), lookAt);
     }
 
     /**
diff --git a/worldwind-tutorials/src/main/assets/look_at_view_tutorial.html b/worldwind-tutorials/src/main/assets/look_at_view_tutorial.html
index c65025892..b438232ed 100644
--- a/worldwind-tutorials/src/main/assets/look_at_view_tutorial.html
+++ b/worldwind-tutorials/src/main/assets/look_at_view_tutorial.html
@@ -58,7 +58,7 @@ <h3>LookAtViewFragment.java</h3>
         // Apply the new view
         LookAt lookAt = new LookAt();
         lookAt.set(airport.latitude, airport.longitude, airport.altitude, WorldWind.ABSOLUTE, range, heading, tilt, 0 /*roll*/);
-        wwd.getCamera().setFromLookAt(lookAt);
+        wwd.getCamera().setFromLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), lookAt);
 
         return wwd;
     }
diff --git a/worldwind-tutorials/src/main/assets/navigator_events_tutorial.html b/worldwind-tutorials/src/main/assets/navigator_events_tutorial.html
index fa40f119e..cb69ff57e 100644
--- a/worldwind-tutorials/src/main/assets/navigator_events_tutorial.html
+++ b/worldwind-tutorials/src/main/assets/navigator_events_tutorial.html
@@ -85,9 +85,12 @@ <h3>NavigatorEventFragment.java</h3>
                 // Update the status overlay views whenever the navigator stops moving,
                 // and also it is moving but at an (arbitrary) maximum refresh rate of 20 Hz.
                 if (eventAction == WorldWind.NAVIGATOR_STOPPED || elapsedTime > 50) {
+                    // Pick terrain located behind the viewport center point
+                    PickedObject terrainPickedObject = wwd.pick(wwd.getViewport().width / 2f, wwd.getViewport().height / 2f).terrainPickedObject();
+                    Position terrainPosition = terrainPickedObject != null ? terrainPickedObject.getTerrainPosition() : null;
 
                     // Get the current camera state to apply to the overlays
-                    event.getCamera().getAsLookAt(lookAt);
+                    event.getCamera().getAsLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), terrainPosition, lookAt);
 
                     // Update the overlays
                     updateOverlayContents(lookAt, event.getCamera());
diff --git a/worldwind-tutorials/src/main/assets/placemarks_picking_tutorial.html b/worldwind-tutorials/src/main/assets/placemarks_picking_tutorial.html
index 3a0a0a583..a4c0bc1f9 100644
--- a/worldwind-tutorials/src/main/assets/placemarks_picking_tutorial.html
+++ b/worldwind-tutorials/src/main/assets/placemarks_picking_tutorial.html
@@ -62,7 +62,7 @@ <h3>PlacemarksPickingFragment.java</h3>
 
         // Position the viewer to look near the airports
         LookAt lookAt = new LookAt().set(34.15, -119.15, 0, WorldWind.ABSOLUTE, 2e4 /*range*/, 0 /*heading*/, 45 /*tilt*/, 0 /*roll*/);
-        wwd.getCamera().setFromLookAt(lookAt);
+        wwd.getCamera().setFromLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), lookAt);
 
         return wwd;
     }
diff --git a/worldwind-tutorials/src/main/assets/placemarks_tutorial.html b/worldwind-tutorials/src/main/assets/placemarks_tutorial.html
index 551be0d80..ec8bdf521 100644
--- a/worldwind-tutorials/src/main/assets/placemarks_tutorial.html
+++ b/worldwind-tutorials/src/main/assets/placemarks_tutorial.html
@@ -90,7 +90,7 @@ <h3>PlacemarksFragment.java</h3>
         Position pos = airport.getPosition();
         LookAt lookAt = new LookAt().set(pos.latitude, pos.longitude, pos.altitude, WorldWind.ABSOLUTE,
             1e5 /*range*/, 0 /*heading*/, 80 /*tilt*/, 0 /*roll*/);
-        wwd.getCamera().setFromLookAt(lookAt);
+        wwd.getCamera().setFromLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), lookAt);
 
         return wwd;
     }
diff --git a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/CameraControlFragment.java b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/CameraControlFragment.java
index 4033042b2..c0c1e9cea 100644
--- a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/CameraControlFragment.java
+++ b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/CameraControlFragment.java
@@ -10,6 +10,7 @@
 import gov.nasa.worldwind.WorldWindow;
 import gov.nasa.worldwind.geom.Camera;
 import gov.nasa.worldwind.geom.Location;
+import gov.nasa.worldwind.geom.Position;
 import gov.nasa.worldwind.gesture.GestureRecognizer;
 import gov.nasa.worldwind.gesture.PinchRecognizer;
 import gov.nasa.worldwind.gesture.RotationRecognizer;
@@ -17,6 +18,8 @@
 
 public class CameraControlFragment extends BasicGlobeFragment {
 
+    private static final double COLLISION_THRESHOLD = 20.0; // 20m above surface
+
     /**
      * Creates a new WorldWindow object with a custom WorldWindowController.
      */
@@ -42,7 +45,7 @@ public WorldWindow createWorldWindow() {
      * A custom WorldWindController that uses gestures to control the camera directly via the setAsCamera interface
      * instead of the default setAsLookAt interface.
      */
-    private class CameraController extends BasicWorldWindowController {
+    private static class CameraController extends BasicWorldWindowController {
 
         protected double beginHeading;
 
@@ -189,15 +192,22 @@ protected void gestureDidBegin() {
         }
 
         protected void applyLimits(Camera camera) {
-            double distanceToExtents = this.wwd.distanceToViewGlobeExtents();
+            Position position = camera.position;
 
             double minAltitude = 100;
-            double maxAltitude = distanceToExtents;
-            camera.position.altitude = WWMath.clamp(camera.position.altitude, minAltitude, maxAltitude);
+            double maxAltitude = this.wwd.distanceToViewGlobeExtents();
+            position.altitude = WWMath.clamp(position.altitude, minAltitude, maxAltitude);
+
+            // Check if camera altitude is not under the surface
+            double elevation = this.wwd.getGlobe().getElevationAtLocation(position.latitude, position.longitude)
+                    * wwd.getVerticalExaggeration() + COLLISION_THRESHOLD;
+            if (elevation > position.altitude) {
+                position.altitude = elevation;
+            }
 
             // Limit the tilt to between nadir and the horizon (roughly)
-            double r = wwd.getGlobe().getRadiusAt(camera.position.latitude, camera.position.latitude);
-            double maxTilt = Math.toDegrees(Math.asin(r / (r + camera.position.altitude)));
+            double r = wwd.getGlobe().getRadiusAt(position.latitude, position.latitude);
+            double maxTilt = Math.toDegrees(Math.asin(r / (r + position.altitude)));
             double minTilt = 0;
             camera.tilt = WWMath.clamp(camera.tilt, minTilt, maxTilt);
         }
diff --git a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/LookAtViewFragment.java b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/LookAtViewFragment.java
index 5f5ecb7ad..dd03c1e90 100644
--- a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/LookAtViewFragment.java
+++ b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/LookAtViewFragment.java
@@ -39,7 +39,7 @@ public WorldWindow createWorldWindow() {
         // Apply the new view
         LookAt lookAt = new LookAt();
         lookAt.set(airport.latitude, airport.longitude, airport.altitude, WorldWind.ABSOLUTE, range, heading, tilt, 0 /*roll*/);
-        wwd.getCamera().setFromLookAt(lookAt);
+        wwd.getCamera().setFromLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), lookAt);
 
         return wwd;
     }
diff --git a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/NavigatorEventFragment.java b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/NavigatorEventFragment.java
index 219cdae1d..1b6e8f7d9 100644
--- a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/NavigatorEventFragment.java
+++ b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/NavigatorEventFragment.java
@@ -18,10 +18,12 @@
 
 import gov.nasa.worldwind.NavigatorEvent;
 import gov.nasa.worldwind.NavigatorListener;
+import gov.nasa.worldwind.PickedObject;
 import gov.nasa.worldwind.WorldWind;
 import gov.nasa.worldwind.WorldWindow;
 import gov.nasa.worldwind.geom.Camera;
 import gov.nasa.worldwind.geom.LookAt;
+import gov.nasa.worldwind.geom.Position;
 
 public class NavigatorEventFragment extends BasicGlobeFragment {
 
@@ -78,9 +80,12 @@ public void onNavigatorEvent(WorldWindow wwd, NavigatorEvent event) {
                 // Update the status overlay views whenever the navigator stops moving,
                 // and also it is moving but at an (arbitrary) maximum refresh rate of 20 Hz.
                 if (eventAction == WorldWind.NAVIGATOR_STOPPED || elapsedTime > 50) {
+                    // Pick terrain located behind the viewport center point
+                    PickedObject terrainPickedObject = wwd.pick(wwd.getViewport().width / 2f, wwd.getViewport().height / 2f).terrainPickedObject();
+                    Position terrainPosition = terrainPickedObject != null ? terrainPickedObject.getTerrainPosition() : null;
 
                     // Get the current camera state to apply to the overlays
-                    event.getCamera().getAsLookAt(lookAt);
+                    event.getCamera().getAsLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), terrainPosition, lookAt);
 
                     // Update the overlays
                     updateOverlayContents(lookAt, event.getCamera());
diff --git a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/OmnidirectionalSightlineFragment.java b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/OmnidirectionalSightlineFragment.java
index c3f719988..5a9f3e341 100644
--- a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/OmnidirectionalSightlineFragment.java
+++ b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/OmnidirectionalSightlineFragment.java
@@ -69,6 +69,6 @@ protected void createPlacemark(Position position, RenderableLayer layer) {
 
     protected void positionView(WorldWindow wwd) {
         LookAt lookAt = new LookAt().set(46.230, -122.190, 500, WorldWind.ABSOLUTE, 1.5e4 /*range*/, 45.0 /*heading*/, 70.0 /*tilt*/, 0 /*roll*/);
-        wwd.getCamera().setFromLookAt(lookAt);
+        wwd.getCamera().setFromLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), lookAt);
     }
 }
diff --git a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/PlacemarksFragment.java b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/PlacemarksFragment.java
index 6ff093bc1..2913efdc0 100644
--- a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/PlacemarksFragment.java
+++ b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/PlacemarksFragment.java
@@ -80,7 +80,7 @@ public WorldWindow createWorldWindow() {
         Position pos = airport.getPosition();
         LookAt lookAt = new LookAt().set(pos.latitude, pos.longitude, pos.altitude, WorldWind.ABSOLUTE,
             1e5 /*range*/, 0 /*heading*/, 80 /*tilt*/, 0 /*roll*/);
-        wwd.getCamera().setFromLookAt(lookAt);
+        wwd.getCamera().setFromLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), lookAt);
 
         return wwd;
     }
diff --git a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/PlacemarksPickingFragment.java b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/PlacemarksPickingFragment.java
index 44604ca09..21ea73406 100644
--- a/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/PlacemarksPickingFragment.java
+++ b/worldwind-tutorials/src/main/java/gov/nasa/worldwindx/PlacemarksPickingFragment.java
@@ -53,7 +53,7 @@ public WorldWindow createWorldWindow() {
 
         // Position the viewer to look near the airports
         LookAt lookAt = new LookAt().set(34.15, -119.15, 0, WorldWind.ABSOLUTE, 2e4 /*range*/, 0 /*heading*/, 45 /*tilt*/, 0 /*roll*/);
-        wwd.getCamera().setFromLookAt(lookAt);
+        wwd.getCamera().setFromLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), lookAt);
 
         return wwd;
     }
diff --git a/worldwind/src/main/java/gov/nasa/worldwind/BasicWorldWindowController.java b/worldwind/src/main/java/gov/nasa/worldwind/BasicWorldWindowController.java
index 4593b594a..d8fb544cc 100644
--- a/worldwind/src/main/java/gov/nasa/worldwind/BasicWorldWindowController.java
+++ b/worldwind/src/main/java/gov/nasa/worldwind/BasicWorldWindowController.java
@@ -12,6 +12,7 @@
 
 import gov.nasa.worldwind.geom.Location;
 import gov.nasa.worldwind.geom.LookAt;
+import gov.nasa.worldwind.geom.Position;
 import gov.nasa.worldwind.gesture.GestureListener;
 import gov.nasa.worldwind.gesture.GestureRecognizer;
 import gov.nasa.worldwind.gesture.MousePanRecognizer;
@@ -79,7 +80,7 @@ public void resetOrientation(boolean headingOnly) {
             this.lookAt.tilt = 0;
             this.lookAt.roll = 0;
         }
-        this.wwd.getCamera().setFromLookAt(this.lookAt);
+        this.wwd.getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), this.lookAt);
         this.wwd.requestRedraw();
         this.gestureDidEnd();
     }
@@ -88,7 +89,7 @@ public void zoomIn() {
         this.gestureDidBegin();
         this.lookAt.range /= ZOOM_FACTOR;
         this.applyLimits(lookAt);
-        this.wwd.getCamera().setFromLookAt(this.lookAt);
+        this.wwd.getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), this.lookAt);
         this.wwd.requestRedraw();
         this.gestureDidEnd();
     }
@@ -97,7 +98,7 @@ public void zoomOut() {
         this.gestureDidBegin();
         this.lookAt.range *= ZOOM_FACTOR;
         this.applyLimits(lookAt);
-        this.wwd.getCamera().setFromLookAt(this.lookAt);
+        this.wwd.getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), this.lookAt);
         this.wwd.requestRedraw();
         this.gestureDidEnd();
     }
@@ -186,7 +187,7 @@ protected void handlePan(GestureRecognizer recognizer) {
                 this.lookAt.position.longitude = lon;
             }
 
-            this.wwd.getCamera().setFromLookAt(this.lookAt);
+            this.wwd.getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), this.lookAt);
             this.wwd.requestRedraw();
         } else if (state == WorldWind.ENDED || state == WorldWind.CANCELLED) {
             this.gestureDidEnd();
@@ -205,7 +206,7 @@ protected void handlePinch(GestureRecognizer recognizer) {
                 this.lookAt.range = this.beginLookAt.range / scale;
                 this.applyLimits(this.lookAt);
 
-                this.wwd.getCamera().setFromLookAt(this.lookAt);
+                this.wwd.getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), this.lookAt);
                 this.wwd.requestRedraw();
             }
         } else if (state == WorldWind.ENDED || state == WorldWind.CANCELLED) {
@@ -226,7 +227,7 @@ protected void handleRotate(GestureRecognizer recognizer) {
             this.lookAt.heading = WWMath.normalizeAngle360(this.lookAt.heading + headingDegrees);
             this.lastRotation = rotation;
 
-            this.wwd.getCamera().setFromLookAt(this.lookAt);
+            this.wwd.getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), this.lookAt);
             this.wwd.requestRedraw();
         } else if (state == WorldWind.ENDED || state == WorldWind.CANCELLED) {
             this.gestureDidEnd();
@@ -250,7 +251,7 @@ protected void handleTilt(GestureRecognizer recognizer) {
             this.lookAt.tilt = this.beginLookAt.tilt + tiltDegrees;
             this.applyLimits(this.lookAt);
 
-            this.wwd.getCamera().setFromLookAt(this.lookAt);
+            this.wwd.getCamera().setFromLookAt(this.wwd.getGlobe(), this.wwd.getVerticalExaggeration(), this.lookAt);
             this.wwd.requestRedraw();
         } else if (state == WorldWind.ENDED || state == WorldWind.CANCELLED) {
             this.gestureDidEnd();
@@ -273,7 +274,10 @@ protected void applyLimits(LookAt lookAt) {
 
     protected void gestureDidBegin() {
         if (this.activeGestures++ == 0) {
-            this.wwd.getCamera().getAsLookAt(this.beginLookAt);
+            // Pick terrain located behind the viewport center point
+            PickedObject terrainPickedObject = wwd.pick(wwd.getViewport().width / 2f, wwd.getViewport().height / 2f).terrainPickedObject();
+            Position terrainPosition = terrainPickedObject != null ? terrainPickedObject.getTerrainPosition() : null;
+            this.wwd.getCamera().getAsLookAt(wwd.getGlobe(), wwd.getVerticalExaggeration(), terrainPosition, this.beginLookAt);
             this.lookAt.set(this.beginLookAt);
         }
     }
diff --git a/worldwind/src/main/java/gov/nasa/worldwind/Navigator.java b/worldwind/src/main/java/gov/nasa/worldwind/Navigator.java
index 06b2721d8..3b4c5d2e5 100644
--- a/worldwind/src/main/java/gov/nasa/worldwind/Navigator.java
+++ b/worldwind/src/main/java/gov/nasa/worldwind/Navigator.java
@@ -8,74 +8,75 @@
 import gov.nasa.worldwind.geom.Camera;
 import gov.nasa.worldwind.geom.LookAt;
 import gov.nasa.worldwind.geom.Matrix4;
+import gov.nasa.worldwind.geom.Position;
 import gov.nasa.worldwind.globe.Globe;
 import gov.nasa.worldwind.util.Logger;
 
 @Deprecated
 public class Navigator {
 
-    private final WorldWindow wwd;
+    private final Camera camera;
 
-    public Navigator(WorldWindow wwd) {
-        if (wwd == null) {
+    public Navigator(Camera camera) {
+        if (camera == null) {
             throw new IllegalArgumentException(
-                    Logger.logMessage(Logger.ERROR, "Navigator", "constructor", "missingWorldWindow"));
+                    Logger.logMessage(Logger.ERROR, "Navigator", "constructor", "missingCamera"));
         }
 
-        this.wwd = wwd;
+        this.camera = camera;
     }
 
     public double getLatitude() {
-        return this.wwd.getCamera().position.latitude;
+        return this.camera.position.latitude;
     }
 
     public Navigator setLatitude(double latitude) {
-        this.wwd.getCamera().position.latitude = latitude;
+        this.camera.position.latitude = latitude;
         return this;
     }
 
     public double getLongitude() {
-        return this.wwd.getCamera().position.longitude;
+        return this.camera.position.longitude;
     }
 
     public Navigator setLongitude(double longitude) {
-        this.wwd.getCamera().position.longitude = longitude;
+        this.camera.position.longitude = longitude;
         return this;
     }
 
     public double getAltitude() {
-        return this.wwd.getCamera().position.altitude;
+        return this.camera.position.altitude;
     }
 
     public Navigator setAltitude(double altitude) {
-        this.wwd.getCamera().position.altitude = altitude;
+        this.camera.position.altitude = altitude;
         return this;
     }
 
     public double getHeading() {
-        return this.wwd.getCamera().heading;
+        return this.camera.heading;
     }
 
     public Navigator setHeading(double headingDegrees) {
-        this.wwd.getCamera().heading = headingDegrees;
+        this.camera.heading = headingDegrees;
         return this;
     }
 
     public double getTilt() {
-        return this.wwd.getCamera().tilt;
+        return this.camera.tilt;
     }
 
     public Navigator setTilt(double tiltDegrees) {
-        this.wwd.getCamera().tilt = tiltDegrees;
+        this.camera.tilt = tiltDegrees;
         return this;
     }
 
     public double getRoll() {
-        return this.wwd.getCamera().roll;
+        return this.camera.roll;
     }
 
     public Navigator setRoll(double rollDegrees) {
-        this.wwd.getCamera().roll = rollDegrees;
+        this.camera.roll = rollDegrees;
         return this;
     }
 
@@ -90,7 +91,7 @@ public Camera getAsCamera(Globe globe, Camera result) {
                 Logger.logMessage(Logger.ERROR, "Navigator", "getAsCamera", "missingResult"));
         }
 
-        result.set(this.wwd.camera);
+        result.set(this.camera);
 
         return result;
     }
@@ -106,12 +107,12 @@ public Navigator setAsCamera(Globe globe, Camera camera) {
                 Logger.logMessage(Logger.ERROR, "Navigator", "setAsCamera", "missingCamera"));
         }
 
-        this.wwd.camera.set(camera);
+        this.camera.set(camera);
 
         return this;
     }
 
-    public LookAt getAsLookAt(Globe globe, LookAt result) {
+    public LookAt getAsLookAt(Globe globe, double verticalExaggeration, Position terrainPosition, LookAt result) {
         if (globe == null) {
             throw new IllegalArgumentException(
                 Logger.logMessage(Logger.ERROR, "Navigator", "getAsLookAt", "missingGlobe"));
@@ -122,12 +123,12 @@ public LookAt getAsLookAt(Globe globe, LookAt result) {
                 Logger.logMessage(Logger.ERROR, "Navigator", "getAsLookAt", "missingResult"));
         }
 
-        this.wwd.getCamera().getAsLookAt(result);
+        this.camera.getAsLookAt(globe, verticalExaggeration, terrainPosition, result);
 
         return result;
     }
 
-    public Navigator setAsLookAt(Globe globe, LookAt lookAt) {
+    public Navigator setAsLookAt(Globe globe, double verticalExaggeration, LookAt lookAt) {
         if (globe == null) {
             throw new IllegalArgumentException(
                 Logger.logMessage(Logger.ERROR, "Navigator", "setAsLookAt", "missingGlobe"));
@@ -138,12 +139,12 @@ public Navigator setAsLookAt(Globe globe, LookAt lookAt) {
                 Logger.logMessage(Logger.ERROR, "Navigator", "setAsLookAt", "missingLookAt"));
         }
 
-        this.wwd.getCamera().setFromLookAt(lookAt);
+        this.camera.setFromLookAt(globe, verticalExaggeration, lookAt);
 
         return this;
     }
 
-    public Matrix4 getAsViewingMatrix(Globe globe, Matrix4 result) {
+    public Matrix4 getAsViewingMatrix(Globe globe, double verticalExaggeration, Matrix4 result) {
         if (globe == null) {
             throw new IllegalArgumentException(
                 Logger.logMessage(Logger.ERROR, "Navigator", "getAsViewingMatrix", "missingGlobe"));
@@ -154,7 +155,7 @@ public Matrix4 getAsViewingMatrix(Globe globe, Matrix4 result) {
                 Logger.logMessage(Logger.ERROR, "Navigator", "getAsViewingMatrix", "missingResult"));
         }
 
-        this.wwd.getCamera().computeViewingTransform(result);
+        this.camera.computeViewingTransform(globe, verticalExaggeration, result);
 
         return result;
     }
diff --git a/worldwind/src/main/java/gov/nasa/worldwind/WorldWindow.java b/worldwind/src/main/java/gov/nasa/worldwind/WorldWindow.java
index f3e59d82f..cbc7d9654 100644
--- a/worldwind/src/main/java/gov/nasa/worldwind/WorldWindow.java
+++ b/worldwind/src/main/java/gov/nasa/worldwind/WorldWindow.java
@@ -73,10 +73,10 @@ public class WorldWindow extends GLSurfaceView implements Choreographer.FrameCal
 
     protected double verticalExaggeration = 1;
 
-    protected Camera camera = new Camera(this);
+    protected Camera camera = new Camera();
 
     @Deprecated
-    protected Navigator navigator = new Navigator(this);
+    protected Navigator navigator = new Navigator(camera);
 
     protected NavigatorEventSupport navigatorEvents = new NavigatorEventSupport(this);
 
@@ -1062,6 +1062,6 @@ protected void computeViewingTransform(Matrix4 projection, Matrix4 modelview) {
         projection.setToPerspectiveProjection(this.viewport.width, this.viewport.height, this.camera.getFieldOfView(), near, far);
 
         // Compute a Cartesian transform matrix from the Camera.
-        this.camera.computeViewingTransform(modelview);
+        this.camera.computeViewingTransform(globe, verticalExaggeration, modelview);
     }
 }
diff --git a/worldwind/src/main/java/gov/nasa/worldwind/geom/Camera.java b/worldwind/src/main/java/gov/nasa/worldwind/geom/Camera.java
index 6b60a005b..be5bdfd75 100644
--- a/worldwind/src/main/java/gov/nasa/worldwind/geom/Camera.java
+++ b/worldwind/src/main/java/gov/nasa/worldwind/geom/Camera.java
@@ -5,9 +5,9 @@
 
 package gov.nasa.worldwind.geom;
 
-import gov.nasa.worldwind.PickedObject;
+import androidx.annotation.NonNull;
+
 import gov.nasa.worldwind.WorldWind;
-import gov.nasa.worldwind.WorldWindow;
 import gov.nasa.worldwind.globe.Globe;
 import gov.nasa.worldwind.util.Logger;
 
@@ -15,8 +15,6 @@ public class Camera {
 
     private final static double COLLISION_THRESHOLD = 10.0; // 10m above surface
 
-    private final WorldWindow wwd;
-
     public final Position position = new Position();
 
     @WorldWind.AltitudeMode
@@ -40,15 +38,6 @@ public class Camera {
 
     private final Line forwardRay = new Line();
 
-    public Camera(WorldWindow wwd) {
-        if (wwd == null) {
-            throw new IllegalArgumentException(
-                    Logger.logMessage(Logger.ERROR, "Camera", "constructor", "missingWorldWindow"));
-        }
-
-        this.wwd = wwd;
-    }
-
     public Camera set(double latitude, double longitude, double altitude, @WorldWind.AltitudeMode int altitudeMode,
                       double heading, double tilt, double roll) {
         this.position.set(latitude, longitude, altitude);
@@ -89,6 +78,7 @@ public Camera set(Camera camera) {
         return this;
     }
 
+    @NonNull
     @Override
     public String toString() {
         return "Camera{" +
@@ -102,14 +92,14 @@ public String toString() {
             '}';
     }
 
-    public Matrix4 computeViewingTransform(Matrix4 result) {
+    public Matrix4 computeViewingTransform(Globe globe, double verticalExaggeration, Matrix4 result) {
         if (result == null) {
             throw new IllegalArgumentException(
                     Logger.logMessage(Logger.ERROR, "Camera", "computeViewingTransform", "missingResult"));
         }
 
         // Transform by the local cartesian transform at the camera's position.
-        this.geographicToCartesianTransform(this.position, this.altitudeMode, result);
+        this.geographicToCartesianTransform(globe, verticalExaggeration, this.position, this.altitudeMode, result);
 
         // Transform by the heading, tilt and roll.
         result.multiplyByRotation(0, 0, 1, -this.heading); // rotate clockwise about the Z axis
@@ -122,26 +112,21 @@ public Matrix4 computeViewingTransform(Matrix4 result) {
         return result;
     }
 
-    public LookAt getAsLookAt(LookAt result) {
-        Globe globe = this.wwd.getGlobe();
-
-        this.computeViewingTransform(this.modelview);
+    public LookAt getAsLookAt(Globe globe, double verticalExaggeration, Position terrainPosition, LookAt result) {
+        this.computeViewingTransform(globe, verticalExaggeration, this.modelview);
 
-        // Pick terrain located behind the viewport center point
-        PickedObject terrainPickedObject = wwd.pick(wwd.getViewport().width / 2f, wwd.getViewport().height / 2f).terrainPickedObject();
-        if (terrainPickedObject != null) {
+        if (terrainPosition != null) {
             // Use picked terrain position including approximate rendered altitude
-            this.originPos.set(terrainPickedObject.getTerrainPosition());
+            this.originPos.set(terrainPosition);
             globe.geographicToCartesian(this.originPos.latitude, this.originPos.longitude, this.originPos.altitude, this.originPoint);
-            globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin);
         } else {
             // Center is outside the globe - use point on horizon
             this.modelview.extractEyePoint(this.forwardRay.origin);
             this.modelview.extractForwardVector(this.forwardRay.direction);
             this.forwardRay.pointAt(globe.horizonDistance(this.position.altitude), this.originPoint);
             globe.cartesianToGeographic(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.originPos);
-            globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin);
         }
+        globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin);
 
         this.modelview.multiplyByMatrix(this.origin);
 
@@ -154,10 +139,8 @@ public LookAt getAsLookAt(LookAt result) {
         return result;
     }
 
-    public Camera setFromLookAt(LookAt lookAt) {
-        Globe globe = this.wwd.getGlobe();
-
-        this.lookAtToViewingTransform(lookAt, this.modelview);
+    public Camera setFromLookAt(Globe globe, double verticalExaggeration, LookAt lookAt) {
+        this.lookAtToViewingTransform(globe, verticalExaggeration, lookAt, this.modelview);
         this.modelview.extractEyePoint(this.originPoint);
 
         globe.cartesianToGeographic(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.originPos);
@@ -171,7 +154,7 @@ public Camera setFromLookAt(LookAt lookAt) {
         this.roll = lookAt.roll; // roll passes straight through
 
         // Check if camera altitude is not under the surface
-        double elevation = globe.getElevationAtLocation(this.position.latitude, this.position.longitude) * wwd.getVerticalExaggeration() + COLLISION_THRESHOLD;
+        double elevation = globe.getElevationAtLocation(this.position.latitude, this.position.longitude) * verticalExaggeration + COLLISION_THRESHOLD;
         if(elevation > this.position.altitude) {
             // Set camera altitude above the surface
             this.position.altitude = elevation;
@@ -192,9 +175,9 @@ public Camera setFromLookAt(LookAt lookAt) {
         return this;
     }
 
-    protected Matrix4 lookAtToViewingTransform(LookAt lookAt, Matrix4 result) {
+    protected Matrix4 lookAtToViewingTransform(Globe globe, double verticalExaggeration, LookAt lookAt, Matrix4 result) {
         // Transform by the local cartesian transform at the look-at's position.
-        this.geographicToCartesianTransform(lookAt.position, lookAt.altitudeMode, result);
+        this.geographicToCartesianTransform(globe, verticalExaggeration, lookAt.position, lookAt.altitudeMode, result);
 
         // Transform by the heading and tilt.
         result.multiplyByRotation(0, 0, 1, -lookAt.heading); // rotate clockwise about the Z axis
@@ -210,21 +193,22 @@ protected Matrix4 lookAtToViewingTransform(LookAt lookAt, Matrix4 result) {
         return result;
     }
 
-    protected void geographicToCartesianTransform(Position position, @WorldWind.AltitudeMode int altitudeMode, Matrix4 result) {
+    protected void geographicToCartesianTransform(Globe globe, double verticalExaggeration, Position position,
+                                                  @WorldWind.AltitudeMode int altitudeMode, Matrix4 result) {
         switch (altitudeMode) {
             case WorldWind.ABSOLUTE:
-                this.wwd.getGlobe().geographicToCartesianTransform(
-                    position.latitude, position.longitude, position.altitude * this.wwd.getVerticalExaggeration(), result);
+                globe.geographicToCartesianTransform(
+                    position.latitude, position.longitude, position.altitude, result);
                 break;
             case WorldWind.CLAMP_TO_GROUND:
-                this.wwd.getGlobe().geographicToCartesianTransform(
-                    position.latitude, position.longitude, this.wwd.getGlobe().getElevationAtLocation(
-                            position.latitude, position.longitude) * this.wwd.getVerticalExaggeration(), result);
+                globe.geographicToCartesianTransform(
+                    position.latitude, position.longitude, globe.getElevationAtLocation(
+                            position.latitude, position.longitude) * verticalExaggeration, result);
                 break;
             case WorldWind.RELATIVE_TO_GROUND:
-                this.wwd.getGlobe().geographicToCartesianTransform(
-                    position.latitude, position.longitude, (position.altitude + this.wwd.getGlobe().getElevationAtLocation(
-                            position.latitude, position.longitude)) * this.wwd.getVerticalExaggeration(), result);
+                globe.geographicToCartesianTransform(
+                    position.latitude, position.longitude, (position.altitude + globe.getElevationAtLocation(
+                            position.latitude, position.longitude)) * verticalExaggeration, result);
                 break;
         }
     }
diff --git a/worldwind/src/test/java/gov/nasa/worldwind/geom/FrustumTest.java b/worldwind/src/test/java/gov/nasa/worldwind/geom/FrustumTest.java
index 1f899aafe..8298f1ecb 100644
--- a/worldwind/src/test/java/gov/nasa/worldwind/geom/FrustumTest.java
+++ b/worldwind/src/test/java/gov/nasa/worldwind/geom/FrustumTest.java
@@ -176,102 +176,100 @@ public void testIntersectsSegment() throws Exception {
         assertFalse("outside far", frustum.intersectsSegment(new Vec3(0, 0, 2), new Vec3(0, 0, 1.0000001)));
     }
 
-// NOTE Navigator is now dependent on WorldWindow instance which is dependent on Android Context.
-// Move these tests to androidTest section?
-//    @Test
-//    public void testSetToModelviewProjection() throws Exception {
-//        // The expected test values were obtained via SystemOut on Frustum object
-//        // at a time in the development cycle when the setToModelviewProjection
-//        // was known to be working correctly (via observed runtime behavior).
-//        // This unit test simply tests for changes in the behavior since that time.
-//
-//        // Create a Frustum similar to the way the WorldWindow does it.
-//
-//        // Setup a Camera, looking near Oxnard Airport.
-//        LookAt lookAt = new LookAt().set(34.15, -119.15, 0, WorldWind.ABSOLUTE, 2e4 /*range*/, 0 /*heading*/, 45 /*tilt*/, 0 /*roll*/);
-//        Camera camera = new Camera(wwd);
-//        camera.setFromLookAt(lookAt);
-//
-//        // Compute a perspective projection matrix given the viewport, field of view, and clip distances.
-//        Viewport viewport = new Viewport(0, 0, 100, 100);  // screen coordinates
-//        double nearDistance = camera.position.altitude * 0.75;
-//        double farDistance = globe.horizonDistance(camera.position.altitude) + globe.horizonDistance(160000);
-//        Matrix4 projection = new Matrix4();
-//        projection.setToPerspectiveProjection(viewport.width, viewport.height, 45d /*fovy*/, nearDistance, farDistance);
-//
-//        // Compute a Cartesian viewing matrix using this Camera's properties as a Camera.
-//        Matrix4 modelview = new Matrix4();
-//        camera.computeViewingTransform(modelview);
-//
-//        // Compute the Frustum
-//        Frustum frustum = new Frustum();
-//        frustum.setToModelviewProjection(projection, modelview, viewport);
-//
-//        // Evaluate the results with known values captured on 07/19/2016
-//        //System.out.println(frustumToString(frustum));
-//        Plane bottom = new Plane(0.17635740224291638, 0.9793994030381801, 0.09836094754823524, -2412232.453445458);
-//        Plane left = new Plane(-0.12177864151960982, 0.07203573632653165, 0.9899398038070459, 1737116.8972521012);
-//        Plane right = new Plane(0.7782605589154529, 0.07203573632653174, -0.6237959242640989, 1737116.8972521003);
-//        Plane top = new Plane(0.48012451515292665, -0.8353279303851167, 0.2677829319947119, 5886466.24794966);
-//        Plane near = new Plane(0.8577349603804412, 0.1882384504636923, 0.4783900328269719, 4528686.830908618);
-//        Plane far = new Plane(-0.8577349603804412, -0.1882384504636923, -0.4783900328269719, -2676528.6881595235);
-//
-//        assertEquals("left", left, frustum.left);
-//        assertEquals("right", right, frustum.right);
-//        assertEquals("bottom", bottom, frustum.bottom);
-//        assertEquals("top", top, frustum.top);
-//        assertEquals("near", near, frustum.near);
-//        assertEquals("far", far, frustum.far);
-//        assertEquals("viewport", viewport, frustum.viewport);
-//    }
-//
-//    @Test
-//    public void testSetToModelviewProjection_SubViewport() throws Exception {
-//        // The expected test values were obtained via SystemOut on Frustum object
-//        // at a time in the development cycle when the setToModelviewProjection
-//        // was known to be working correctly (via observed runtime behavior).
-//        // This unit test simply tests for changes in the behavior since that time.
-//
-//        // Create a Frustum similar to the way the WorldWindow does it when picking
-//
-//        // Setup a Camera, looking near Oxnard Airport.
-//        LookAt lookAt = new LookAt().set(34.15, -119.15, 0, WorldWind.ABSOLUTE, 2e4 /*range*/, 0 /*heading*/, 45 /*tilt*/, 0 /*roll*/);
-//        Camera camera = new Camera(wwd);
-//        camera.setFromLookAt(lookAt);
-//
-//        // Compute a perspective projection matrix given the viewport, field of view, and clip distances.
-//        Viewport viewport = new Viewport(0, 0, 100, 100);  // screen coordinates
-//        Viewport pickViewport = new Viewport(49, 49, 3, 3); // 3x3 viewport centered on a pick point
-//        double nearDistance = camera.position.altitude * 0.75;
-//        double farDistance = globe.horizonDistance(camera.position.altitude) + globe.horizonDistance(160000);
-//        Matrix4 projection = new Matrix4();
-//        projection.setToPerspectiveProjection(viewport.width, viewport.height, 45d /*fovy*/, nearDistance, farDistance);
-//
-//        // Compute a Cartesian viewing matrix using this Camera's properties as a Camera.
-//        Matrix4 modelview = new Matrix4();
-//        camera.computeViewingTransform(modelview);
-//
-//        // Compute the Frustum
-//        Frustum frustum = new Frustum();
-//        frustum.setToModelviewProjection(projection, modelview, viewport, pickViewport);
-//
-//        // Evaluate the results with known values captured on 06/03/2016
-//        //System.out.println(frustumToString(frustum));
-//        Plane bottom = new Plane(-0.15728647066358287, 0.9836490211411795, -0.0877243942936819, -4453465.7217097925);
-//        Plane left = new Plane(-0.4799755263103557, 0.001559364875310035, 0.8772804925018466, 37603.54528193692);
-//        Plane right = new Plane(0.5012403287200531, 0.003118408767628064, -0.8653024953109584, 75199.35019616158);
-//        Plane top = new Plane(0.17858448447919384, -0.9788701700756626, 0.09960307243927863, 4565806.392885632);
-//        Plane near = new Plane(0.8577349603809148, 0.18823845046641746, 0.4783900328250505, 4528686.830896157);
-//        Plane far = new Plane(-0.8577349603804465, -0.1882384504638284, -0.4783900328269087, -2676528.6881588553);
-//
-//        assertEquals("left", left, frustum.left);
-//        assertEquals("right", right, frustum.right);
-//        assertEquals("bottom", bottom, frustum.bottom);
-//        assertEquals("top", top, frustum.top);
-//        assertEquals("near", near, frustum.near);
-//        assertEquals("far", far, frustum.far);
-//        assertEquals("viewport", pickViewport, frustum.viewport);
-//    }
+    @Test
+    public void testSetToModelviewProjection() throws Exception {
+        // The expected test values were obtained via SystemOut on Frustum object
+        // at a time in the development cycle when the setToModelviewProjection
+        // was known to be working correctly (via observed runtime behavior).
+        // This unit test simply tests for changes in the behavior since that time.
+
+        // Create a Frustum similar to the way the WorldWindow does it.
+
+        // Setup a Camera, looking near Oxnard Airport.
+        LookAt lookAt = new LookAt().set(34.15, -119.15, 0, WorldWind.ABSOLUTE, 2e4 /*range*/, 0 /*heading*/, 45 /*tilt*/, 0 /*roll*/);
+        Camera camera = new Camera();
+        camera.setFromLookAt(globe, 1.0, lookAt);
+
+        // Compute a perspective projection matrix given the viewport, field of view, and clip distances.
+        Viewport viewport = new Viewport(0, 0, 100, 100);  // screen coordinates
+        double nearDistance = camera.position.altitude * 0.75;
+        double farDistance = globe.horizonDistance(camera.position.altitude) + globe.horizonDistance(160000);
+        Matrix4 projection = new Matrix4();
+        projection.setToPerspectiveProjection(viewport.width, viewport.height, 45d /*fovy*/, nearDistance, farDistance);
+
+        // Compute a Cartesian viewing matrix using this Camera's properties as a Camera.
+        Matrix4 modelview = new Matrix4();
+        camera.computeViewingTransform(globe, 1.0, modelview);
+
+        // Compute the Frustum
+        Frustum frustum = new Frustum();
+        frustum.setToModelviewProjection(projection, modelview, viewport);
+
+        // Evaluate the results with known values captured on 07/19/2016
+        //System.out.println(frustumToString(frustum));
+        Plane bottom = new Plane(0.17635740224291638, 0.9793994030381801, 0.09836094754823524, -2412232.453445458);
+        Plane left = new Plane(-0.12177864151960982, 0.07203573632653165, 0.9899398038070459, 1737116.8972521012);
+        Plane right = new Plane(0.7782605589154529, 0.07203573632653174, -0.6237959242640989, 1737116.8972521003);
+        Plane top = new Plane(0.48012451515292665, -0.8353279303851167, 0.2677829319947119, 5886466.24794966);
+        Plane near = new Plane(0.8577349603804412, 0.1882384504636923, 0.4783900328269719, 4528686.830908618);
+        Plane far = new Plane(-0.8577349603804412, -0.1882384504636923, -0.4783900328269719, -2676528.6881595235);
+
+        assertEquals("left", left, frustum.left);
+        assertEquals("right", right, frustum.right);
+        assertEquals("bottom", bottom, frustum.bottom);
+        assertEquals("top", top, frustum.top);
+        assertEquals("near", near, frustum.near);
+        assertEquals("far", far, frustum.far);
+        assertEquals("viewport", viewport, frustum.viewport);
+    }
+
+    @Test
+    public void testSetToModelviewProjection_SubViewport() throws Exception {
+        // The expected test values were obtained via SystemOut on Frustum object
+        // at a time in the development cycle when the setToModelviewProjection
+        // was known to be working correctly (via observed runtime behavior).
+        // This unit test simply tests for changes in the behavior since that time.
+
+        // Create a Frustum similar to the way the WorldWindow does it when picking
+
+        // Setup a Camera, looking near Oxnard Airport.
+        LookAt lookAt = new LookAt().set(34.15, -119.15, 0, WorldWind.ABSOLUTE, 2e4 /*range*/, 0 /*heading*/, 45 /*tilt*/, 0 /*roll*/);
+        Camera camera = new Camera();
+        camera.setFromLookAt(globe, 1.0, lookAt);
+
+        // Compute a perspective projection matrix given the viewport, field of view, and clip distances.
+        Viewport viewport = new Viewport(0, 0, 100, 100);  // screen coordinates
+        Viewport pickViewport = new Viewport(49, 49, 3, 3); // 3x3 viewport centered on a pick point
+        double nearDistance = camera.position.altitude * 0.75;
+        double farDistance = globe.horizonDistance(camera.position.altitude) + globe.horizonDistance(160000);
+        Matrix4 projection = new Matrix4();
+        projection.setToPerspectiveProjection(viewport.width, viewport.height, 45d /*fovy*/, nearDistance, farDistance);
+
+        // Compute a Cartesian viewing matrix using this Camera's properties as a Camera.
+        Matrix4 modelview = new Matrix4();
+        camera.computeViewingTransform(globe, 1.0, modelview);
+
+        // Compute the Frustum
+        Frustum frustum = new Frustum();
+        frustum.setToModelviewProjection(projection, modelview, viewport, pickViewport);
+
+        // Evaluate the results with known values captured on 06/03/2016
+        //System.out.println(frustumToString(frustum));
+        Plane bottom = new Plane(-0.15728647066358287, 0.9836490211411795, -0.0877243942936819, -4453465.7217097925);
+        Plane left = new Plane(-0.4799755263103557, 0.001559364875310035, 0.8772804925018466, 37603.54528193692);
+        Plane right = new Plane(0.5012403287200531, 0.003118408767628064, -0.8653024953109584, 75199.35019616158);
+        Plane top = new Plane(0.17858448447919384, -0.9788701700756626, 0.09960307243927863, 4565806.392885632);
+        Plane near = new Plane(0.8577349603809148, 0.18823845046641746, 0.4783900328250505, 4528686.830896157);
+        Plane far = new Plane(-0.8577349603804465, -0.1882384504638284, -0.4783900328269087, -2676528.6881588553);
+
+        assertEquals("left", left, frustum.left);
+        assertEquals("right", right, frustum.right);
+        assertEquals("bottom", bottom, frustum.bottom);
+        assertEquals("top", top, frustum.top);
+        assertEquals("near", near, frustum.near);
+        assertEquals("far", far, frustum.far);
+        assertEquals("viewport", pickViewport, frustum.viewport);
+    }
 
     @Test
     public void testIntersectsViewport() throws Exception {