All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.view.orbit.OrbitViewInputHandler Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.worldwind.view.orbit;

import gov.nasa.worldwind.*;
import gov.nasa.worldwind.animation.*;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.awt.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.*;
import gov.nasa.worldwind.util.*;
import gov.nasa.worldwind.view.*;

import java.awt.*;
import java.awt.event.*;
import java.util.Date;

/**
 * @author dcollins
 * @version $Id: OrbitViewInputHandler.java 2253 2014-08-22 16:33:46Z dcollins $
 */
public class OrbitViewInputHandler extends BasicViewInputHandler
{
    protected AnimationController gotoAnimControl = new AnimationController();
    protected AnimationController uiAnimControl = new AnimationController();
    protected static final String VIEW_ANIM_HEADING = "ViewAnimHeading";
    protected static final String VIEW_ANIM_PITCH = "ViewAnimPitch";
    protected static final String VIEW_ANIM_ROLL = "ViewAnimRoll";
    protected static final String VIEW_ANIM_HEADING_PITCH = "ViewAnimHeadingPitch";
    protected static final String VIEW_ANIM_POSITION = "ViewAnimPosition";
    protected static final String VIEW_ANIM_CENTER = "ViewAnimCenter";
    protected static final String VIEW_ANIM_ZOOM = "ViewAnimZoom";
    protected static final String VIEW_ANIM_PAN = "ViewAnimPan";
    protected static final String VIEW_ANIM_APP = "ViewAnimApp";
    protected static final String VIEW_ANIM_EYE = "ViewAnimEye";
    protected static final String VIEW_ANIM_STOP = "ViewAnimStop";

    public static final String ORBITVIEW_RESET_ROLL = "gov.nasa.worldwind.ViewResetRoll";


    /** Action handler to reset roll. */
    public class ResetRollActionListener extends ViewInputActionHandler
    {
        @Override
        public boolean inputActionPerformed(AbstractViewInputHandler inputHandler,
            java.awt.event.MouseEvent mouseEvent, ViewInputAttributes.ActionAttributes viewAction)
        {
            onResetRoll(viewAction);
            return true;
        }
    }

    /**
     * Create a new input handler.
     */
    public OrbitViewInputHandler()
    {
        this.initializeInputHandlers();
    }

    /**
     * Initialize input handlers specific to ObitView.
     */
    protected void initializeInputHandlers()
    {
        // OrbitView allows application controllers to set the view's roll, but it does not provide user controls to
        // change the roll. Add an input handler that will reset the roll to zero when the user clicks the mouse so that
        // the user can easily get back to normal roll state.

        // Reset roll on mouse click
        ViewInputAttributes.ActionAttributes.MouseAction[] resetRollMouseEvents =
        {
            new ViewInputAttributes.ActionAttributes.MouseAction(MouseEvent.BUTTON1_DOWN_MASK)
        };

        // Set up the input attributes for reset roll
        this.getAttributes().setMouseActionAttributes(
            ORBITVIEW_RESET_ROLL, // Action to map to mouse button
            0, // Modifiers, none in this case
            ViewInputAttributes.ActionAttributes.ActionTrigger.ON_PRESS, // The event that triggers the action
            resetRollMouseEvents, // Input actions to map to the behavior
            ViewInputAttributes.DEFAULT_KEY_ROLL_MIN_VALUE,
            ViewInputAttributes.DEFAULT_KEY_ROLL_MAX_VALUE,
            false, // Disable smoothing
            0.0); // Smoothing value

        // Add the action listener
        ViewInputAttributes.ActionAttributes actionAttrs =
            this.getAttributes().getActionMap(ViewInputAttributes.DEVICE_MOUSE).getActionAttributes(
                ORBITVIEW_RESET_ROLL);
        actionAttrs.setMouseActionListener(new ResetRollActionListener());
    }

    protected boolean isNonContinous2DGlobe()
    {
        Globe globe = this.getWorldWindow().getModel().getGlobe();
        return globe instanceof Globe2D && !((Globe2D) globe).isContinuous();
    }

  /**
    * observer point for setting animation targets. Can be used in subclasses to do target position and
    * animation type specific processing without having to guess, filter from transient animation states,
    * or do fragile overrides of all our methods that can set/cancel animations - pcm
    */
    protected void setTargetEyePosition(Position targetPosition, AnimationController controller, String actionKey)
    {
        // only here to be an extension point for subclasses
    }

    //**************************************************************//
    //********************  View Change Events  ********************//
    //**************************************************************//

    protected void onMoveTo(Position focalPosition, ViewInputAttributes.DeviceAttributes deviceAttributes,
        ViewInputAttributes.ActionAttributes actionAttribs)
    {
        this.stopAllAnimators();

        View view = this.getView();
        if (view == null) // include this test to ensure any derived implementation performs it
        {
            return;
        }

        if (this.isNonContinous2DGlobe())
        {
            this.onMoveTo2D(focalPosition, deviceAttributes, actionAttribs);
            return;
        }

        if (view instanceof OrbitView)
        {
            // We're treating a speed parameter as smoothing here. A greater speed results in greater smoothing and
            // slower response. Therefore the min speed used at lower altitudes ought to be *greater* than the max
            // speed used at higher altitudes.
            //double[] values = actionAttribs.getValues();
            double smoothing = this.getScaleValueZoom(actionAttribs);
            if (!actionAttribs.isEnableSmoothing())
                smoothing = 0.0;

            OrbitViewCenterAnimator centerAnimator = new OrbitViewCenterAnimator((BasicOrbitView) this.getView(),
                    view.getEyePosition(), focalPosition, smoothing,
                    OrbitViewPropertyAccessor.createCenterPositionAccessor((OrbitView) view), true);
            this.gotoAnimControl.put(VIEW_ANIM_CENTER, centerAnimator);
            setTargetEyePosition(new Position(focalPosition, view.getEyePosition().getAltitude()), gotoAnimControl, VIEW_ANIM_CENTER);
            view.firePropertyChange(AVKey.VIEW, null, view);
        }
    }

