Package org.geoforge.worldwind.util.listener

Source Code of org.geoforge.worldwind.util.listener.OurViewControlsSelectListener

/*
* Copyright (C) 2011 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/

package org.geoforge.worldwind.util.listener;

import gov.nasa.worldwind.SceneController;
import gov.nasa.worldwind.WorldWindow;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.event.SelectEvent;
import gov.nasa.worldwind.event.SelectListener;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.layers.ViewControlsLayer;
import gov.nasa.worldwind.render.ScreenAnnotation;
import gov.nasa.worldwind.util.Logging;
import gov.nasa.worldwind.view.orbit.OrbitView;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.Timer;

/**
* Controller for onscreen view controls displayed by {@link ViewControlsLayer}.
*
* @author Patrick Murris
* @version $Id: ViewControlsSelectListener.java 1 2011-07-16 23:22:47Z dcollins $
* @see ViewControlsLayer
*/
public class OurViewControlsSelectListener implements SelectListener
{

   protected static final int DEFAULT_TIMER_DELAY = 50;
   protected WorldWindow wwd;
   protected ViewControlsLayer viewControlsLayer;
   protected ScreenAnnotation pressedControl;
   protected String pressedControlType;
   protected Point lastPickPoint = null;
   protected Timer repeatTimer;
   protected double panStep = 0.6;
   protected double zoomStep = 0.6;
   protected double headingStep = 1;
   protected double pitchStep = 1;
   protected double fovStep = 1.05;
   protected double veStep = 0.1;
   //beg added Amadeus for synchro
   private ArrayList<WorldWindow> wwdSyncro = new ArrayList<WorldWindow>();

   public void addWorldWindow(WorldWindow wwd)
   {
      wwdSyncro.add(wwd);
   }

   //end added Amadeus
   /**
    * Construct a controller for specified <code>WorldWindow</code> and <code>ViewControlsLayer<c/code>.
    * <p/>
    * <code>ViewControlLayer</code>s are not sharable among <code>WorldWindow</code>s. A separate layer and controller
    * must be established for each window that's to have view controls.
    *
    * @param wwd   the <code>WorldWindow</code> the specified layer is associated with.
    * @param layer the layer to control.
    */
   public OurViewControlsSelectListener(WorldWindow wwd, ViewControlsLayer layer)
   {
      if (wwd == null)
      {
         String msg = Logging.getMessage("nullValue.WorldWindow");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }
      if (layer == null)
      {
         String msg = Logging.getMessage("nullValue.LayerIsNull");
         Logging.logger().severe(msg);
         throw new IllegalArgumentException(msg);
      }

      this.wwd = wwd;
      this.viewControlsLayer = layer;

      // Setup repeat timer
      this.repeatTimer = new Timer(DEFAULT_TIMER_DELAY, new ActionListener()
      {

         public void actionPerformed(ActionEvent event)
         {
            if (pressedControl != null)
               updateView(pressedControl, pressedControlType);
         }
      });
      this.repeatTimer.start();
   }

   /**
    * Set the repeat timer delay in milliseconds.
    *
    * @param delay the repeat timer delay in milliseconds.
    *
    * @throws IllegalArgumentException
    */
   public void setRepeatTimerDelay(int delay)
   {
      if (delay <= 0)
      {
         String message = Logging.getMessage("generic.ArgumentOutOfRange", delay);
         Logging.logger().severe(message);
         throw new IllegalArgumentException(message);
      }
      this.repeatTimer.setDelay(delay);
   }

   /**
    * Get the repeat timer delay in milliseconds.
    *
    * @return the repeat timer delay in milliseconds.
    */
   public int getRepeatTimerDelay()
   {
      return this.repeatTimer.getDelay();
   }

   /**
    * Set the panning distance factor. Doubling this value will double the panning speed. Negating it will reverse the
    * panning direction. Default value is .6.
    *
    * @param value the panning distance factor.
    */
   public void setPanIncrement(double value)
   {
      this.panStep = value;
   }

   /**
    * Get the panning distance factor.
    *
    * @return the panning distance factor.
    */
   public double getPanIncrement()
   {
      return this.panStep;
   }

   /**
    * Set the zoom distance factor. Doubling this value will double the zooming speed. Negating it will reverse the
    * zooming direction. Default value is .8.
    *
    * @param value the zooming distance factor.
    */
   public void setZoomIncrement(double value)
   {
      this.zoomStep = value;
   }

