protected void movePolygon(Point previousMousePoint, Point mousePoint) {
    // Intersect a ray through each mouse point, with a geoid passing through the reference
    // elevation.
    // If either ray fails to intersect the geoid, then ignore this event. Use the difference
    // between the two
    // intersected positions to move the control point's location.

    View view = this.wwd.getView();
    Globe globe = this.wwd.getModel().getGlobe();

    Position refPos = this.polygon.getReferencePosition();
    if (refPos == null) return;

    Line ray = view.computeRayFromScreenPoint(mousePoint.getX(), mousePoint.getY());
    Line previousRay =
        view.computeRayFromScreenPoint(previousMousePoint.getX(), previousMousePoint.getY());

    Vec4 vec = AirspaceEditorUtil.intersectGlobeAt(this.wwd, refPos.getElevation(), ray);
    Vec4 previousVec =
        AirspaceEditorUtil.intersectGlobeAt(this.wwd, refPos.getElevation(), previousRay);

    if (vec == null || previousVec == null) {
      return;
    }

    Position pos = globe.computePositionFromPoint(vec);
    Position previousPos = globe.computePositionFromPoint(previousVec);
    LatLon change = pos.subtract(previousPos);

    this.polygon.move(new Position(change.getLatitude(), change.getLongitude(), 0.0));
  }
  protected void setPolygonHeight(Point previousMousePoint, Point mousePoint) {
    // Find the closest points between the rays through each screen point, and the ray from the
    // control point
    // and in the direction of the globe's surface normal. Compute the elevation difference between
    // these two
    // points, and use that as the change in polygon height.

    Position referencePos = this.polygon.getReferencePosition();
    if (referencePos == null) return;

    Vec4 referencePoint = this.wwd.getModel().getGlobe().computePointFromPosition(referencePos);

    Vec4 surfaceNormal =
        this.wwd
            .getModel()
            .getGlobe()
            .computeSurfaceNormalAtLocation(
                referencePos.getLatitude(), referencePos.getLongitude());
    Line verticalRay = new Line(referencePoint, surfaceNormal);
    Line screenRay =
        this.wwd.getView().computeRayFromScreenPoint(mousePoint.getX(), mousePoint.getY());
    Line previousScreenRay =
        this.wwd
            .getView()
            .computeRayFromScreenPoint(previousMousePoint.getX(), previousMousePoint.getY());

    Vec4 pointOnLine = AirspaceEditorUtil.nearestPointOnLine(verticalRay, screenRay);
    Vec4 previousPointOnLine =
        AirspaceEditorUtil.nearestPointOnLine(verticalRay, previousScreenRay);

    Position pos = this.wwd.getModel().getGlobe().computePositionFromPoint(pointOnLine);
    Position previousPos =
        this.wwd.getModel().getGlobe().computePositionFromPoint(previousPointOnLine);
    double elevationChange = pos.getElevation() - previousPos.getElevation();

    java.util.List<Position> boundary = new ArrayList<Position>();
    for (LatLon ll : this.polygon.getOuterBoundary()) {
      boundary.add(new Position(ll, ((Position) ll).getElevation() + elevationChange));
    }

    this.polygon.setOuterBoundary(boundary);
  }
  /**
   * Add a vertex to the polygon's outer boundary.
   *
   * @param mousePoint the point at which the mouse was clicked. The new vertex will be placed as
   *     near as possible to this point, at the elevation of the polygon.
   */
  protected void addVertex(Point mousePoint) {
    // Try to find the edge that is closest to a ray passing through the screen point. We're trying
    // to determine
    // the user's intent as to which edge a new two control points should be added to.

    Line ray = this.wwd.getView().computeRayFromScreenPoint(mousePoint.getX(), mousePoint.getY());
    Vec4 pickPoint = this.intersectPolygonAltitudeAt(ray);

    double nearestDistance = Double.MAX_VALUE;
    int newVertexIndex = 0;

    // Loop through the control points and determine which edge is closest to the pick point
    for (int i = 0; i < this.controlPoints.size(); i++) {
      ControlPointMarker thisMarker = (ControlPointMarker) this.controlPoints.get(i);
      ControlPointMarker nextMarker =
          (ControlPointMarker) this.controlPoints.get((i + 1) % this.controlPoints.size());

      Vec4 pointOnEdge =
          AirspaceEditorUtil.nearestPointOnSegment(thisMarker.point, nextMarker.point, pickPoint);
      if (!AirspaceEditorUtil.isPointBehindLineOrigin(ray, pointOnEdge)) {
        double d = pointOnEdge.distanceTo3(pickPoint);
        if (d < nearestDistance) {
          newVertexIndex = i + 1;
          nearestDistance = d;
        }
      }
    }

    Position newPosition = this.wwd.getModel().getGlobe().computePositionFromPoint(pickPoint);

    // Copy the outer boundary list
    ArrayList<Position> positionList = new ArrayList<Position>(this.controlPoints.size());
    for (LatLon position : this.getPolygon().getOuterBoundary()) {
      positionList.add((Position) position);
    }

    // Add the new vertex
    positionList.add(newVertexIndex, newPosition);

    this.getPolygon().setOuterBoundary(positionList);
  }
  protected void moveControlPoint(
      ControlPointMarker controlPoint, Point lastMousePoint, Point moveToPoint) {
    View view = this.wwd.getView();
    Globe globe = this.wwd.getModel().getGlobe();

    Position refPos = controlPoint.getPosition();
    if (refPos == null) return;

    Line ray = view.computeRayFromScreenPoint(moveToPoint.getX(), moveToPoint.getY());
    Line previousRay = view.computeRayFromScreenPoint(lastMousePoint.getX(), lastMousePoint.getY());

    Vec4 vec = AirspaceEditorUtil.intersectGlobeAt(this.wwd, refPos.getElevation(), ray);
    Vec4 previousVec =
        AirspaceEditorUtil.intersectGlobeAt(this.wwd, refPos.getElevation(), previousRay);

    if (vec == null || previousVec == null) {
      return;
    }

    Position pos = globe.computePositionFromPoint(vec);
    Position previousPos = globe.computePositionFromPoint(previousVec);
    LatLon change = pos.subtract(previousPos);

    java.util.List<LatLon> boundary = new ArrayList<LatLon>();
    for (LatLon ll : this.polygon.getOuterBoundary()) {
      boundary.add(ll);
    }

    boundary.set(controlPoint.getIndex(), new Position(pos.add(change), refPos.getAltitude()));

    // ExtrudedPolygon ensures that the last boundary position is the same as the first. Remove the
    // last point
    // before setting the boundary.
    boundary.remove(boundary.size() - 1);

    this.polygon.setOuterBoundary(boundary);
  }