    @SuppressWarnings("UnusedParameters")
    protected void onMoveTo2D(Position focalPosition, ViewInputAttributes.DeviceAttributes deviceAttributes,
        ViewInputAttributes.ActionAttributes actionAttribs)
    {
        View view = this.getView();
        if (view == null || !(view instanceof BasicOrbitView))
        {
            return;
        }

        Globe globe = this.getWorldWindow().getModel().getGlobe();
        BasicOrbitView orbitView = (BasicOrbitView) view;
        Matrix modelview = OrbitViewInputSupport.computeTransformMatrix(globe, focalPosition, orbitView.getHeading(),
            orbitView.getPitch(), orbitView.getRoll(), orbitView.getZoom());
        Vec4 eyePoint = modelview.extractEyePoint();

        double smoothing = actionAttribs.isEnableSmoothing() ? this.getScaleValueZoom(actionAttribs) : 0;
        this.gotoAnimControl.put(VIEW_ANIM_EYE, new OrbitViewEyePointAnimator(globe, orbitView, eyePoint, smoothing));
        setTargetEyePosition(focalPosition, gotoAnimControl, VIEW_ANIM_EYE);
        view.firePropertyChange(AVKey.VIEW, null, view);
    }

    protected void onHorizontalTranslateAbs(Angle latitudeChange, Angle longitudeChange,
        ViewInputAttributes.ActionAttributes actionAttribs)
    {
        this.stopGoToAnimators();
        this.stopUserInputAnimators(VIEW_ANIM_HEADING, VIEW_ANIM_PITCH, VIEW_ANIM_ZOOM, VIEW_ANIM_EYE);

        View view = this.getView();
        if (view == null) // include this test to ensure any derived implementation performs it
        {
            return;
        }

        if (latitudeChange.equals(Angle.ZERO) && longitudeChange.equals(Angle.ZERO))
        {
            return;
        }

        if (view instanceof OrbitView)
        {
            double latDegrees = latitudeChange.degrees;
            double lonDegrees = longitudeChange.degrees;

            Position centerPos = ((OrbitView) view).getCenterPosition();
            latDegrees = WWMath.clamp(centerPos.latitude.degrees + latDegrees, -90, 90);
            lonDegrees = Angle.normalizedDegreesLongitude(centerPos.longitude.degrees + lonDegrees);
            centerPos = Position.fromDegrees(latDegrees, lonDegrees, centerPos.elevation);
            this.setCenterPosition((BasicOrbitView) view, uiAnimControl, centerPos, actionAttribs);
        }
    }

    protected void onHorizontalTranslateRel(double forwardInput, double sideInput,
        double totalForwardInput, double totalSideInput,
        ViewInputAttributes.DeviceAttributes deviceAttributes,
        ViewInputAttributes.ActionAttributes actionAttributes)
    {
        if (this.isNonContinous2DGlobe())
        {
            this.onHorizontalTranslate2D(forwardInput, sideInput, totalForwardInput, totalSideInput, deviceAttributes,
                actionAttributes);
            return;
        }

        this.stopGoToAnimators();
        this.stopUserInputAnimators(VIEW_ANIM_HEADING, VIEW_ANIM_PITCH, VIEW_ANIM_ZOOM, VIEW_ANIM_EYE);

        if (actionAttributes.getMouseActions() != null)
        {
            // Normalize the forward and right magnitudes.
            double length = Math.sqrt(forwardInput * forwardInput + sideInput * sideInput);
            if (length > 0.0)
            {
                forwardInput /= length;
                sideInput /= length;
            }

            Point point = constrainToSourceBounds(getMousePoint(), getWorldWindow());
            Point lastPoint = constrainToSourceBounds(getLastMousePoint(), getWorldWindow());
            if (getSelectedPosition() == null)
            {
                // Compute the current selected position if none exists. This happens if the user starts dragging when
                // the cursor is off the globe, then drags the cursor onto the globe.
                setSelectedPosition(computeSelectedPosition());
            }
            else if (computeSelectedPosition() == null)
            {
                // User dragged the cursor off the globe. Clear the selected position to ensure a new one will be
                // computed if the user drags the cursor back to the globe.
                setSelectedPosition(null);
            }
            else if (computeSelectedPointAt(point) == null || computeSelectedPointAt(lastPoint) == null)
            {
                // User selected a position that is won't work for dragging. Probably the selected elevation is above the
                // eye elevation, in which case dragging becomes unpredictable. Clear the selected position to ensure
                // a new one will be computed if the user drags the cursor to a valid position.
                setSelectedPosition(null);
            }

            Vec4 vec = computeSelectedPointAt(point);
            Vec4 lastVec = computeSelectedPointAt(lastPoint);

            // Cursor is on the globe, pan between the two positions.
            if (vec != null && lastVec != null)
            {


                // Compute the change in view location given two screen points and corresponding world vectors.
                LatLon latlon = getChangeInLocation(lastPoint, point, lastVec, vec);
                onHorizontalTranslateAbs(latlon.getLatitude(), latlon.getLongitude(),  actionAttributes);
                return;
            }

            Point movement = ViewUtil.subtract(point, lastPoint);
            forwardInput = movement.y;
            sideInput = -movement.x;
        }

        // Cursor is off the globe, we potentially want to simulate globe dragging.
        // or this is a keyboard event.
        Angle forwardChange = Angle.fromDegrees(
            forwardInput * getScaleValueHorizTransRel(deviceAttributes, actionAttributes));
        Angle sideChange = Angle.fromDegrees(
            sideInput * getScaleValueHorizTransRel(deviceAttributes, actionAttributes));
        onHorizontalTranslateRel(forwardChange, sideChange, actionAttributes);
    }

    protected void onHorizontalTranslateRel(Angle forwardChange, Angle sideChange,
        ViewInputAttributes.ActionAttributes actionAttribs)
    {
        View view = this.getView();
        if (view == null) // include this test to ensure any derived implementation performs it
        {
            return;
        }

        if (forwardChange.equals(Angle.ZERO) && sideChange.equals(Angle.ZERO))
        {
            return;
        }

        if (view instanceof OrbitView)
        {
            double sinHeading = view.getHeading().sin();
            double cosHeading = view.getHeading().cos();
            double latDegrees = cosHeading * forwardChange.degrees - sinHeading * sideChange.degrees;
            double lonDegrees = sinHeading * forwardChange.degrees + cosHeading * sideChange.degrees;

            Position centerPos = ((OrbitView) view).getCenterPosition();
            latDegrees = WWMath.clamp(centerPos.latitude.degrees + latDegrees, -90, 90);
            lonDegrees = Angle.normalizedDegreesLongitude(centerPos.longitude.degrees + lonDegrees);
            centerPos = Position.fromDegrees(latDegrees, lonDegrees, centerPos.elevation);
            this.setCenterPosition((BasicOrbitView) view, this.uiAnimControl, centerPos, actionAttribs);
        }
    }