   /**
    * Get the zooming distance factor.
    *
    * @return the zooming distance factor.
    */
   public double getZoomIncrement()
   {
      return this.zoomStep;
   }

   /**
    * Set the heading increment value in decimal degrees. Doubling this value will double the heading change speed.
    * Negating it will reverse the heading change direction. Default value is 1 degree.
    *
    * @param value the heading increment value in decimal degrees.
    */
   public void setHeadingIncrement(double value)
   {
      this.headingStep = value;
   }

   /**
    * Get the heading increment value in decimal degrees.
    *
    * @return the heading increment value in decimal degrees.
    */
   public double getHeadingIncrement()
   {
      return this.headingStep;
   }

   /**
    * Set the pitch increment value in decimal degrees. Doubling this value will double the pitch change speed. Must be
    * positive. Default value is 1 degree.
    *
    * @param value the pitch increment value in decimal degrees.
    *
    * @throws IllegalArgumentException
    */
   public void setPitchIncrement(double value)
   {
      if (value < 0)
      {
         String message = Logging.getMessage("generic.ArgumentOutOfRange", value);
         Logging.logger().severe(message);
         throw new IllegalArgumentException(message);
      }
      this.pitchStep = value;
   }

   /**
    * Get the pitch increment value in decimal degrees.
    *
    * @return the pitch increment value in decimal degrees.
    */
   public double getPitchIncrement()
   {
      return this.pitchStep;
   }

   /**
    * Set the field of view increment factor. At each iteration the current field of view will be multiplied or divided
    * by this value. Must be greater then or equal to one. Default value is 1.05.
    *
    * @param value the field of view increment factor.
    *
    * @throws IllegalArgumentException
    */
   public void setFovIncrement(double value)
   {
      if (value < 1)
      {
         String message = Logging.getMessage("generic.ArgumentOutOfRange", value);
         Logging.logger().severe(message);
         throw new IllegalArgumentException(message);
      }
      this.fovStep = value;
   }

   /**
    * Get the field of view increment factor.
    *
    * @return the field of view increment factor.
    */
   public double getFovIncrement()
   {
      return this.fovStep;
   }

   /**
    * Set the vertical exaggeration increment. At each iteration the current vertical exaggeration will be increased or
    * decreased by this amount. Must be greater than or equal to zero. Default value is 0.1.
    *
    * @param value the vertical exaggeration increment.
    *
    * @throws IllegalArgumentException
    */
   public void setVeIncrement(double value)
   {
      if (value < 0)
      {
         String message = Logging.getMessage("generic.ArgumentOutOfRange", value);
         Logging.logger().severe(message);
         throw new IllegalArgumentException(message);
      }
      this.veStep = value;
   }

   /**
    * Get the vertical exaggeration increment.
    *
    * @return the vertical exaggeration increment.
    */
   public double getVeIncrement()
   {
      return this.veStep;
   }

   public void selected(SelectEvent event)
   {
      if (this.wwd == null)
         return;

      if (!(this.wwd.getView() instanceof OrbitView))
         return;

      OrbitView view = (OrbitView) this.wwd.getView();

      if (this.viewControlsLayer.getHighlightedObject() != null)
      {
         this.viewControlsLayer.highlight(null);
         this.wwd.redraw(); // must redraw so the de-highlight can take effect
      }

      if (event.getMouseEvent() != null && event.getMouseEvent().isConsumed())
         return;

      if (event.getTopObject() == null || event.getTopPickedObject().getParentLayer() != this.getParentLayer()
              || !(event.getTopObject() instanceof AVList))
         return;

      String controlType = ((AVList) event.getTopObject()).getStringValue(AVKey.VIEW_OPERATION);
      if (controlType == null)
         return;

      ScreenAnnotation selectedObject = (ScreenAnnotation) event.getTopObject();

      this.lastPickPoint = event.getPickPoint();
      if (event.getEventAction().equals(SelectEvent.ROLLOVER))
      {
         // Highlight on rollover
         this.viewControlsLayer.highlight(selectedObject);
         this.wwd.redraw();
      }
      if (event.getEventAction().equals(SelectEvent.DRAG))
      {
         // just consume drag events
         event.consume();
      }
      else if (event.getEventAction().equals(SelectEvent.HOVER))
      {
         // Highlight on hover
         this.viewControlsLayer.highlight(selectedObject);
         this.wwd.redraw();
      }
      else if (event.getEventAction().equals(SelectEvent.LEFT_PRESS)
              || (event.getEventAction().equals(SelectEvent.DRAG) && controlType.equals(AVKey.VIEW_PAN))
              || (event.getEventAction().equals(SelectEvent.DRAG) && controlType.equals(AVKey.VIEW_LOOK)))
      {
         // Handle left press on controls
         this.pressedControl = selectedObject;
         this.pressedControlType = controlType;
         event.consume();
      }
      else if (event.getEventAction().equals(SelectEvent.LEFT_CLICK)
              || event.getEventAction().equals(SelectEvent.LEFT_DOUBLE_CLICK)
              || event.getEventAction().equals(SelectEvent.DRAG_END))
      {
         // Release pressed control
         this.pressedControl = null;
         resetOrbitView(view);
         view.firePropertyChange(AVKey.VIEW, null, view);
      }

      // Keep pressed control highlighted - overrides rollover non currently pressed controls
      if (this.pressedControl != null)
      {
         this.viewControlsLayer.highlight(this.pressedControl);
         this.wwd.redraw();
      }
   }

