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 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 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;
 }
 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 void transformBackgroundImageCoordsToAnnotationCoords(
      DrawContext dc, int width, int height, WWTexture texture) {
    GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

    // Scale background image coordinates to fit the Annotation's dimensions.
    java.awt.Dimension size = this.getImageSize(dc);
    if (size != null) {
      gl.glScaled(size.getWidth() / (double) width, size.getHeight() / (double) height, 1d);
    }

    super.transformBackgroundImageCoordsToAnnotationCoords(dc, width, height, texture);
  }
  protected void applyBackgroundTextureState(
      DrawContext dc, int width, int height, double opacity, WWTexture texture) {
    super.applyBackgroundTextureState(dc, width, height, opacity, texture);

    // Setup the texture filters to correspond to the smoothing and mipmap settings.
    int minFilter =
        this.isEnableSmoothing()
            ? (this.isUseMipmaps() ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR)
            : GL.GL_NEAREST;
    int magFilter = this.isEnableSmoothing() ? GL.GL_LINEAR : GL.GL_NEAREST;

    GL gl = dc.getGL();
    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, minFilter);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, magFilter);
  }
 public void drawContent(
     DrawContext dc, int width, int height, double opacity, Position pickPosition) {
   super.drawContent(dc, width, height, opacity, pickPosition);
   this.drawToolTip(dc);
 }
  // Set controls positions according to layout and viewport dimension
  protected void updatePositions(DrawContext dc) {
    boolean horizontalLayout = this.layout.equals(AVKey.HORIZONTAL);

    // horizontal layout: pan button + look button beside 2 rows of 4 buttons
    int width =
        (showPanControls ? panSize : 0)
            + (showLookControls ? panSize : 0)
            + (showZoomControls ? buttonSize : 0)
            + (showHeadingControls ? buttonSize : 0)
            + (showPitchControls ? buttonSize : 0)
            + (showFovControls ? buttonSize : 0)
            + (showVeControls ? buttonSize : 0);
    int height = Math.max(panSize, buttonSize * 2);
    width = (int) (width * scale);
    height = (int) (height * scale);
    int xOffset = 0;
    int yOffset = (int) (buttonSize * scale);

    if (!horizontalLayout) {
      // vertical layout: pan button above look button above 4 rows of 2 buttons
      int temp = height;
      //noinspection SuspiciousNameCombination
      height = width;
      width = temp;
      xOffset = (int) (buttonSize * scale);
      yOffset = 0;
    }

    int halfPanSize = (int) (panSize * scale / 2);
    int halfButtonSize = (int) (buttonSize * scale / 2);

    Rectangle controlsRectangle = new Rectangle(width, height);
    Point locationSW = computeLocation(dc.getView().getViewport(), controlsRectangle);

    // Layout start point
    int x = locationSW.x;
    int y = horizontalLayout ? locationSW.y : locationSW.y + height;

    if (this.showPanControls) {
      if (!horizontalLayout) y -= (int) (panSize * scale);
      controlPan.setScreenPoint(new Point(x + halfPanSize, y));
      if (horizontalLayout) x += (int) (panSize * scale);
    }
    if (this.showLookControls) {
      if (!horizontalLayout) y -= (int) (panSize * scale);
      controlLook.setScreenPoint(new Point(x + halfPanSize, y));
      if (horizontalLayout) x += (int) (panSize * scale);
    }
    if (this.showZoomControls) {
      if (!horizontalLayout) y -= (int) (buttonSize * scale);
      controlZoomIn.setScreenPoint(new Point(x + halfButtonSize + xOffset, y + yOffset));
      controlZoomOut.setScreenPoint(new Point(x + halfButtonSize, y));
      if (horizontalLayout) x += (int) (buttonSize * scale);
    }
    if (this.showHeadingControls) {
      if (!horizontalLayout) y -= (int) (buttonSize * scale);
      controlHeadingLeft.setScreenPoint(new Point(x + halfButtonSize + xOffset, y + yOffset));
      controlHeadingRight.setScreenPoint(new Point(x + halfButtonSize, y));
      if (horizontalLayout) x += (int) (buttonSize * scale);
    }
    if (this.showPitchControls) {
      if (!horizontalLayout) y -= (int) (buttonSize * scale);
      controlPitchUp.setScreenPoint(new Point(x + halfButtonSize + xOffset, y + yOffset));
      controlPitchDown.setScreenPoint(new Point(x + halfButtonSize, y));
      if (horizontalLayout) x += (int) (buttonSize * scale);
    }
    if (this.showFovControls) {
      if (!horizontalLayout) y -= (int) (buttonSize * scale);
      controlFovNarrow.setScreenPoint(new Point(x + halfButtonSize + xOffset, y + yOffset));
      controlFovWide.setScreenPoint(new Point(x + halfButtonSize, y));
      if (horizontalLayout) x += (int) (buttonSize * scale);
    }
    if (this.showVeControls) {
      if (!horizontalLayout) y -= (int) (buttonSize * scale);
      controlVeUp.setScreenPoint(new Point(x + halfButtonSize + xOffset, y + yOffset));
      controlVeDown.setScreenPoint(new Point(x + halfButtonSize, y));
      if (horizontalLayout) x += (int) (buttonSize * scale);
    }
    if (this.showTrash) {
      if (!horizontalLayout) y -= (int) (buttonSize * scale);
      this.trashControl.setScreenPoint(new Point(x + halfButtonSize + 5, y));
    }
    //        if (this.showMenu) {
    //        	if (!horizontalLayout)
    //        		y -= (int) (buttonSize * scale);
    //        	this.menuControl.setScreenPoint(new Point(x + halfButtonSize + 15 +
    // this.myButtonWidth, y));
    //        }

    this.referenceViewport = dc.getView().getViewport();
  }
  protected void initialize(DrawContext dc) {
    if (this.initialized) return;

    // Setup user interface - common default attributes
    AnnotationAttributes ca = new AnnotationAttributes();
    ca.setAdjustWidthToText(AVKey.SIZE_FIXED);
    ca.setInsets(new Insets(0, 0, 0, 0));
    ca.setBorderWidth(0);
    ca.setCornerRadius(0);
    ca.setSize(new Dimension(buttonSize, buttonSize));
    ca.setBackgroundColor(new Color(0, 0, 0, 0));
    ca.setImageOpacity(.5);
    ca.setScale(scale);

    final String NOTEXT = "";
    final Point ORIGIN = new Point(0, 0);
    if (this.showPanControls) {
      // Pan
      controlPan = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlPan.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_PAN);
      controlPan.getAttributes().setImageSource(getImageSource(AVKey.VIEW_PAN));
      controlPan.getAttributes().setSize(new Dimension(panSize, panSize));
      this.addRenderable(controlPan);
    }
    if (this.showLookControls) {
      // Look
      controlLook = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlLook.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_LOOK);
      controlLook.getAttributes().setImageSource(getImageSource(AVKey.VIEW_LOOK));
      controlLook.getAttributes().setSize(new Dimension(panSize, panSize));
      this.addRenderable(controlLook);
    }
    if (this.showZoomControls) {
      // Zoom
      controlZoomIn = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlZoomIn.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_ZOOM_IN);
      controlZoomIn.getAttributes().setImageSource(getImageSource(AVKey.VIEW_ZOOM_IN));
      this.addRenderable(controlZoomIn);
      controlZoomOut = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlZoomOut.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_ZOOM_OUT);
      controlZoomOut.getAttributes().setImageSource(getImageSource(AVKey.VIEW_ZOOM_OUT));
      this.addRenderable(controlZoomOut);
    }
    if (this.showHeadingControls) {
      // Heading
      controlHeadingLeft = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlHeadingLeft.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_HEADING_LEFT);
      controlHeadingLeft.getAttributes().setImageSource(getImageSource(AVKey.VIEW_HEADING_LEFT));
      this.addRenderable(controlHeadingLeft);
      controlHeadingRight = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlHeadingRight.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_HEADING_RIGHT);
      controlHeadingRight.getAttributes().setImageSource(getImageSource(AVKey.VIEW_HEADING_RIGHT));
      this.addRenderable(controlHeadingRight);
    }
    if (this.showPitchControls) {
      // Pitch
      controlPitchUp = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlPitchUp.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_PITCH_UP);
      controlPitchUp.getAttributes().setImageSource(getImageSource(AVKey.VIEW_PITCH_UP));
      this.addRenderable(controlPitchUp);
      controlPitchDown = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlPitchDown.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_PITCH_DOWN);
      controlPitchDown.getAttributes().setImageSource(getImageSource(AVKey.VIEW_PITCH_DOWN));
      this.addRenderable(controlPitchDown);
    }
    if (this.showFovControls) {
      // Field of view FOV
      controlFovNarrow = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlFovNarrow.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_FOV_NARROW);
      controlFovNarrow.getAttributes().setImageSource(getImageSource(AVKey.VIEW_FOV_NARROW));
      this.addRenderable(controlFovNarrow);
      controlFovWide = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlFovWide.setValue(AVKey.VIEW_OPERATION, AVKey.VIEW_FOV_WIDE);
      controlFovWide.getAttributes().setImageSource(getImageSource(AVKey.VIEW_FOV_WIDE));
      this.addRenderable(controlFovWide);
    }
    if (this.showVeControls) {
      // Vertical Exaggeration
      controlVeUp = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlVeUp.setValue(AVKey.VIEW_OPERATION, AVKey.VERTICAL_EXAGGERATION_UP);
      controlVeUp.getAttributes().setImageSource(getImageSource(AVKey.VERTICAL_EXAGGERATION_UP));
      this.addRenderable(controlVeUp);
      controlVeDown = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      controlVeDown.setValue(AVKey.VIEW_OPERATION, AVKey.VERTICAL_EXAGGERATION_DOWN);
      controlVeDown
          .getAttributes()
          .setImageSource(getImageSource(AVKey.VERTICAL_EXAGGERATION_DOWN));
      this.addRenderable(controlVeDown);
    }
    if (this.showTrash) {
      trashControl = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
      trashControl.getAttributes().setImageSource(IMAGE_SETTINGS);
      trashControl.getAttributes().setSize(new Dimension(myButtonWidth, myButtonHeight));
      trashControl.setValue(AVKey.VIEW_OPERATION, "trash it");
      this.addRenderable(trashControl);
    }
    //        if (this.showMenu) {
    //        	menuControl = new ScreenAnnotation(NOTEXT, ORIGIN, ca);
    //        	menuControl.getAttributes().setImageSource(IMAGE_SIMPLE_GUI);
    //        	menuControl.getAttributes().setSize(new Dimension(50, 50));
    //        	menuControl.setValue(AVKey.VIEW_OPERATION, "auxiliary");
    //        	this.addRenderable(menuControl);
    //        }

    // Place controls according to layout and viewport dimension
    updatePositions(dc);

    this.initialized = true;
  }
  /**
   * Get the control type associated with the given object or null if unknown.
   *
   * @param control the control object
   * @return the control type. Can be one of {@link AVKey#VIEW_PAN}, {@link AVKey#VIEW_LOOK}, {@link
   *     AVKey#VIEW_HEADING_LEFT}, {@link AVKey#VIEW_HEADING_RIGHT}, {@link AVKey#VIEW_ZOOM_IN},
   *     {@link AVKey#VIEW_ZOOM_OUT}, {@link AVKey#VIEW_PITCH_UP}, {@link AVKey#VIEW_PITCH_DOWN},
   *     {@link AVKey#VIEW_FOV_NARROW} or {@link AVKey#VIEW_FOV_WIDE}.
   *     <p>Returns null if the object is not a view control associated with this layer.
   */
  public String getControlType(Object control) {
    if (control == null || !(control instanceof ScreenAnnotation)) return null;

    if (showPanControls && controlPan.equals(control)) return AVKey.VIEW_PAN;
    else if (showLookControls && controlLook.equals(control)) return AVKey.VIEW_LOOK;
    else if (showHeadingControls && controlHeadingLeft.equals(control))
      return AVKey.VIEW_HEADING_LEFT;
    else if (showHeadingControls && controlHeadingRight.equals(control))
      return AVKey.VIEW_HEADING_RIGHT;
    else if (showZoomControls && controlZoomIn.equals(control)) return AVKey.VIEW_ZOOM_IN;
    else if (showZoomControls && controlZoomOut.equals(control)) return AVKey.VIEW_ZOOM_OUT;
    else if (showPitchControls && controlPitchUp.equals(control)) return AVKey.VIEW_PITCH_UP;
    else if (showPitchControls && controlPitchDown.equals(control)) return AVKey.VIEW_PITCH_DOWN;
    else if (showFovControls && controlFovNarrow.equals(control)) return AVKey.VIEW_FOV_NARROW;
    else if (showFovControls && controlFovWide.equals(control)) return AVKey.VIEW_FOV_WIDE;
    else if (showVeControls && controlVeUp.equals(control)) return AVKey.VERTICAL_EXAGGERATION_UP;
    else if (showVeControls && controlVeDown.equals(control))
      return AVKey.VERTICAL_EXAGGERATION_DOWN;

    return null;
  }