    @SuppressWarnings("UnusedParameters")
    protected void onHorizontalTranslate2D(double forwardInput, double sideInput,
        double totalForwardInput, double totalSideInput,
        ViewInputAttributes.DeviceAttributes deviceAttributes,
        ViewInputAttributes.ActionAttributes actionAttributes)
    {
        View view = this.getView();
        if (view == null || !(view instanceof BasicOrbitView))
        {
            return;
        }

        this.stopAllAnimators();

        if (actionAttributes.getMouseActions() != null)
        {
            // Compute the model coordinate rays corresponding to the mouse down point and the current mouse point.
            BasicOrbitView orbitView = (BasicOrbitView) this.getView();
            Point p1 = constrainToSourceBounds(this.getMouseDownPoint(), this.getWorldWindow());
            Point p2 = constrainToSourceBounds(this.getMousePoint(), this.getWorldWindow());
            Line ray1 = ViewUtil.computeRayFromScreenPoint(orbitView, p1.x, p1.y, this.mouseDownModelview,
                this.mouseDownProjection, this.mouseDownViewport);
            Line ray2 = ViewUtil.computeRayFromScreenPoint(orbitView, p2.x, p2.y, this.mouseDownModelview,
                this.mouseDownProjection, this.mouseDownViewport);

            // Compute a model coordinate plane passing through the position under the cursor when the mouse button was
            // pressed. Fall back to a plane normal to the globe if the cursor was off the globe.
            Globe globe = this.getWorldWindow().getModel().getGlobe();
            Position pos = this.getSelectedPosition();
            Vec4 point = pos != null ? globe.computePointFromPosition(pos) : new Vec4(0, 0, 0);
            Vec4 normal = globe.computeSurfaceNormalAtPoint(point);
            Plane plane = new Plane(normal.x, normal.y, normal.z, -normal.dot3(point));

            // Intersect the model coordinate plane with the two model coordinate rays. The difference is the
            // translation in model coordinates.
            Vec4 point1 = plane.intersect(ray1);
            Vec4 point2 = plane.intersect(ray2);
            Vec4 translation = point2.subtract3(point1);

            // Apply the translation vector to the eye point.
            Matrix modelview = this.mouseDownModelview.multiply(Matrix.fromTranslation(translation));
            Vec4 eyePoint = modelview.extractEyePoint();
            this.setEyePoint(eyePoint, actionAttributes);
        }
        else
        {
            // Convert the translation vector from a unitless direction to eye coordinates.
            Globe globe = this.getWorldWindow().getModel().getGlobe();
            double degreesPerUnit = this.getScaleValueHorizTransRel(deviceAttributes, actionAttributes);
            double radiansPerUnit = degreesPerUnit * Math.PI / 180.0;
            double metersPerUnit = radiansPerUnit * globe.getRadius();
            Vec4 translation = new Vec4(-sideInput, -forwardInput, 0);
            translation = translation.multiply3(metersPerUnit);

            // Convert the translation vector from eye coordinates to model coordinates in order to match the view's
            // current orientation relative to the model.
            BasicOrbitView orbitView = (BasicOrbitView) this.getView();
            Matrix matrix = ViewUtil.computeTransformMatrix(globe, orbitView.getCenterPosition(),
                orbitView.getHeading(), Angle.ZERO, orbitView.getRoll());
            translation = translation.transformBy3(matrix.getInverse());

            // Apply the translation vector to the eye point.
            Matrix modelview = this.getView().getModelviewMatrix().multiply(Matrix.fromTranslation(translation));
            Vec4 eyePoint = modelview.extractEyePoint();
            this.setEyePoint(eyePoint, actionAttributes);
        }
    }

    @Override
    protected void onResetHeading(ViewInputAttributes.ActionAttributes actionAttribs)
    {
        this.stopAllAnimators();

        View view = this.getView();
        if (view == null) // include this test to ensure any derived implementation performs it
        {
            return;
        }
        this.addHeadingAnimator(view.getHeading(), Angle.ZERO);
    }

    /**
     * Called when user input causes the roll to reset.
     *
     * @param actionAttribs input that caused the change.
     */
    @SuppressWarnings("UnusedParameters")
    protected void onResetRoll(ViewInputAttributes.ActionAttributes actionAttribs)
    {
        View view = this.getView();
        if (view == null) // include this test to ensure any derived implementation performs it
        {
            return;
        }

        if (Angle.ZERO.equals(view.getRoll())) // Don't need to reset if roll is already zero
        {
            return;
        }

        this.addRollAnimator(view.getRoll(), Angle.ZERO);
    }

    @Override
    protected void onResetHeadingPitchRoll(ViewInputAttributes.ActionAttributes actionAttribs)
    {
        this.stopAllAnimators();

        View view = this.getView();
        if (view == null) // include this test to ensure any derived implementation performs it
        {
            return;
        }

        this.addHeadingPitchRollAnimator(view.getHeading(), Angle.ZERO, view.getPitch(), Angle.ZERO, view.getRoll(),
            Angle.ZERO);
    }

    protected void onRotateView(double headingInput, double pitchInput,
        double totalHeadingInput, double totalPitchInput,
        ViewInputAttributes.DeviceAttributes deviceAttributes,
        ViewInputAttributes.ActionAttributes actionAttributes)
    {
        this.stopGoToAnimators();
        this.stopUserInputAnimators(VIEW_ANIM_CENTER, VIEW_ANIM_ZOOM, VIEW_ANIM_EYE);

        if (actionAttributes.getMouseActions() != null)
        {
            // Switch the direction of heading change depending on whether the cursor is above or below
            // the center of the screen.
            if (getWorldWindow() instanceof Component)
            {
                if (getMousePoint().y < ((Component) getWorldWindow()).getHeight() / 2)
                {
                    headingInput = -headingInput;
                }
            }
        }
        else
        {
            double length = Math.sqrt(headingInput * headingInput + pitchInput * pitchInput);
            if (length > 0.0)
            {
                headingInput /= length;
                pitchInput /= length;
            }


        }

        Angle headingChange = Angle.fromDegrees(
            headingInput * getScaleValueRotate(actionAttributes));
        Angle pitchChange = Angle.fromDegrees(
            pitchInput * getScaleValueRotate(actionAttributes));

        onRotateView(headingChange, pitchChange, actionAttributes);
    }