   /**
    * Returns this ViewControlsSelectListener's parent layer. The parent layer is associated with picked objects, and
    * is used to determine which SelectEvents thsi ViewControlsSelectListner responds to.
    *
    * @return this ViewControlsSelectListener's parent layer.
    */
   protected Layer getParentLayer()
   {
      return this.viewControlsLayer;
   }

   protected void updateView(ScreenAnnotation control, String controlType)
   {
      if (this.wwd == null)
         return;
      if (!(this.wwd.getView() instanceof OrbitView))
         return;

     
     
      //beg added Amadeus for synchro
      for (int i = 0 ; i < this.wwdSyncro.size(); i++)
      {
         WorldWindow wwdCur = this.wwdSyncro.get(i);
         if (wwdCur == null)
            return;
         if (!(wwdCur.getView() instanceof OrbitView))
            return;
      }

      int updatedSize = this.wwdSyncro.size();

      OrbitView[] views = new OrbitView[updatedSize];

      for (int i = 0; i < updatedSize; i++)
      {
         views[i] = (OrbitView) this.wwdSyncro.get(i).getView();
         views[i].stopAnimations();
         views[i].stopMovement();
      }


      //end Amadeus

      OrbitView view = (OrbitView) this.wwd.getView();
      view.stopAnimations();
      view.stopMovement();

      if (controlType.equals(AVKey.VIEW_PAN))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            resetOrbitView(views[i]);
            // Go some distance in the control mouse direction
            Angle heading = computePanHeading(views[i], control);
            Angle distance = computePanAmount(this.wwd.getModel().getGlobe(), views[i], control, panStep);
            LatLon newViewCenter = LatLon.greatCircleEndPosition(views[i].getCenterPosition(),
                    heading, distance);
            // Turn around if passing by a pole - TODO: better handling of the pole crossing situation
            if (this.isPathCrossingAPole(newViewCenter, views[i].getCenterPosition()))
               views[i].setHeading(Angle.POS180.subtract(views[i].getHeading()));
            // Set new center pos
            views[i].setCenterPosition(new Position(newViewCenter, views[i].getCenterPosition().getElevation()));
         }
         //end Amadeus


