protected void setEyePosition(
      AnimationController animControl,
      View view,
      Position position,
      ViewInputAttributes.ActionAttributes attrib) {

    MoveToPositionAnimator posAnimator =
        (MoveToPositionAnimator) animControl.get(VIEW_ANIM_POSITION);

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

    if (smoothing != 0.0) {

      double elevation =
          ((FlyViewLimits) view.getViewPropertyLimits())
              .limitEyeElevation(position, view.getGlobe());
      if (elevation != position.getElevation()) {
        position = new Position(position, elevation);
      }
      if (posAnimator == null) {
        posAnimator =
            new MoveToPositionAnimator(
                view.getEyePosition(),
                position,
                smoothing,
                OrbitViewPropertyAccessor.createEyePositionAccessor(view));
        animControl.put(VIEW_ANIM_POSITION, posAnimator);
      } else {
        posAnimator.setEnd(position);
        posAnimator.start();
      }
    }
    view.firePropertyChange(AVKey.VIEW, null, view);
  }
  /**
   * Set the roll in a view.
   *
   * @param view View to modify.
   * @param animControl Animator controller for the view.
   * @param roll new roll value.
   * @param attrib action that caused the roll to change.
   */
  protected void setRoll(
      View view,
      AnimationController animControl,
      Angle roll,
      ViewInputAttributes.ActionAttributes attrib) {
    double smoothing = attrib.getSmoothingValue();
    if (!(attrib.isEnableSmoothing() && this.isEnableSmoothing())) smoothing = 0.0;

    AngleAnimator angleAnimator =
        new RotateToAngleAnimator(
            view.getRoll(), roll, smoothing, ViewPropertyAccessor.createRollAccessor(view));
    animControl.put(VIEW_ANIM_ROLL, angleAnimator);
  }
    public boolean inputActionPerformed(
        AbstractViewInputHandler inputHandler,
        KeyEventState keys,
        String target,
        ViewInputAttributes.ActionAttributes viewAction) {
      java.util.List keyList = viewAction.getKeyActions();
      double rollInput = 0;

      for (Object k : keyList) {
        ViewInputAttributes.ActionAttributes.KeyAction keyAction =
            (ViewInputAttributes.ActionAttributes.KeyAction) k;
        if (keys.isKeyDown(keyAction.keyCode)) {
          rollInput += keyAction.sign;
        }
      }

      if (rollInput == 0) {
        return false;
      }

      //noinspection StringEquality
      if (target == GENERATE_EVENTS) {
        ViewInputAttributes.DeviceAttributes deviceAttributes =
            inputHandler.getAttributes().getDeviceAttributes(ViewInputAttributes.DEVICE_KEYBOARD);

        onRoll(rollInput, deviceAttributes, viewAction);
      }
      return true;
    }
  protected void onHorizontalTranslateRel(
      double forwardInput,
      double sideInput,
      double totalForwardInput,
      double totalSideInput,
      ViewInputAttributes.DeviceAttributes deviceAttributes,
      ViewInputAttributes.ActionAttributes actionAttributes) {
    Angle forwardChange;
    Angle sideChange;

    this.stopGoToAnimators();
    if (actionAttributes.getMouseActions() != null) {
      forwardChange =
          Angle.fromDegrees(
              -totalForwardInput * getScaleValueElevation(deviceAttributes, actionAttributes));
      sideChange =
          Angle.fromDegrees(
              totalSideInput * getScaleValueElevation(deviceAttributes, actionAttributes));
    } else {
      forwardChange =
          Angle.fromDegrees(
              forwardInput * speed * getScaleValueElevation(deviceAttributes, actionAttributes));
      sideChange =
          Angle.fromDegrees(
              sideInput * speed * getScaleValueElevation(deviceAttributes, actionAttributes));
    }
    onHorizontalTranslateRel(forwardChange, sideChange, actionAttributes);
  }
  protected void onResetPitch(ViewInputAttributes.ActionAttributes actionAttribs) {

    View view = this.getView();
    if (view == null) // include this test to ensure any derived implementation performs it
    {
      return;
    }
    double smoothing = actionAttribs.getSmoothingValue();
    if (!(actionAttribs.isEnableSmoothing() && this.isEnableSmoothing())) smoothing = 0.0;
    this.gotoAnimControl.put(
        VIEW_ANIM_PITCH,
        new RotateToAngleAnimator(
            view.getPitch(),
            Angle.POS90,
            smoothing,
            ViewPropertyAccessor.createPitchAccessor(view)));
    view.firePropertyChange(AVKey.VIEW, null, view);
  }
 public boolean inputActionPerformed(
     AbstractViewInputHandler inputHandler,
     java.awt.event.KeyEvent event,
     ViewInputAttributes.ActionAttributes viewAction) {
   java.util.List keyList = viewAction.getKeyActions();
   for (Object k : keyList) {
     ViewInputAttributes.ActionAttributes.KeyAction keyAction =
         (ViewInputAttributes.ActionAttributes.KeyAction) k;
     if (event.getKeyCode() == keyAction.keyCode) {
       onResetPitch(viewAction);
       return true;
     }
   }
   return false;
 }
  protected void onMoveTo(
      Position focalPosition,
      ViewInputAttributes.DeviceAttributes deviceAttributes,
      ViewInputAttributes.ActionAttributes actionAttribs) {
    BasicFlyView view = (BasicFlyView) this.getView();
    if (view == null) // include this test to ensure any derived implementation performs it
    {
      return;
    }

    // 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 smoothing = this.getScaleValueElevation(deviceAttributes, actionAttribs);
    if (!actionAttribs.isEnableSmoothing()) smoothing = 0.0;

    Vec4 currentLookAtPt = view.getCenterPoint();
    if (currentLookAtPt == null) {
      currentLookAtPt = view.getGlobe().computePointFromPosition(focalPosition);
    }

    Vec4 currentEyePt = view.getEyePoint();
    double distanceToSurface = currentEyePt.distanceTo3(currentLookAtPt);
    Vec4 lookDirection = currentEyePt.subtract3(currentLookAtPt).normalize3();
    Vec4 newLookAtPt = view.getGlobe().computePointFromPosition(focalPosition);
    Vec4 flyToPoint = newLookAtPt.add3(lookDirection.multiply3(distanceToSurface));

    Position newPosition = view.getGlobe().computePositionFromPoint(flyToPoint);

    ViewUtil.ViewState viewCoords = view.getViewState(newPosition, focalPosition);

    this.stopAnimators();
    this.gotoAnimControl.put(
        VIEW_ANIM_HEADING,
        new RotateToAngleAnimator(
            view.getHeading(),
            viewCoords.getHeading(),
            smoothing,
            ViewPropertyAccessor.createHeadingAccessor(view)));
    this.gotoAnimControl.put(
        VIEW_ANIM_PITCH,
        new RotateToAngleAnimator(
            view.getPitch(),
            viewCoords.getPitch(),
            smoothing,
            ViewPropertyAccessor.createPitchAccessor(view)));

    double elevation =
        ((FlyViewLimits) view.getViewPropertyLimits())
            .limitEyeElevation(newPosition, view.getGlobe());
    if (elevation != newPosition.getElevation()) {
      newPosition = new Position(newPosition, elevation);
    }
    this.gotoAnimControl.put(
        VIEW_ANIM_POSITION,
        new MoveToPositionAnimator(
            view.getEyePosition(),
            newPosition,
            smoothing,
            ViewPropertyAccessor.createEyePositionAccessor(view)));

    view.firePropertyChange(AVKey.VIEW, null, view);
  }
  public FlyViewInputHandler() {
    // Mouse Button Horizontal Translate Events
    // Button 1
    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_HORIZONTAL_TRANSLATE,
            DEFAULT_MOUSE_HORIZONTAL_TRANSLATE_MIN_VALUE,
            DEFAULT_MOUSE_HORIZONTAL_TRANSLATE_MAX_VALUE);
    this.getAttributes()
        .setActionTrigger(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_HORIZONTAL_TRANSLATE,
            ViewInputAttributes.ActionAttributes.ActionTrigger.ON_KEY_DOWN);

    // Mouse Button Rotate Events
    // Button 1 + SHIFT
    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_ROTATE_SHIFT,
            DEFAULT_MOUSE_ROTATE_MIN_VALUE,
            DEFAULT_MOUSE_ROTATE_MAX_VALUE);
    this.getAttributes()
        .setActionTrigger(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_ROTATE_SHIFT,
            ViewInputAttributes.ActionAttributes.ActionTrigger.ON_KEY_DOWN);
    // Button 3
    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_ROTATE,
            DEFAULT_MOUSE_ROTATE_MIN_VALUE,
            DEFAULT_MOUSE_ROTATE_MAX_VALUE);
    this.getAttributes()
        .setActionTrigger(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_ROTATE,
            ViewInputAttributes.ActionAttributes.ActionTrigger.ON_KEY_DOWN);

    // Mouse Vertical Translate
    // Button 2
    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_VERTICAL_TRANSLATE,
            DEFAULT_MOUSE_VERTICAL_TRANSLATE_MIN_VALUE,
            DEFAULT_MOUSE_VERTICAL_TRANSLATE_MAX_VALUE);
    this.getAttributes()
        .setActionTrigger(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_VERTICAL_TRANSLATE,
            ViewInputAttributes.ActionAttributes.ActionTrigger.ON_KEY_DOWN);
    // Button 1 + CTRL
    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_VERTICAL_TRANSLATE_CTRL,
            DEFAULT_MOUSE_VERTICAL_TRANSLATE_MIN_VALUE,
            DEFAULT_MOUSE_VERTICAL_TRANSLATE_MAX_VALUE);
    this.getAttributes()
        .setActionTrigger(
            ViewInputAttributes.DEVICE_MOUSE,
            ViewInputAttributes.VIEW_VERTICAL_TRANSLATE_CTRL,
            ViewInputAttributes.ActionAttributes.ActionTrigger.ON_KEY_DOWN);

    // Arrow keys rotate

    // ----------------------------------Key Roll --------------------------------------------
    RollActionListener rollActionListener = new RollActionListener();
    this.getAttributes()
        .setActionListener(
            ViewInputAttributes.DEVICE_KEYBOARD,
            ViewInputAttributes.VIEW_ROLL_KEYS,
            rollActionListener);

    // Arrow Keys horizontal translate
    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_KEYBOARD,
            ViewInputAttributes.VIEW_HORIZONTAL_TRANS_KEYS,
            DEFAULT_KEY_HORIZONTAL_TRANSLATE_MIN_VALUE,
            DEFAULT_KEY_HORIZONTAL_TRANSLATE_MAX_VALUE);
    this.getAttributes()
        .getActionAttributes(
            ViewInputAttributes.DEVICE_KEYBOARD, ViewInputAttributes.VIEW_HORIZONTAL_TRANS_KEYS)
        .setSmoothingValue(DEFAULT_KEY_TRANSLATE_SMOOTHING_VALUE);

    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_KEYBOARD,
            ViewInputAttributes.VIEW_HORIZONTAL_TRANSLATE_SLOW,
            DEFAULT_KEY_HORIZONTAL_TRANSLATE_MIN_VALUE_SLOW,
            DEFAULT_KEY_HORIZONTAL_TRANSLATE_MAX_VALUE_SLOW);
    /*
    this.getAttributes().setActionTrigger(ViewInputAttributes.DEVICE_KEYBOARD,
        ViewInputAttributes.VIEW_HORIZONTAL_TRANSLATE_SLOW,
        ViewInputAttributes.ActionAttributes.ActionTrigger.ON_KEY_DOWN);
    */

    // +- Keys vertical translate
    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_KEYBOARD,
            ViewInputAttributes.VIEW_VERTICAL_TRANS_KEYS,
            DEFAULT_KEY_VERTICAL_TRANSLATE_MIN_VALUE,
            DEFAULT_KEY_VERTICAL_TRANSLATE_MAX_VALUE);
    // Arrow keys vertical translate
    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_KEYBOARD,
            ViewInputAttributes.VIEW_VERTICAL_TRANS_KEYS_META,
            DEFAULT_KEY_VERTICAL_TRANSLATE_MIN_VALUE,
            DEFAULT_KEY_VERTICAL_TRANSLATE_MAX_VALUE);
    this.getAttributes()
        .setValues(
            ViewInputAttributes.DEVICE_KEYBOARD,
            ViewInputAttributes.VIEW_VERTICAL_TRANS_KEYS_CTRL,
            DEFAULT_KEY_VERTICAL_TRANSLATE_MIN_VALUE,
            DEFAULT_KEY_VERTICAL_TRANSLATE_MAX_VALUE);

    // Mouse Wheel vertical translate
    if (Configuration.isMacOS()) {
      this.getAttributes()
          .setValues(
              ViewInputAttributes.DEVICE_MOUSE_WHEEL,
              ViewInputAttributes.VIEW_VERTICAL_TRANSLATE,
              DEFAULT_MOUSE_WHEEL_VERTICAL_TRANSLATE_VALUE_MIN_OSX,
              DEFAULT_MOUSE_WHEEL_VERTICAL_TRANSLATE_VALUE_MAX_OSX);
    } else {
      this.getAttributes()
          .setValues(
              ViewInputAttributes.DEVICE_MOUSE_WHEEL,
              ViewInputAttributes.VIEW_VERTICAL_TRANSLATE,
              DEFAULT_MOUSE_WHEEL_VERTICAL_TRANSLATE_VALUE_MIN,
              DEFAULT_MOUSE_WHEEL_VERTICAL_TRANSLATE_VALUE_MAX);
    }

    // P Key Reset Pitch
    this.getAttributes()
        .addAction(
            ViewInputAttributes.DEVICE_KEYBOARD,
            ViewInputAttributes.ActionAttributes.NO_MODIFIER,
            ACTION_RESET_PITCH,
            new ViewInputAttributes.ActionAttributes(
                resetPitchEvents,
                ViewInputAttributes.ActionAttributes.ActionTrigger.ON_PRESS,
                0,
                0.1,
                0.1,
                false,
                0.1));
    // Reset Pitch
    ViewInputAttributes.ActionAttributes actionAttrs =
        this.getAttributes()
            .getActionMap(ViewInputAttributes.DEVICE_KEYBOARD)
            .getActionAttributes(ACTION_RESET_PITCH);
    actionAttrs.setActionListener(new ResetPitchActionListener());
  }