    protected void onRotateView(Angle headingChange, Angle pitchChange,
        ViewInputAttributes.ActionAttributes actionAttribs)
    {
        View view = this.getView();
        if (view == null) // include this test to ensure any derived implementation performs it
        {
            return;
        }

        if (view instanceof BasicOrbitView)
        {
            if (!headingChange.equals(Angle.ZERO))
                this.changeHeading((BasicOrbitView) view, uiAnimControl, headingChange, actionAttribs);

            if (!pitchChange.equals(Angle.ZERO))
                this.changePitch((BasicOrbitView) view, uiAnimControl, pitchChange, actionAttribs);
        }
    }

    protected void onVerticalTranslate(double translateChange, double totalTranslateChange,
        ViewInputAttributes.DeviceAttributes deviceAttributes,
        ViewInputAttributes.ActionAttributes actionAttributes)
    {
        this.stopGoToAnimators();
        this.stopUserInputAnimators(VIEW_ANIM_CENTER, VIEW_ANIM_HEADING, VIEW_ANIM_PITCH, VIEW_ANIM_EYE);

        double zoomChange = translateChange * getScaleValueRotate(actionAttributes);
        onVerticalTranslate(zoomChange, actionAttributes);
    }

    protected void onVerticalTranslate(double translateChange, ViewInputAttributes.ActionAttributes actionAttribs)
    {
        View view = this.getView();
        if (view == null) // include this test to ensure any derived implementation performs it
        {
            return;
        }

        if (translateChange == 0)
        {
            return;
        }
        if (view instanceof BasicOrbitView)
        {
            this.changeZoom((BasicOrbitView) view, uiAnimControl, translateChange, actionAttribs);
        }
    }

    //**************************************************************//
    //********************                    **********************//
    //**************************************************************//

    /**
     * Apply the changes prior to rendering a frame.
     * The method will step animators, applying the results of those steps to the View, then
     * if a focus on terrain is required, it will do that as well.
     *
     **/
    @Override
    public void apply()
    {
        super.apply();

        View view = this.getView();
        if (view == null)
        {
            return;
        }

        if (this.gotoAnimControl.stepAnimators())
        {
            view.firePropertyChange(AVKey.VIEW, null, view);
        }
        else
        {
            this.gotoAnimControl.clear();
        }

        if (this.uiAnimControl.stepAnimators())
        {
            view.firePropertyChange(AVKey.VIEW, null, view);
        }
        else
        {
            this.uiAnimControl.clear();
        }
    }

    //**************************************************************//
    //********************  Property Change Events  ****************//
    //**************************************************************//

    protected void handlePropertyChange(java.beans.PropertyChangeEvent e)
    {
        super.handlePropertyChange(e);

        //noinspection StringEquality
        if (e.getPropertyName() == OrbitView.CENTER_STOPPED)
        {
            this.handleOrbitViewCenterStopped();
        }
    }

    protected void stopAllAnimators()
    {
        // Explicitly stop all animators, then clear the data structure which holds them. If we remove an animator
        // from this data structure without invoking stop(), the animator has no way of knowing it was forcibly stopped.
        // An animator's owner - potentially an object other than this ViewInputHandler - may need to know if an
        // animator has been forcibly stopped in order to react correctly to that event.
        this.uiAnimControl.stopAnimations();
        this.gotoAnimControl.stopAnimations();
        this.uiAnimControl.clear();
        this.gotoAnimControl.clear();

        View view = this.getView();
        if (view == null)
            return;

        if (view instanceof BasicOrbitView)
        {
            ((BasicOrbitView) view).setViewOutOfFocus(true);
        }
    }

    protected void stopGoToAnimators()
    {
        // Explicitly stop all 'go to' animators, then clear the data structure which holds them. If we remove an
        // animator from this data structure without invoking stop(), the animator has no way of knowing it was forcibly
        // stopped. An animator's owner - likely an application object other - may need to know if an animator has been
        // forcibly stopped in order to react correctly to that event.
        this.gotoAnimControl.stopAnimations();
        this.gotoAnimControl.clear();
    }

    protected void stopUserInputAnimators(Object... names)
    {
        for (Object o : names)
        {
            if (this.uiAnimControl.get(o) != null)
            {
                // Explicitly stop the 'ui' animator, then clear it from the data structure which holds it. If we remove
                // an animator from this data structure without invoking stop(), the animator has no way of knowing it
                // was forcibly stopped. Though applications cannot access the 'ui' animator data structure, stopping
                // the animators here is the correct action.
                this.uiAnimControl.get(o).stop();
                this.uiAnimControl.remove(o);
            }
        }
    }

    protected void handleViewStopped()
    {
        this.stopAllAnimators();
        setTargetEyePosition(getView().getEyePosition(), null, VIEW_ANIM_STOP);
    }

    protected void handleOrbitViewCenterStopped()
    {
        // The "center stopped" message instructs components to stop modifying the OrbitView's center position.
        // Therefore we stop any center position animations started by this view controller.
        this.stopUserInputAnimators(VIEW_ANIM_CENTER, VIEW_ANIM_EYE);
        setTargetEyePosition(getView().getEyePosition(), uiAnimControl, VIEW_ANIM_STOP);
    }

    //**************************************************************//
    //********************  View State Change Utilities  ***********//
    //**************************************************************//

    protected void setEyePoint(Vec4 eyePoint, ViewInputAttributes.ActionAttributes attrib)
    {
        Globe globe = this.getWorldWindow().getModel().getGlobe();
        BasicOrbitView view = (BasicOrbitView) this.getView();

        double smoothing = (this.isEnableSmoothing() && attrib.isEnableSmoothing()) ? attrib.getSmoothingValue() : 0;
        if (smoothing == 0)
        {
            OrbitViewEyePointAnimator.setEyePoint(globe, view, eyePoint);
            this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
        }
        else
        {
            this.uiAnimControl.put(VIEW_ANIM_EYE, new OrbitViewEyePointAnimator(globe, view, eyePoint, smoothing));
            this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
        }
    }