         resetOrbitView(view);
         // Go some distance in the control mouse direction
         Angle heading = computePanHeading(view, control);
         Angle distance = computePanAmount(this.wwd.getModel().getGlobe(), view, control, panStep);
         LatLon newViewCenter = LatLon.greatCircleEndPosition(view.getCenterPosition(),
                 heading, distance);
         // Turn around if passing by a pole - TODO: better handling of the pole crossing situation
         if (this.isPathCrossingAPole(newViewCenter, view.getCenterPosition()))
            view.setHeading(Angle.POS180.subtract(view.getHeading()));
         // Set new center pos
         view.setCenterPosition(new Position(newViewCenter, view.getCenterPosition().getElevation()));
      }
      else if (controlType.equals(AVKey.VIEW_LOOK))
      {
         setupFirstPersonView(view);
         Angle heading = computeLookHeading(view, control, headingStep);
         Angle pitch = computeLookPitch(view, control, pitchStep);
         // Check whether the view will still point at terrain
         Vec4 surfacePoint = computeSurfacePoint(view, heading, pitch);
         if (surfacePoint != null)
         {
            // Change view state
            final Position eyePos = view.getEyePosition();// Save current eye position
            view.setHeading(heading);
            view.setPitch(pitch);
            view.setZoom(0);
            view.setCenterPosition(eyePos); // Set center at the eye position
         }
      }
      else if (controlType.equals(AVKey.VIEW_ZOOM_IN))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            resetOrbitView(views[i]);
            views[i].setZoom(computeNewZoom(views[i], -zoomStep));
         }
         //end Amadeus

         resetOrbitView(view);
         view.setZoom(computeNewZoom(view, -zoomStep));
      }
      else if (controlType.equals(AVKey.VIEW_ZOOM_OUT))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            resetOrbitView(views[i]);
            views[i].setZoom(computeNewZoom(views[i], zoomStep));
         }
         //end Amadeus

         resetOrbitView(view);
         view.setZoom(computeNewZoom(view, zoomStep));
      }
      else if (controlType.equals(AVKey.VIEW_HEADING_LEFT))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            resetOrbitView(views[i]);
            views[i].setHeading(views[i].getHeading().addDegrees(headingStep));
         }
         //end Amadeus

         resetOrbitView(view);
         view.setHeading(view.getHeading().addDegrees(headingStep));
      }
      else if (controlType.equals(AVKey.VIEW_HEADING_RIGHT))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            resetOrbitView(views[i]);
            views[i].setHeading(views[i].getHeading().addDegrees(-headingStep));
         }
         //end Amadeus

         resetOrbitView(view);
         view.setHeading(view.getHeading().addDegrees(-headingStep));
      }
      else if (controlType.equals(AVKey.VIEW_PITCH_UP))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            resetOrbitView(views[i]);
            if (view.getPitch().degrees >= pitchStep)
               views[i].setPitch(views[i].getPitch().addDegrees(-pitchStep));
         }
         //end Amadeus
         resetOrbitView(view);
         if (view.getPitch().degrees >= pitchStep)
            view.setPitch(view.getPitch().addDegrees(-pitchStep));
      }
      else if (controlType.equals(AVKey.VIEW_PITCH_DOWN))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            resetOrbitView(views[i]);
            if (views[i].getPitch().degrees <= 90 - pitchStep)
               views[i].setPitch(views[i].getPitch().addDegrees(pitchStep));
         }
         //end Amadeus

         resetOrbitView(view);
         if (view.getPitch().degrees <= 90 - pitchStep)
            view.setPitch(view.getPitch().addDegrees(pitchStep));
      }
      else if (controlType.equals(AVKey.VIEW_FOV_NARROW))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            if (views[i].getFieldOfView().degrees / fovStep >= 4)
               views[i].setFieldOfView(views[i].getFieldOfView().divide(fovStep));
         }
         //end Amadeus

         if (view.getFieldOfView().degrees / fovStep >= 4)
            view.setFieldOfView(view.getFieldOfView().divide(fovStep));
      }
      else if (controlType.equals(AVKey.VIEW_FOV_WIDE))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            if (views[i].getFieldOfView().degrees * fovStep < 120)
               views[i].setFieldOfView(views[i].getFieldOfView().multiply(fovStep));
         }
         //end Amadeus

         if (view.getFieldOfView().degrees * fovStep < 120)
            view.setFieldOfView(view.getFieldOfView().multiply(fovStep));
      }
      else if (controlType.equals(AVKey.VERTICAL_EXAGGERATION_UP))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            SceneController sc = this.wwdSyncro.get(i).getSceneController();
            sc.setVerticalExaggeration(sc.getVerticalExaggeration() + this.veStep);
         }
         //end Amadeus
         SceneController sc = this.wwd.getSceneController();
         sc.setVerticalExaggeration(sc.getVerticalExaggeration() + this.veStep);
      }
      else if (controlType.equals(AVKey.VERTICAL_EXAGGERATION_DOWN))
      {
         //beg added Amadeus for synchro
         for (int i = 0; i < updatedSize; i++)
         {
            SceneController sc = this.wwdSyncro.get(i).getSceneController();
            sc.setVerticalExaggeration(Math.max(1d, sc.getVerticalExaggeration() - this.veStep));
         }
         //end Amadeus

         SceneController sc = this.wwd.getSceneController();
         sc.setVerticalExaggeration(Math.max(1d, sc.getVerticalExaggeration() - this.veStep));
      }
      view.firePropertyChange(AVKey.VIEW, null, view);
   }

   protected boolean isPathCrossingAPole(LatLon p1, LatLon p2)
   {
      return Math.abs(p1.getLongitude().degrees - p2.getLongitude().degrees) > 20
              && Math.abs(p1.getLatitude().degrees - 90 * Math.signum(p1.getLatitude().degrees)) < 10;
   }

   protected double computeNewZoom(OrbitView view, double amount)
   {
      double coeff = 0.05;
      double change = coeff * amount;
      double logZoom = view.getZoom() != 0 ? Math.log(view.getZoom()) : 0;
      // Zoom changes are treated as logarithmic values. This accomplishes two things:
      // 1) Zooming is slow near the globe, and fast at great distances.
      // 2) Zooming in then immediately zooming out returns the viewer to the same zoom value.
      return Math.exp(logZoom + change);
   }

   protected Angle computePanHeading(OrbitView view, ScreenAnnotation control)
   {
      // Compute last pick point 'heading' relative to pan control center
      double size = control.getAttributes().getSize().width * control.getAttributes().getScale();
      Vec4 center = new Vec4(control.getScreenPoint().x, control.getScreenPoint().y + size / 2, 0);
      double px = lastPickPoint.x - center.x;
      double py = view.getViewport().getHeight() - lastPickPoint.y - center.y;
      Angle heading = view.getHeading().add(Angle.fromRadians(Math.atan2(px, py)));
      heading = heading.degrees >= 0 ? heading : heading.addDegrees(360);
      return heading;
   }

   protected Angle computePanAmount(Globe globe, OrbitView view, ScreenAnnotation control, double panStep)
   {
      // Compute last pick point distance relative to pan control center
      double size = control.getAttributes().getSize().width * control.getAttributes().getScale();
      Vec4 center = new Vec4(control.getScreenPoint().x, control.getScreenPoint().y + size / 2, 0);
      double px = lastPickPoint.x - center.x;
      double py = view.getViewport().getHeight() - lastPickPoint.y - center.y;
      double pickDistance = Math.sqrt(px * px + py * py);
      double pickDistanceFactor = Math.min(pickDistance / 10, 5);

      // Compute globe angular distance depending on eye altitude
      Position eyePos = view.getEyePosition();
      double radius = globe.getRadiusAt(eyePos);
      double minValue = 0.5 * (180.0 / (Math.PI * radius)); // Minimum change ~0.5 meters
      double maxValue = 1.0; // Maximum change ~1 degree

      // Compute an interpolated value between minValue and maxValue, using (eye altitude)/(globe radius) as
      // the interpolant. Interpolation is performed on an exponential curve, to keep the value from
      // increasing too quickly as eye altitude increases.
      double a = eyePos.getElevation() / radius;
      a = (a < 0 ? 0 : (a > 1 ? 1 : a));
      double expBase = 2.0; // Exponential curve parameter.
      double value = minValue + (maxValue - minValue) * ((Math.pow(expBase, a) - 1.0) / (expBase - 1.0));

      return Angle.fromDegrees(value * pickDistanceFactor * panStep);
   }

   protected Angle computeLookHeading(OrbitView view, ScreenAnnotation control, double headingStep)
   {
      // Compute last pick point 'heading' relative to look control center on x
      double size = control.getAttributes().getSize().width * control.getAttributes().getScale();
      Vec4 center = new Vec4(control.getScreenPoint().x, control.getScreenPoint().y + size / 2, 0);
      double px = lastPickPoint.x - center.x;
      double pickDistanceFactor = Math.min(Math.abs(px) / 3000, 5) * Math.signum(px);
      // New heading
      Angle heading = view.getHeading().add(Angle.fromRadians(headingStep * pickDistanceFactor));
      heading = heading.degrees >= 0 ? heading : heading.addDegrees(360);
      return heading;
   }

   protected Angle computeLookPitch(OrbitView view, ScreenAnnotation control, double pitchStep)
   {
      // Compute last pick point 'pitch' relative to look control center on y
      double size = control.getAttributes().getSize().width * control.getAttributes().getScale();
      Vec4 center = new Vec4(control.getScreenPoint().x, control.getScreenPoint().y + size / 2, 0);
      double py = view.getViewport().getHeight() - lastPickPoint.y - center.y;
      double pickDistanceFactor = Math.min(Math.abs(py) / 3000, 5) * Math.signum(py);
      // New pitch
      Angle pitch = view.getPitch().add(Angle.fromRadians(pitchStep * pickDistanceFactor));
      pitch = pitch.degrees >= 0 ? (pitch.degrees <= 90 ? pitch : Angle.fromDegrees(90)) : Angle.ZERO;
      return pitch;
   }

   /**
    * Reset the view to an orbit view state if in first person mode (zoom = 0)
    *
    * @param view the orbit view to reset
    */
   protected void resetOrbitView(OrbitView view)
   {
      if (view.getZoom() > 0)   // already in orbit view mode
         return;

      // Find out where on the terrain the eye is looking at in the viewport center
      // TODO: if no terrain is found in the viewport center, iterate toward viewport bottom until it is found
      Vec4 centerPoint = computeSurfacePoint(view, view.getHeading(), view.getPitch());
      // Reset the orbit view center point heading, pitch and zoom
      if (centerPoint != null)
      {
         Vec4 eyePoint = view.getEyePoint();
         // Center pos on terrain surface
         Position centerPosition = wwd.getModel().getGlobe().computePositionFromPoint(centerPoint);
         // Compute pitch and heading relative to center position
         Vec4 normal = wwd.getModel().getGlobe().computeSurfaceNormalAtLocation(centerPosition.getLatitude(),
                 centerPosition.getLongitude());
         Vec4 north = wwd.getModel().getGlobe().computeNorthPointingTangentAtLocation(centerPosition.getLatitude(),
                 centerPosition.getLongitude());
         // Pitch
         view.setPitch(Angle.POS180.subtract(view.getForwardVector().angleBetween3(normal)));
         // Heading
         Vec4 perpendicular = view.getForwardVector().perpendicularTo3(normal);
         Angle heading = perpendicular.angleBetween3(north);
         double direction = Math.signum(-normal.cross3(north).dot3(perpendicular));
         view.setHeading(heading.multiply(direction));
         // Zoom
         view.setZoom(eyePoint.distanceTo3(centerPoint));
         // Center pos
         view.setCenterPosition(centerPosition);
      }
   }

   /**
    * Setup the view to a first person mode (zoom = 0)
    *
    * @param view the orbit view to set into a first person view.
    */
   protected void setupFirstPersonView(OrbitView view)
   {
      if (view.getZoom() == 0// already in first person mode
         return;

      Vec4 eyePoint = view.getEyePoint();
      // Center pos at eye pos
      Position centerPosition = wwd.getModel().getGlobe().computePositionFromPoint(eyePoint);
      // Compute pitch and heading relative to center position
      Vec4 normal = wwd.getModel().getGlobe().computeSurfaceNormalAtLocation(centerPosition.getLatitude(),
              centerPosition.getLongitude());
      Vec4 north = wwd.getModel().getGlobe().computeNorthPointingTangentAtLocation(centerPosition.getLatitude(),
              centerPosition.getLongitude());
      // Pitch
      view.setPitch(Angle.POS180.subtract(view.getForwardVector().angleBetween3(normal)));
      // Heading
      Vec4 perpendicular = view.getForwardVector().perpendicularTo3(normal);
      Angle heading = perpendicular.angleBetween3(north);
      double direction = Math.signum(-normal.cross3(north).dot3(perpendicular));
      view.setHeading(heading.multiply(direction));
      // Zoom
      view.setZoom(0);
      // Center pos
      view.setCenterPosition(centerPosition);
   }

   /**
    * Find out where on the terrain surface the eye would be looking at with the given heading and pitch angles.
    *
    * @param view    the orbit view
    * @param heading heading direction clockwise from north.
    * @param pitch   view pitch angle from the surface normal at the center point.
    *
    * @return the terrain surface point the view would be looking at in the viewport center.
    */
   protected Vec4 computeSurfacePoint(OrbitView view, Angle heading, Angle pitch)
   {
      Globe globe = wwd.getModel().getGlobe();
      // Compute transform to be applied to north pointing Y so that it would point in the view direction
      // Move coordinate system to view center point
      Matrix transform = globe.computeSurfaceOrientationAtPosition(view.getCenterPosition());
      // Rotate so that the north pointing axes Y will point in the look at direction
      transform = transform.multiply(Matrix.fromRotationZ(heading.multiply(-1)));
      transform = transform.multiply(Matrix.fromRotationX(Angle.NEG90.add(pitch)));
      // Compute forward vector
      Vec4 forward = Vec4.UNIT_Y.transformBy4(transform);
      // Return intersection with terrain
      Intersection[] intersections = wwd.getSceneController().getTerrain().intersect(
              new Line(view.getEyePoint(), forward));
      return (intersections != null && intersections.length != 0) ? intersections[0].getIntersectionPoint() : null;
   }
}
TOP

Related Classes of org.geoforge.worldwind.util.listener.OurViewControlsSelectListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.