    protected void setCenterPosition(BasicOrbitView view,
        AnimationController animControl,
        Position position, ViewInputAttributes.ActionAttributes attrib)
    {
        Position newPosition;
        double smoothing = attrib.getSmoothingValue();
        if (!(attrib.isEnableSmoothing() && this.isEnableSmoothing()))
            smoothing = 0.0;

        if (smoothing == 0)
        {
            if (animControl.get(VIEW_ANIM_CENTER) != null)
                animControl.remove(VIEW_ANIM_CENTER);
            newPosition = view.getOrbitViewLimits().limitCenterPosition(view, position);
            view.setCenterPosition(newPosition);
            view.setViewOutOfFocus(true);
        }
        else
        {
            OrbitViewCenterAnimator centerAnimator = (OrbitViewCenterAnimator) animControl.get(VIEW_ANIM_CENTER);
            Position cur = view.getCenterPosition();

            if (centerAnimator == null || !centerAnimator.hasNext())
            {
                newPosition = computeNewPosition(view, position);
                centerAnimator = new OrbitViewCenterAnimator((BasicOrbitView) this.getView(),
                    cur, newPosition, smoothing,
                    OrbitViewPropertyAccessor.createCenterPositionAccessor(view), true);
                animControl.put(VIEW_ANIM_CENTER, centerAnimator);
            }
            else
            {
                newPosition = new Position(
                    centerAnimator.getEnd().getLatitude().add(
                        position.getLatitude()).subtract(cur.getLatitude()),
                    centerAnimator.getEnd().getLongitude().add(
                        position.getLongitude()).subtract(cur.getLongitude()),
                    centerAnimator.getEnd().getElevation() +
                        position.getElevation() - cur.getElevation());
                newPosition = computeNewPosition(view, newPosition);
                centerAnimator.setEnd(newPosition);
            }

            centerAnimator.start();
        }
        setTargetEyePosition(new Position(newPosition, view.getEyePosition().elevation), animControl, VIEW_ANIM_CENTER);
        view.firePropertyChange(AVKey.VIEW, null, view);
    }

    //protected void setHeading(BasicOrbitView view,
    //    AnimationController animControl,
    //    Angle heading)
    //{
    //    view.computeAndSetViewCenterIfNeeded();
    //    RotateToAngleAnimator angleAnimator = new RotateToAngleAnimator(
    //        view.getHeading(), heading, .95,
    //        ViewPropertyAccessor.createHeadingAccessor(view));
    //    animControl.put(VIEW_ANIM_HEADING, angleAnimator);
    //
    //    view.firePropertyChange(AVKey.VIEW, null, view);
    //}

    protected void changeHeading(BasicOrbitView view,
        AnimationController animControl,
        Angle change, ViewInputAttributes.ActionAttributes attrib)
    {
        view.computeAndSetViewCenterIfNeeded();

        double smoothing = attrib.getSmoothingValue();
        if (!(attrib.isEnableSmoothing() && this.isEnableSmoothing()))
            smoothing = 0.0;

        if (smoothing == 0)
        {
            if (animControl.get(VIEW_ANIM_HEADING) != null)
                animControl.remove(VIEW_ANIM_HEADING);
            Angle newHeading = computeNewHeading(view, view.getHeading().add(change));
            view.setHeading(newHeading);
        }
        else
        {
            RotateToAngleAnimator angleAnimator = (RotateToAngleAnimator)
                animControl.get(VIEW_ANIM_HEADING);

            if (angleAnimator == null || !angleAnimator.hasNext())
            {
                Angle newHeading = computeNewHeading(view, view.getHeading().add(change));
                angleAnimator = new RotateToAngleAnimator(
                    view.getHeading(), newHeading, smoothing,
                    ViewPropertyAccessor.createHeadingAccessor(view));
                animControl.put(VIEW_ANIM_HEADING, angleAnimator);
            }
            else
            {
                Angle newHeading = computeNewHeading(view, angleAnimator.getEnd().add(change));
                angleAnimator.setEnd(newHeading);
            }

            angleAnimator.start();
        }

        view.firePropertyChange(AVKey.VIEW, null, view);
    }

    //protected void setPitch(BasicOrbitView view,
    //    AnimationController animControl,
    //    Angle pitch)
    //{
    //    view.computeAndSetViewCenterIfNeeded();
    //    RotateToAngleAnimator angleAnimator = new RotateToAngleAnimator(
    //        view.getPitch(), pitch, .95,
    //        ViewPropertyAccessor.createPitchAccessor(view));
    //    animControl.put(VIEW_ANIM_PITCH, angleAnimator);
    //    view.firePropertyChange(AVKey.VIEW, null, view);
    //}

    protected void changePitch(BasicOrbitView view,
        AnimationController animControl,
        Angle change, ViewInputAttributes.ActionAttributes attrib)
    {
        view.computeAndSetViewCenterIfNeeded();

        double smoothing = attrib.getSmoothingValue();
        if (!(attrib.isEnableSmoothing() && this.isEnableSmoothing()))
            smoothing = 0.0;

        if (smoothing == 0.0)
        {
            if (animControl.get(VIEW_ANIM_PITCH) != null)
                animControl.remove(VIEW_ANIM_PITCH);
            Angle newPitch = computeNewPitch(view, view.getPitch().add(change));
            view.setPitch(newPitch);
        }
        else
        {
            RotateToAngleAnimator angleAnimator = (RotateToAngleAnimator) animControl.get(VIEW_ANIM_PITCH);

            if (angleAnimator == null || !angleAnimator.hasNext())
            {
                // Create an angle animator which tilts the view to the specified new pitch. If this changes causes the
                // view to collide with the surface, this animator is set to stop. We enable this behavior by using a
                // {@link #CollisionAwarePitchAccessor} angle accessor and setting the animator's stopOnInvalidState
                // property to 'true'.
                Angle newPitch = computeNewPitch(view, view.getPitch().add(change));
                angleAnimator = new RotateToAngleAnimator(
                    view.getPitch(), newPitch, smoothing,
                    new CollisionAwarePitchAccessor(view));
                angleAnimator.setStopOnInvalidState(true);
                animControl.put(VIEW_ANIM_PITCH, angleAnimator);
            }
            else
            {
                Angle newPitch = computeNewPitch(view, angleAnimator.getEnd().add(change));
                angleAnimator.setEnd(newPitch);
            }

            angleAnimator.start();
        }

        view.firePropertyChange(AVKey.VIEW, null, view);
    }

    protected void changeZoom(BasicOrbitView view,
        AnimationController animControl,
        double change, ViewInputAttributes.ActionAttributes attrib)
    {
        view.computeAndSetViewCenterIfNeeded();

        double smoothing = attrib.getSmoothingValue();
        if (!(attrib.isEnableSmoothing() && this.isEnableSmoothing()))
            smoothing = 0.0;

        if (smoothing == 0.0)
        {
            if (animControl.get(VIEW_ANIM_ZOOM) != null)
                animControl.remove(VIEW_ANIM_ZOOM);
            view.setZoom(computeNewZoom(view, view.getZoom(), change));
        }
        else
        {
            double newZoom;
            OrbitViewMoveToZoomAnimator zoomAnimator = (OrbitViewMoveToZoomAnimator) animControl.get(VIEW_ANIM_ZOOM);

            if (zoomAnimator == null || !zoomAnimator.hasNext())
            {
                newZoom = computeNewZoom(view, view.getZoom(), change);
                zoomAnimator = new OrbitViewMoveToZoomAnimator(view, newZoom, smoothing,
                    OrbitViewPropertyAccessor.createZoomAccessor(view), false);
                animControl.put(VIEW_ANIM_ZOOM, zoomAnimator);
            }
            else
            {
                newZoom = computeNewZoom(view, zoomAnimator.getEnd(), change);
                zoomAnimator.setEnd(newZoom);
            }
            setTargetEyePosition(new Position(view.getEyePosition(), newZoom), animControl, VIEW_ANIM_ZOOM);
            zoomAnimator.start();
        }
        view.firePropertyChange(AVKey.VIEW, null, view);
    }

    protected Position computeNewPosition(OrbitView view, Position position)
    {
        Angle newLat = Angle.fromDegrees(WWMath.clamp(position.latitude.degrees, -90, 90));
        Angle newLon = Angle.normalizedLongitude(position.longitude);
        Position newPosition = new Position(newLat, newLon, position.elevation);
        return view.getOrbitViewLimits().limitCenterPosition(view, newPosition);
    }

    protected Angle computeNewHeading(OrbitView view, Angle heading)
    {
        Angle newHeading = BasicOrbitView.normalizedHeading(heading);
        return view.getOrbitViewLimits().limitHeading(view, newHeading);
    }

    protected Angle computeNewPitch(OrbitView view, Angle pitch)
    {
        Angle newPitch = BasicOrbitView.normalizedPitch(pitch);
        return view.getOrbitViewLimits().limitPitch(view, newPitch);
    }

    protected double computeNewZoom(OrbitView view, double curZoom, double change)
    {
        double logCurZoom = curZoom != 0 ? Math.log(curZoom) : 0;
        double newZoom = Math.exp(logCurZoom + change);
        return view.getOrbitViewLimits().limitZoom(view, newZoom);
    }

    //**************************************************************//
    //********************  Input Handler Property Accessors  ******//
    //**************************************************************//

    /**
     * CollisionAwarePitchAccessor implements an {@link gov.nasa.worldwind.util.PropertyAccessor.AngleAccessor}
     * interface onto the pitch property of an {@link gov.nasa.worldwind.view.orbit.OrbitView}. In addition to accessing
     * the pitch property, this implementation is aware of view-surface collisions caused by setting the pitch property.
     * If a call to {@link #setAngle(gov.nasa.worldwind.geom.Angle)} causes the view to collide with the surface, then
     * the call returns false indicating to the caller that the set operation was not entirely successful.
     */
    protected static class CollisionAwarePitchAccessor implements PropertyAccessor.AngleAccessor
    {
        protected OrbitView orbitView;

        /**
         * Creates a new CollisionAwarePitchAccessor with the specified OrbitView, but otherwise does nothing.
         *
         * @param orbitView the OrbitView who's pitch will be accessed.
         *
         * @throws IllegalArgumentException if the orbitView is null.
         */
        public CollisionAwarePitchAccessor(OrbitView orbitView)
        {
            if (orbitView == null)
            {
                String message = Logging.getMessage("nullValue.OrbitViewIsNull");
                Logging.logger().severe(message);
                throw new IllegalArgumentException(message);
            }

            this.orbitView = orbitView;
        }

        /**
         * Returns the pitch property value from this accessor's view.
         *
         * @return the pitch from this accessor's view.
         */
        public Angle getAngle()
        {
            return this.orbitView.getPitch();
        }

        /**
         * Sets the pitch property of this accessor's view to the specified value. If the value is null, setting the
         * view's pitch causes a surface collision, or setting the view's pitch causes an exception, this returns false.
         * Otherwise this returns true.
         *
         * @param value the value to set as this view's pitch property.
         *
         * @return true if the pitch property was successfully set, and false otherwise.
         */
        public boolean setAngle(Angle value)
        {
            if (value == null)
                return false;

            // If the view supports surface collision detection, then clear the view's collision flag prior to
            // making any property changes.
            if (this.orbitView.isDetectCollisions())
                this.orbitView.hadCollisions();

            try
            {
                this.orbitView.setPitch(value);
            }
            catch (Exception e)
            {
                String message = Logging.getMessage("generic.ExceptionWhileChangingView");
                Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
                return false;
            }

            // If the view supports surface collision detection, then return false if the collision flag is set,
            // otherwise return true.
            return !(this.orbitView.isDetectCollisions() && this.orbitView.hadCollisions());
        }
    }

    //**************************************************************//
    //********************  Scaling Utilities  *********************//
    //**************************************************************//

    protected double getScaleValueHorizTransRel(
        ViewInputAttributes.DeviceAttributes deviceAttributes, ViewInputAttributes.ActionAttributes actionAttributes)
    {

        View view = this.getView();
        if (view == null)
        {
            return 0.0;
        }
        if (view instanceof OrbitView)
        {
            double[] range = actionAttributes.getValues();
            // If this is an OrbitView, we use the zoom value to set the scale
            double radius = this.getWorldWindow().getModel().getGlobe().getRadius();
            double t = getScaleValue(range[0], range[1],
                ((OrbitView) view).getZoom(), 3.0 * radius, true);
            return (t);
        } else {
            // Any other view, use the base class scaling method
            return(super.getScaleValueElevation(deviceAttributes, actionAttributes));
        }
    }

    protected double getScaleValueRotate(
        ViewInputAttributes.ActionAttributes actionAttributes)
    {

        View view = this.getView();
        if (view == null)
        {
            return 0.0;
        }
        if (view instanceof OrbitView)
        {
            double[] range = actionAttributes.getValues();
            // If this is an OrbitView, we use the zoom value to set the scale
            double radius = this.getWorldWindow().getModel().getGlobe().getRadius();
            double t = getScaleValue(range[0], range[1],
                ((OrbitView) view).getZoom(), 3.0 * radius, false);
            return (t);
        }
        return(1.0);
    }

    protected double getScaleValueZoom(ViewInputAttributes.ActionAttributes actionAttributes)
    {
        View view = this.getView();
        if (view == null)
        {
            return 0.0;
        }
        if (view instanceof OrbitView)
        {
            double[] range = actionAttributes.getValues();
            // If this is an OrbitView, we use the zoom value to set the scale
            double radius = this.getWorldWindow().getModel().getGlobe().getRadius();
            double t = ((OrbitView) view).getZoom() / (3.0 * radius);
            t = (t < 0 ? 0 : (t > 1 ? 1 : t));
            return range[0] * (1.0 - t) + range[1] * t;
        }
        return(1.0);
    }


    public void addPanToAnimator(Position beginCenterPos, Position endCenterPos,
        Angle beginHeading, Angle endHeading,
        Angle beginPitch, Angle endPitch,
        double beginZoom, double endZoom, long timeToMove, boolean endCenterOnSurface)
    {
        int altitudeMode = endCenterOnSurface ? WorldWind.CLAMP_TO_GROUND : WorldWind.ABSOLUTE;

        OrbitView orbitView = (OrbitView) this.getView();
        FlyToOrbitViewAnimator panAnimator = FlyToOrbitViewAnimator.createFlyToOrbitViewAnimator(orbitView,
            beginCenterPos, endCenterPos, beginHeading, endHeading, beginPitch, endPitch,
            beginZoom, endZoom, timeToMove, altitudeMode);

        this.gotoAnimControl.put(VIEW_ANIM_PAN, panAnimator);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
        setTargetEyePosition(new Position(endCenterPos, endZoom), gotoAnimControl, VIEW_ANIM_PAN);
    }

    public void addPanToAnimator(Position beginCenterPos, Position endCenterPos,
        Angle beginHeading, Angle endHeading,
        Angle beginPitch, Angle endPitch,
        double beginZoom, double endZoom, boolean endCenterOnSurface)
    {
        int altitudeMode = endCenterOnSurface ? WorldWind.CLAMP_TO_GROUND : WorldWind.ABSOLUTE;

        // TODO: scale on mid-altitude?
        final long MIN_LENGTH_MILLIS = 2000;
        final long MAX_LENGTH_MILLIS = 10000;
        long timeToMove = AnimationSupport.getScaledTimeMillisecs(
            beginCenterPos, endCenterPos,
            MIN_LENGTH_MILLIS, MAX_LENGTH_MILLIS);
        OrbitView orbitView = (OrbitView) this.getView();
        FlyToOrbitViewAnimator panAnimator = FlyToOrbitViewAnimator.createFlyToOrbitViewAnimator(orbitView,
            beginCenterPos, endCenterPos, beginHeading, endHeading, beginPitch, endPitch,
            beginZoom, endZoom, timeToMove, altitudeMode);

        this.gotoAnimControl.put(VIEW_ANIM_PAN, panAnimator);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
        setTargetEyePosition(new Position(endCenterPos, endZoom), gotoAnimControl, VIEW_ANIM_PAN);
    }

    public void addPanToAnimator(Position centerPos, Angle heading, Angle pitch, double zoom,
        long timeToMove, boolean endCenterOnSurface)
    {
        OrbitView view = (OrbitView) this.getView();
        addPanToAnimator(view.getCenterPosition(), centerPos,
            view.getHeading(), heading,
            view.getPitch(), pitch, view.getZoom(), zoom, timeToMove, endCenterOnSurface);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
    }

    public void addPanToAnimator(Position centerPos, Angle heading, Angle pitch, double zoom,
        boolean endCenterOnSurface)
    {
        OrbitView view = (OrbitView) this.getView();
        addPanToAnimator(view.getCenterPosition(), centerPos,
            view.getHeading(), heading,
            view.getPitch(), pitch, view.getZoom(), zoom, endCenterOnSurface);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
    }

    public void addPanToAnimator(Position centerPos, Angle heading, Angle pitch, double zoom)
    {
        OrbitView view = (OrbitView) this.getView();
        addPanToAnimator(view.getCenterPosition(), centerPos,
            view.getHeading(), heading,
            view.getPitch(), pitch, view.getZoom(), zoom, false);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
    }

    public void addEyePositionAnimator(long timeToIterate, Position beginPosition, Position endPosition)
    {
        PositionAnimator eyePosAnimator = ViewUtil.createEyePositionAnimator(this.getView(),
            timeToIterate, beginPosition, endPosition);
        this.gotoAnimControl.put(VIEW_ANIM_POSITION, eyePosAnimator);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
        setTargetEyePosition(endPosition, gotoAnimControl, VIEW_ANIM_POSITION);
    }

    public void addHeadingAnimator(Angle begin, Angle end)
    {
        this.gotoAnimControl.remove(VIEW_ANIM_HEADING_PITCH);
        AngleAnimator headingAnimator = ViewUtil.createHeadingAnimator(this.getView(), begin, end);
        this.gotoAnimControl.put(VIEW_ANIM_HEADING, headingAnimator);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
    }

    public void addPitchAnimator(Angle begin, Angle end)
    {
        this.gotoAnimControl.remove(VIEW_ANIM_HEADING_PITCH);
        AngleAnimator pitchAnimator = ViewUtil.createPitchAnimator(this.getView(), begin, end);
        this.gotoAnimControl.put(VIEW_ANIM_PITCH, pitchAnimator);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
    }

    /**
     * Add an animator to animate roll.
     *
     * @param begin starting roll
     * @param end   final roll
     */
    public void addRollAnimator(Angle begin, Angle end)
    {
        this.gotoAnimControl.remove(VIEW_ANIM_ROLL);
        AngleAnimator rollAnimator = ViewUtil.createRollAnimator(this.getView(), begin, end);
        this.gotoAnimControl.put(VIEW_ANIM_ROLL, rollAnimator);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
    }

    /**
     * Add an animator to animate heading, pitch, and roll.
     *
     * @param beginHeading starting heading
     * @param endHeading   final heading
     * @param beginPitch   starting pitch
     * @param endPitch     final pitch
     * @param beginRoll    starting roll
     * @param endRoll      final roll
     */
    public void addHeadingPitchRollAnimator(Angle beginHeading, Angle endHeading, Angle beginPitch, Angle endPitch,
        Angle beginRoll, Angle endRoll)
    {
        this.gotoAnimControl.remove(VIEW_ANIM_PITCH);
        this.gotoAnimControl.remove(VIEW_ANIM_HEADING);
        CompoundAnimator headingPitchAnimator = ViewUtil.createHeadingPitchRollAnimator(this.getView(),
            beginHeading, endHeading, beginPitch, endPitch, beginRoll, endRoll);
        this.gotoAnimControl.put(VIEW_ANIM_HEADING_PITCH, headingPitchAnimator);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
    }

    public void addZoomAnimator(double zoomStart, double zoomEnd)
    {
        final long DEFAULT_LENGTH_MILLIS = 4000;
        DoubleAnimator zoomAnimator = new DoubleAnimator(new ScheduledInterpolator(DEFAULT_LENGTH_MILLIS),
            zoomStart, zoomEnd, OrbitViewPropertyAccessor.createZoomAccessor(((OrbitView) this.getView())));
        this.gotoAnimControl.put(VIEW_ANIM_ZOOM, zoomAnimator);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
    }

    public void addFlyToZoomAnimator(Angle heading, Angle pitch, double zoom)
    {
        if (heading == null || pitch == null)
        {
            String message = Logging.getMessage("nullValue.AngleIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }
        View view = this.getView();
        if (view instanceof OrbitView)
        {
            OrbitView orbitView = (OrbitView) view;
            Angle beginHeading = orbitView.getHeading();
            Angle beginPitch = orbitView.getPitch();
            double beginZoom = orbitView.getZoom();
            final long MIN_LENGTH_MILLIS = 1000;
            final long MAX_LENGTH_MILLIS = 8000;
            long lengthMillis = AnimationSupport.getScaledTimeMillisecs(
                beginZoom, zoom,
                MIN_LENGTH_MILLIS, MAX_LENGTH_MILLIS);
            DoubleAnimator zoomAnimator = new DoubleAnimator(
                new ScheduledInterpolator(lengthMillis), beginZoom, zoom,
                OrbitViewPropertyAccessor.createZoomAccessor(orbitView));
            AngleAnimator headingAnimator = new AngleAnimator(new ScheduledInterpolator(lengthMillis),
                beginHeading, heading, ViewPropertyAccessor.createHeadingAccessor(orbitView));
            AngleAnimator pitchAnimator = new AngleAnimator(new ScheduledInterpolator(lengthMillis),
                beginPitch, pitch, ViewPropertyAccessor.createPitchAccessor(orbitView));

            this.gotoAnimControl.put(VIEW_ANIM_ZOOM, zoomAnimator);
            this.gotoAnimControl.put(VIEW_ANIM_HEADING, headingAnimator);
            this.gotoAnimControl.put(VIEW_ANIM_PITCH, pitchAnimator);
            orbitView.firePropertyChange(AVKey.VIEW, null, orbitView);
        }
    }

    public void addCenterAnimator(Position begin, Position end, boolean smoothed)
    {
        if (begin == null || end == null)
        {
            String message = Logging.getMessage("nullValue.PositionIsNull");
            Logging.logger().fine(message);
            throw new IllegalArgumentException(message);
        }

        View view = this.getView();
        if (view instanceof OrbitView)
        {
            // TODO: length-scaling factory function
            final long DEFAULT_LENGTH_MILLIS = 4000;
            this.addCenterAnimator(begin, end, DEFAULT_LENGTH_MILLIS, smoothed);
        }
    }

    public void addCenterAnimator(Position begin, Position end, long lengthMillis, boolean smoothed)
    {
        if (begin == null || end == null)
        {
            String message = Logging.getMessage("nullValue.PositionIsNull");
            Logging.logger().fine(message);
            throw new IllegalArgumentException(message);
        }

        View view = this.getView();
        if (view instanceof OrbitView)
        {
            OrbitView orbitView = (OrbitView) view;
            Interpolator interpolator;
            if (smoothed)
            {
                interpolator = new SmoothInterpolator(lengthMillis);
            }
            else
            {
                interpolator = new ScheduledInterpolator(lengthMillis);
            }
            Animator centerAnimator = new PositionAnimator(interpolator,
                begin, end, OrbitViewPropertyAccessor.createCenterPositionAccessor(orbitView));
            this.gotoAnimControl.put(VIEW_ANIM_CENTER, centerAnimator);
            setTargetEyePosition(end, gotoAnimControl, VIEW_ANIM_CENTER);
            orbitView.firePropertyChange(AVKey.VIEW, null, orbitView);
        }
    }

    public void goTo(Position lookAtPos, double distance)
    {
        OrbitView view = (OrbitView) this.getView();
        stopAnimators();
        addPanToAnimator(lookAtPos, view.getHeading(), view.getPitch(), distance, true);
        this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());
    }

    public void stopAnimators()
    {
        this.uiAnimControl.stopAnimations();
        this.gotoAnimControl.stopAnimations();
        setTargetEyePosition(getView().getEyePosition(), null, VIEW_ANIM_STOP);
    }

    public boolean isAnimating()
    {
        return (this.uiAnimControl.hasActiveAnimation() || this.gotoAnimControl.hasActiveAnimation());
    }

    public void addAnimator(Animator animator)
    {
        long date = new Date().getTime();
        this.gotoAnimControl.put(VIEW_ANIM_APP+date, animator);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy