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

gov.nasa.worldwind.util.ShapeEditor Maven / Gradle / Ivy

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

package gov.nasa.worldwind.util;

import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.event.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.layers.*;
import gov.nasa.worldwind.pick.*;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.render.airspaces.*;
import gov.nasa.worldwind.render.airspaces.Box;
import gov.nasa.worldwind.render.airspaces.Polygon;
import gov.nasa.worldwind.render.markers.*;

import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.util.*;
import java.util.List;

/**
 * Provides a user interface for editing a shape and performs editing. Depending on the shape type, the shape is shown
 * with control points for vertex locations and size. All shapes are shown with a handle that provides rotation.
 * 

* Left-drag on the shape's body moves the whole shape. Left-drag on a control point performs the action associated with * that control point. The editor provides vertex insertion and removal for airspace Polygon, Curtain, Route and Track * shapes, and SurfacePolygon and SurfacePolyline. Shift-left-click when the cursor is over the shape inserts a control * point at the cursor's position. Alt-left-click when the cursor is over a control point removes that control point. * Control points are added to the ends of airspace Polygon, Curtain, Route and Track, and SurfacePolyline by * shift-left-click on the first or last control point of the shape. *

* This editor supports airspaces other than Cake and all surface shapes except SurfaceMultiPolygon and SurfaceImage. * * @author tag * @version $Id: ShapeEditor.java 3423 2015-09-23 20:59:03Z tgaskins $ */ public class ShapeEditor implements SelectListener, PropertyChangeListener { // Control point purposes /** * Indicates that a control point is associated with annotation. */ protected static String ANNOTATION = "gov.nasa.worldwind.shapeEditor.Annotation"; /** * Indicates a control point is associated with a location. */ protected static String LOCATION = "gov.nasa.worldwind.shapeEditor.Location"; /** * Indicates that a control point is associated with whole-shape rotation. */ protected static String ROTATION = "gov.nasa.worldwind.shapeEditor.Rotation"; /** * Indicates that a control point is associated with width change. */ protected static String WIDTH = "gov.nasa.worldwind.shapeEditor.Width"; /** * Indicates that a control point is associated with height change. */ protected static String HEIGHT = "gov.nasa.worldwind.shapeEditor.Height"; /** * Indicates that a control point is associated with the left width of a shape. */ protected static String LEFT_WIDTH = "gov.nasa.worldwind.shapeEditor.LeftWidth"; /** * Indicates that a control point is associated with the right width of a shape. */ protected static String RIGHT_WIDTH = "gov.nasa.worldwind.shapeEditor.RightWidth"; /** * Indicates that a control point is associated with the inner radius of a shape. */ protected static String INNER_RADIUS = "gov.nasa.worldwind.shapeEditor.InnerRadius"; /** * Indicates that a control point is associated with the outer radius of a shape. */ protected static String OUTER_RADIUS = "gov.nasa.worldwind.shapeEditor.OuterRadius"; /** * Indicates that a control point is associated with the inner minor radius of a shape. */ protected static String INNER_MINOR_RADIUS = "gov.nasa.worldwind.shapeEditor.InnerMinorRadius"; /** * Indicates that a control point is associated with the outer minor radius of a shape. */ protected static String OUTER_MINOR_RADIUS = "gov.nasa.worldwind.shapeEditor.OuterMinorRadius"; /** * Indicates that a control point is associated with the inner major radius of a shape. */ protected static String INNER_MAJOR_RADIUS = "gov.nasa.worldwind.shapeEditor.InnerMajorRadius"; /** * Indicates that a control point is associated with the outer major radius of a shape. */ protected static String OUTER_MAJOR_RADIUS = "gov.nasa.worldwind.shapeEditor.OuterMajorRadius"; /** * Indicates that a control point is associated with the left azimuth of a shape. */ protected static String LEFT_AZIMUTH = "gov.nasa.worldwind.shapeEditor.LeftAzimuth"; /** * Indicates that a control point is associated with the right azimuth of a shape. */ protected static String RIGHT_AZIMUTH = "gov.nasa.worldwind.shapeEditor.RightAzimuth"; /** * Represents editor control points. */ protected static class ControlPointMarker extends BasicMarker { /** * The control point's ID, which is typically its list index when the shape has a list of locations. */ protected int id; /** * Identifies individual track boxes. */ protected int leg; /** * Indicates the feature the control point affects. */ protected String purpose; // indicates the feature the control point affects /** * Indicates size (in meters) if this control point affects a size of the shape, otherwise null. */ protected Double size; /** * Indicates angle if this control point affects an angle associated with the shape, otherwise null. */ protected Angle rotation; public ControlPointMarker(Position position, MarkerAttributes attrs, int id, String purpose) { super(position, attrs); this.id = id; this.purpose = purpose; } public ControlPointMarker(Position position, MarkerAttributes attrs, int id, int leg, String purpose) { this(position, attrs, id, purpose); this.leg = leg; } public int getId() { return this.id; } public int getLeg() { return leg; } public String getPurpose() { return this.purpose; } public void setSize(double size) { this.size = size; } public Double getSize() { return size; } public void setRotation(Angle rotation) { this.rotation = rotation; } public Angle getRotation() { return rotation; } } /** * Editor state indicating that the shape is not being resized or moved. */ protected static final int NONE = 0; /** * Editor state indicating that the shape is being moved. */ protected static final int MOVING = 1; /** * Editor state indicating that the shape is being sized or otherwise respecified. */ protected static final int SIZING = 2; /** * The {@link gov.nasa.worldwind.WorldWindow} associated with the shape. */ protected final WorldWindow wwd; /** * The shape associated with the editor. Specified at construction and not subsequently modifiable. */ protected Renderable shape; /** * The layer holding the editor's control points. */ protected MarkerLayer controlPointLayer; /** * The layer holding the rotation line and perhaps other affordances. */ protected RenderableLayer accessoryLayer; /** * The layer holding the control point's annotation. */ protected RenderableLayer annotationLayer; /** * The layer holding a shadow copy of the shape while the shape is being moved or sized. */ protected RenderableLayer shadowLayer; /** * The control point annotation. */ protected EditorAnnotation annotation; /** * The units formatter to use when creating control point annotations. */ protected UnitsFormat unitsFormat; /** * Indicates whether the editor is ready for editing. */ protected boolean armed; /** * Indicates whether the editor is in the midst of an editing operation. */ protected boolean active; /** * Indicates the current editing operation, one of NONE, MOVING or SIZING. */ protected int activeOperation = NONE; /** * The terrain position associated with the cursor during the just previous drag event. */ protected Position previousPosition = null; /** * The control point associated with the current sizing operation. */ protected ControlPointMarker currentSizingMarker; /** * The attributes associated with the shape when the editor is constructed. These are swapped out during editing * operations in order to make the shape semi-transparent. */ protected ShapeAttributes originalAttributes; /** * The highlight attributes associated with the shape when the editor is constructed. These are swapped out during * editing operations in order to make the shape semi-transparent. */ protected ShapeAttributes originalHighlightAttributes; /** * The event most recently recieved by the selection method. */ protected SelectEvent currentEvent; /** * For shapes without an inherent heading, the current heading established by the editor for the shape. */ protected Angle currentHeading = Angle.ZERO; /** * Indicates track legs that are adjacent to their previous leg in the track. */ protected List trackAdjacencyList; /** * Attributes used to represent shape vertices. */ protected MarkerAttributes locationControlPointAttributes; /** * Attributes used to represent shape size. */ protected MarkerAttributes sizeControlPointAttributes; /** * Attributes used to represent shape rotation and other angular features. */ protected MarkerAttributes angleControlPointAttributes; /** * Indicates whether shapes with sub-segments such as Route and Track may be edited to add and remove legs. */ protected boolean extensionEnabled = true; /** * Constructs an editor for a specified shape. Once constructed, the editor must be armed to operate. See {@link * #setArmed(boolean)}. * * @param wwd the {@link gov.nasa.worldwind.WorldWindow} associated with the specified shape. * @param originalShape the shape to edit. * * @throws java.lang.IllegalArgumentException if either the specified world window or shape is null. */ public ShapeEditor(WorldWindow wwd, Renderable originalShape) { if (wwd == null) { String msg = Logging.getMessage("nullValue.WorldWindow"); Logging.logger().log(java.util.logging.Level.SEVERE, msg); throw new IllegalArgumentException(msg); } if (originalShape == null) { String msg = Logging.getMessage("nullValue.Shape"); Logging.logger().log(java.util.logging.Level.SEVERE, msg); throw new IllegalArgumentException(msg); } if (!(originalShape instanceof Movable2)) { String msg = Logging.getMessage("generic.Movable2NotSupported"); Logging.logger().log(java.util.logging.Level.SEVERE, msg); throw new IllegalArgumentException(msg); } if (!(originalShape instanceof Attributable)) { String msg = Logging.getMessage("generic.AttributableNotSupported"); Logging.logger().log(java.util.logging.Level.SEVERE, msg); throw new IllegalArgumentException(msg); } this.wwd = wwd; this.shape = originalShape; this.originalAttributes = ((Attributable) this.getShape()).getAttributes(); // Create a layer to hold the control points. this.controlPointLayer = new MarkerLayer(); this.controlPointLayer.setKeepSeparated(false); this.controlPointLayer.setValue(AVKey.IGNORE, true); // means "Don't show this layer in the layer manager." if (this.shape instanceof SurfaceShape || (this.shape instanceof Airspace && ((Airspace) this.shape).isDrawSurfaceShape())) { // This ensures that control points are always placed on the terrain for surface shapes. this.controlPointLayer.setOverrideMarkerElevation(true); this.controlPointLayer.setElevation(0); } // Create a layer to hold the rotation line and any other affordances. this.accessoryLayer = new RenderableLayer(); this.accessoryLayer.setPickEnabled(false); this.accessoryLayer.setValue(AVKey.IGNORE, true); // Set up the Path for the rotation line. ShapeAttributes lineAttrs = new BasicShapeAttributes(); lineAttrs.setOutlineMaterial(Material.GREEN); lineAttrs.setOutlineWidth(2); java.util.List lineLocations = new ArrayList(2); lineLocations.add(Position.ZERO); lineLocations.add(Position.ZERO); Path rotationLine = new Path(lineLocations); rotationLine.setFollowTerrain(true); rotationLine.setPathType(AVKey.GREAT_CIRCLE); rotationLine.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND); rotationLine.setAttributes(lineAttrs); this.accessoryLayer.addRenderable(rotationLine); // Create a layer to hold the editing annotations. this.annotationLayer = new RenderableLayer(); this.annotationLayer.setPickEnabled(false); this.annotationLayer.setValue(AVKey.IGNORE, true); // Create the annotation. this.annotation = new EditorAnnotation(""); this.annotationLayer.addRenderable(this.annotation); // Create a layer to hold the shadow shape, the shape that shows the state before an editing operation. this.shadowLayer = new RenderableLayer(); this.shadowLayer.setPickEnabled(false); this.shadowLayer.setValue(AVKey.IGNORE, true); // Create a units formatter for the annotations. this.unitsFormat = new UnitsFormat(); this.unitsFormat.setFormat(UnitsFormat.FORMAT_LENGTH, " %,12.3f %s"); // Create the attributes assigned to the control points. this.makeControlPointAttributes(); } protected void makeControlPointAttributes() { // Each attribute has color, marker type, opacity, size in pixels, and minimum size in meters (0 indicates that // the minimum size is not considered. this.locationControlPointAttributes = new BasicMarkerAttributes(Material.BLUE, BasicMarkerShape.SPHERE, 0.7, 10, 0); this.sizeControlPointAttributes = new BasicMarkerAttributes(Material.CYAN, BasicMarkerShape.SPHERE, 0.7, 10, 0); this.angleControlPointAttributes = new BasicMarkerAttributes(Material.GREEN, BasicMarkerShape.SPHERE, 0.7, 10, 0); } /** * Indicates the units formatter associated with this editor. * * @return the units formatter associated with this editor. */ public UnitsFormat getUnitsFormat() { return unitsFormat; } /** * Specifies the units formatter to use when creating editor annotations. * * @param unitsFormat the units formatter to use. A default is created if null is specified. */ public void setUnitsFormat(UnitsFormat unitsFormat) { this.unitsFormat = unitsFormat != null ? unitsFormat : new UnitsFormat(); } /** * Indicates the World Window associated with this editor. * * @return the World Window associated with this editor. */ public WorldWindow getWwd() { return this.wwd; } /** * Indicates the shape associated with this editor. * * @return the shape associated with this editor. */ public Renderable getShape() { return this.shape; } /** * Indicates the control point layer used by this editor. * * @return the control point layer used by this editor. */ public MarkerLayer getControlPointLayer() { return controlPointLayer; } /** * Indicates the accessory layer used by this editor. * * @return the accessory layer used by this editor. */ public RenderableLayer getAccessoryLayer() { return accessoryLayer; } /** * Indicates the annotation layer used by this editor. * * @return the annotation layer used by this editor. */ public RenderableLayer getAnnotationLayer() { return annotationLayer; } /** * Indicates the shadow layer used by this editor. * * @return the shadow layer used by this editor. */ public RenderableLayer getShadowLayer() { return shadowLayer; } /** * Indicates the annotation used to show locations and measurements. * * @return the annotation used to show shape locations and measurements. */ public EditorAnnotation getAnnotation() { return annotation; } /** * Indicates whether an editing operation is currently underway. Operations are SIZING and MOVING. * * @return true if an operation is underway, otherwise false. */ public boolean isActive() { return active; } /** * Indicates the current operation being performed, either SIZING, MOVING or NONE. * * @return the current operation underway. */ public int getActiveOperation() { return activeOperation; } /** * Returns the geographic position associated with the previous select event. * * @return the geographic position associated with the previous select event. */ public Position getPreviousPosition() { return previousPosition; } /** * Indicates the control point currently used in the operation underway. * * @return the control point used in the operation currently underway. */ public ControlPointMarker getCurrentSizingMarker() { return currentSizingMarker; } /** * Indicates the attributes associated with the shape when the editor was created. * * @return the attributes associated with the shape when the editor was created. */ public ShapeAttributes getOriginalAttributes() { return originalAttributes; } /** * Indicates the highlight attributes associated with the shape prior to their being changed to achieve shape * transparency. * * @return the original highlight attributes. */ public ShapeAttributes getOriginalHighlightAttributes() { return originalHighlightAttributes; } /** * Indicates the current rotation heading. This is updated as shapes are rotated. * * @return the current rotation heading. */ public Angle getCurrentHeading() { return currentHeading; } /** * Indicates the attributes associated with location control points. * * @return the attributes associated with location control points. */ public MarkerAttributes getLocationControlPointAttributes() { return locationControlPointAttributes; } /** * Indicates the attributes associated with size control points. * * @return the attributes associated with size control points. */ public MarkerAttributes getSizeControlPointAttributes() { return sizeControlPointAttributes; } /** * Indicates the attributes associated with angle control points. * * @return the attributes associated with angle control points. */ public MarkerAttributes getAngleControlPointAttributes() { return angleControlPointAttributes; } /** * Indicates whether multi-segment shapes such as Route and Track may be edited to add or remove segments. * @return true if segment addition and deletion are enabled, otherwise false. The default is true. */ public boolean isExtensionEnabled() { return extensionEnabled; } /** * Specifies whether multi-segment shapes such as Route and Track may be edited to add or remove segments. * * @param extensionEnabled true to allow segment addition and removal, otherwise false. */ public void setExtensionEnabled(boolean extensionEnabled) { this.extensionEnabled = extensionEnabled; } /** * Indicates the event most recently passed to the select handler. * * @return the event most recently passed to the select handler. */ public SelectEvent getCurrentEvent() { return currentEvent; } /** * Indicates whether this editor is armed. * * @return true if the editor is armed, otherwise false. */ public boolean isArmed() { return this.armed; } /** * Arms or disarms the editor. When armed, the editor's shape is displayed with control points and other affordances * that indicate possible editing operations. * * @param armed true to arm the editor, false to disarm it and remove the control points * and other affordances. This method must be called when the editor is no longer needed so that the * editor may remove the resources it created when it was armed. */ public void setArmed(boolean armed) { if (!this.isArmed() && armed) { this.enable(); } else if (this.isArmed() && !armed) { this.disable(); } this.armed = armed; } /** * Called by {@link #setArmed(boolean)} to initialize this editor. */ protected void enable() { LayerList layers = this.getWwd().getModel().getLayers(); if (!layers.contains(this.getControlPointLayer())) layers.add(this.getControlPointLayer()); if (!this.getControlPointLayer().isEnabled()) this.getControlPointLayer().setEnabled(true); if (!layers.contains(this.getAccessoryLayer())) layers.add(this.getAccessoryLayer()); if (!this.getAccessoryLayer().isEnabled()) this.getAccessoryLayer().setEnabled(true); if (!layers.contains(this.getAnnotationLayer())) layers.add(this.getAnnotationLayer()); if (!layers.contains(this.getShadowLayer())) layers.add(0, this.getShadowLayer()); this.getShadowLayer().setEnabled(true); if (this.getShape() instanceof TrackAirspace) this.determineTrackAdjacency(); this.updateControlPoints(); this.getWwd().addSelectListener(this); this.getWwd().getSceneController().addPropertyChangeListener(this); } /** * Called by {@link #setArmed(boolean)} to remove resources no longer needed after editing. */ protected void disable() { LayerList layers = this.getWwd().getModel().getLayers(); layers.remove(this.getControlPointLayer()); layers.remove(this.getAccessoryLayer()); layers.remove(this.getAnnotationLayer()); layers.remove(this.getShadowLayer()); getWwd().removeSelectListener(this); getWwd().getSceneController().removePropertyChangeListener(this); ((Component) this.getWwd()).setCursor(null); } /** * Determines and stores internally the adjacency of successive track legs. Called during editor arming. */ protected void determineTrackAdjacency() { if (this.trackAdjacencyList == null) this.trackAdjacencyList = new ArrayList(); else this.trackAdjacencyList.clear(); TrackAirspace track = (TrackAirspace) this.getShape(); List legs = track.getLegs(); for (int i = 1; i < legs.size(); i++) { boolean adjacent = legs.get(i - 1).getLocations()[1].equals(legs.get(i).getLocations()[0]); if (adjacent) this.trackAdjacencyList.add(legs.get(i)); } } /** * The select handler, the method called when the user selects (rolls over, left clicks, etc.) the shape or a * control point. Does not necessarily indicate the shape associated with this editor. * * @param event the select event indicating what was selected and the geographic location under the cursor. */ public void selected(SelectEvent event) { if (event == null) { String msg = Logging.getMessage("nullValue.EventIsNull"); Logging.logger().log(java.util.logging.Level.FINE, msg); throw new IllegalArgumentException(msg); } this.currentEvent = event; if (event.getEventAction().equals(SelectEvent.DRAG_END)) { this.active = false; this.activeOperation = NONE; this.previousPosition = null; ((Component) this.getWwd()).setCursor(null); this.removeShadowShape(); this.updateAnnotation(null); } else if (event.getEventAction().equals(SelectEvent.ROLLOVER)) { if (!(this.getWwd() instanceof Component)) return; // Update the cursor. Cursor cursor = null; if (this.activeOperation == MOVING) cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); else if (this.getActiveOperation() == SIZING) cursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); else if (event.getTopObject() != null && event.getTopObject() == this.getShape()) cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); else if (event.getTopObject() != null && event.getTopObject() instanceof Marker) cursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); ((Component) this.getWwd()).setCursor(cursor); // Update the shape or control point annotation. if (this.getActiveOperation() == MOVING && event.getTopObject() == this.getShape()) this.updateShapeAnnotation(); else if (this.getActiveOperation() == SIZING) this.updateAnnotation(this.getCurrentSizingMarker()); else if (event.getTopObject() != null && event.getTopObject() == this.getShape()) this.updateShapeAnnotation(); else if (event.getTopObject() != null && event.getTopObject() instanceof ControlPointMarker) this.updateAnnotation((ControlPointMarker) event.getTopObject()); else this.updateAnnotation(null); } else if (event.getEventAction().equals(SelectEvent.LEFT_PRESS)) { // Prepare for a drag. this.active = true; PickedObjectList objectsUnderCursor = this.getWwd().getObjectsAtCurrentPosition(); if (objectsUnderCursor != null) { PickedObject terrainObject = objectsUnderCursor.getTerrainObject(); if (terrainObject != null) this.previousPosition = terrainObject.getPosition(); } } else if (event.getEventAction().equals(SelectEvent.LEFT_CLICK)) { Object topObject = event.getTopObject(); if (topObject == null) return; // Add and delete control points. if (event.getTopPickedObject().getParentLayer() == this.getControlPointLayer()) { this.reshapeShape((ControlPointMarker) topObject); this.updateControlPoints(); this.updateAnnotation(this.getCurrentSizingMarker()); event.consume(); } else if ((event.getTopObject() == this.getShape()) && (this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) { this.reshapeShape(null); this.updateControlPoints(); event.consume(); } } else if (event.getEventAction().equals(SelectEvent.DRAG)) { if (!this.isActive()) return; DragSelectEvent dragEvent = (DragSelectEvent) event; Object topObject = dragEvent.getTopObject(); if (topObject == null) return; if (this.getActiveOperation() == NONE) // drag is starting this.makeShadowShape(); if (topObject == this.getShape() || this.getActiveOperation() == MOVING) { // Move the whole shape. this.activeOperation = MOVING; this.dragWholeShape(dragEvent); this.updateControlPoints(); this.updateShapeAnnotation(); event.consume(); } else if (dragEvent.getTopPickedObject().getParentLayer() == this.getControlPointLayer() || this.getActiveOperation() == SIZING) { // Perform the editing operation associated with the selected control point. this.activeOperation = SIZING; this.reshapeShape((ControlPointMarker) topObject); this.updateControlPoints(); this.updateAnnotation(this.getCurrentSizingMarker()); event.consume(); } this.getWwd().redraw(); // update the display } } /** * The property change listener, the method called when a property of the Scene Controller changes * (vertical exaggeration, etc.). Does not necessarily indicate a property associated with this editor. * * @param event the property change event indicating the property name and its associated value. */ public void propertyChange(PropertyChangeEvent event) { if (event == null) { String msg = Logging.getMessage("nullValue.EventIsNull"); Logging.logger().log(java.util.logging.Level.FINE, msg); throw new IllegalArgumentException(msg); } if (event.getPropertyName().equals(AVKey.VERTICAL_EXAGGERATION)) { // The orientation line altitudes depend on the vertical exaggeration. this.updateControlPoints(); } } /** * Creates the shape that will remain at the same location and is the same size as the shape to be edited. */ protected void makeShadowShape() { Renderable shadowShape = this.doMakeShadowShape(); if (shadowShape == null) return; if (this.getShape() instanceof Airspace) ((Airspace) this.getShape()).setAlwaysOnTop(true); // Reduce the opacity of an opaque current shape so that the shadow shape is visible while editing // is performed. this.originalAttributes = ((Attributable) this.getShape()).getAttributes(); this.originalHighlightAttributes = ((Attributable) this.getShape()).getHighlightAttributes(); ShapeAttributes editingAttributes = new BasicShapeAttributes(this.originalAttributes); if (editingAttributes.getInteriorOpacity() == 1) editingAttributes.setInteriorOpacity(0.7); ((Attributable) this.getShape()).setAttributes(editingAttributes); ((Attributable) this.getShape()).setHighlightAttributes(editingAttributes); this.getShadowLayer().addRenderable(shadowShape); if (this.getShape() instanceof Airspace) { double[] altitudes = ((Airspace) shadowShape).getAltitudes(); ((Airspace) shadowShape).setAltitudes(altitudes[0], 0.95 * altitudes[1]); // // // Show only the outline of the shadow shape. // AirspaceAttributes shadowAttributes = new BasicAirspaceAttributes(this.originalAttributes); // shadowAttributes.setDrawInterior(false); // ((Airspace)shadowShape).setAttributes(shadowAttributes); // ((Airspace)shadowShape).setHighlightAttributes(shadowAttributes); } } /** * Remove the shadow shape. */ protected void removeShadowShape() { this.getShadowLayer().removeAllRenderables(); if (this.getShape() instanceof AbstractAirspace) ((AbstractAirspace) this.getShape()).setAlwaysOnTop(false); // Restore the original attributes. if (this.getOriginalAttributes() != null) { ((Attributable) this.getShape()).setAttributes(this.getOriginalAttributes()); ((Attributable) this.getShape()).setHighlightAttributes(this.getOriginalHighlightAttributes()); } this.getWwd().redraw(); } /** * Creates and returns the stationary shape displayed during editing operations. Subclasses should override this * method to create shadow shapes appropriate to the editor's shape. * * @return the new shadow shape created, or null if the shape type is not recognized. */ protected Renderable doMakeShadowShape() { if (this.getShape() instanceof Polygon) return new Polygon((Polygon) this.getShape()); else if (this.getShape() instanceof PartialCappedCylinder) return new PartialCappedCylinder((PartialCappedCylinder) this.getShape()); else if (this.getShape() instanceof CappedCylinder) return new CappedCylinder((CappedCylinder) this.getShape()); else if (this.getShape() instanceof CappedEllipticalCylinder) return new CappedEllipticalCylinder((CappedEllipticalCylinder) this.getShape()); else if (this.getShape() instanceof Orbit) return new Orbit((Orbit) this.getShape()); else if (this.getShape() instanceof Route) return new Route((Route) this.getShape()); else if (this.getShape() instanceof Curtain) return new Curtain((Curtain) this.getShape()); else if (this.getShape() instanceof SphereAirspace) return new SphereAirspace((SphereAirspace) this.getShape()); else if (this.getShape() instanceof TrackAirspace) return new TrackAirspace((TrackAirspace) this.getShape()); else if (this.getShape() instanceof SurfaceSquare) return new SurfaceSquare((SurfaceSquare) this.getShape()); else if (this.getShape() instanceof SurfaceQuad) return new SurfaceQuad((SurfaceQuad) this.getShape()); else if (this.getShape() instanceof SurfaceCircle) return new SurfaceCircle((SurfaceCircle) this.getShape()); else if (this.getShape() instanceof SurfaceEllipse) return new SurfaceEllipse((SurfaceEllipse) this.getShape()); else if (this.getShape() instanceof SurfacePolyline) return new SurfacePolyline((SurfacePolyline) this.getShape()); else if (this.getShape() instanceof SurfacePolygon) return new SurfacePolygon((SurfacePolygon) this.getShape()); return null; } /** * Performs shape-specific minor modifications to shapes after editing operation are performed. Some editing * operations cause positions that are originally identical to become slightly different and thereby disrupt the * original connectivity of the shape. This is the case for track-airspace legs, for instance. This method is called * just after editing operations are performed in order to give the editor a chance to reform connectivity or * otherwise modify the shape to retain its original properties. Subclasses should override this method if they are * aware of shapes other than those recognized by default and those shapes need such adjustment during editing. */ protected void adjustShape() { if (this.getShape() instanceof TrackAirspace) this.adjustTrackShape(); } /** * Restores adjacency of {@link gov.nasa.worldwind.render.airspaces.TrackAirspace} shapes. Called by {@link * #adjustShape()}. */ protected void adjustTrackShape() { TrackAirspace track = (TrackAirspace) this.getShape(); List legs = track.getLegs(); if (legs == null) return; // Start with the second leg and restore coincidence of the first leg position with that of the previous leg. for (int i = 1; i < legs.size(); i++) { Box leg = legs.get(i); if (this.trackAdjacencyList.contains(legs.get(i))) { leg.setLocations(legs.get(i - 1).getLocations()[1], leg.getLocations()[1]); } } } /** * Moves the entire shape according to a specified drag event. * * @param dragEvent the event initiating the move. */ protected void dragWholeShape(DragSelectEvent dragEvent) { Movable2 dragObject = (Movable2) this.getShape(); View view = getWwd().getView(); Globe globe = getWwd().getModel().getGlobe(); // Compute ref-point position in screen coordinates. Position refPos = dragObject.getReferencePosition(); if (refPos == null) return; Vec4 refPoint = globe.computePointFromPosition(refPos); Vec4 screenRefPoint = view.project(refPoint); // Compute screen-coord delta since last event. int dx = dragEvent.getPickPoint().x - dragEvent.getPreviousPickPoint().x; int dy = dragEvent.getPickPoint().y - dragEvent.getPreviousPickPoint().y; // Find intersection of screen coord ref-point with globe. double x = screenRefPoint.x + dx; double y = dragEvent.getMouseEvent().getComponent().getSize().height - screenRefPoint.y + dy - 1; Line ray = view.computeRayFromScreenPoint(x, y); Intersection inters[] = globe.intersect(ray, refPos.getElevation()); if (inters != null) { // Intersection with globe. Move reference point to the intersection point. Position p = globe.computePositionFromPoint(inters[0].getIntersectionPoint()); dragObject.moveTo(getWwd().getModel().getGlobe(), new Position(p, ((Movable2) this.getShape()).getReferencePosition().getAltitude())); } this.adjustShape(); } /** * Modifies the shape's locations, size or rotation. This method is called when a control point is dragged. * * @param controlPoint the control point selected. */ protected void reshapeShape(ControlPointMarker controlPoint) { this.currentSizingMarker = controlPoint; // If the terrain beneath the control point is null, then the user is attempting to drag the handle off the // globe. This is not a valid state, so we ignore this action but keep the drag operation in effect. PickedObjectList objectsUnderCursor = this.getWwd().getObjectsAtCurrentPosition(); if (objectsUnderCursor == null) return; PickedObject terrainObject = this.getWwd().getObjectsAtCurrentPosition().getTerrainObject(); if (terrainObject == null) return; if (this.getPreviousPosition() == null) { this.previousPosition = terrainObject.getPosition(); return; } // Perform the editing operation. this.doReshapeShape(controlPoint, terrainObject.getPosition()); this.previousPosition = terrainObject.getPosition(); this.adjustShape(); } /** * Called by {@link #reshapeShape(ShapeEditor.ControlPointMarker)} to perform the actual shape modification. * Subclasses should override this method if they provide editing for shapes other than those supported by the basic * editor. * * @param controlPoint the control point selected. * @param terrainPosition the terrain position under the cursor. */ protected void doReshapeShape(ControlPointMarker controlPoint, Position terrainPosition) { if (this.getShape() instanceof Airspace) { if (this.getShape() instanceof Polygon || this.getShape() instanceof Curtain) this.reshapePolygonAirspace(terrainPosition, controlPoint); else if (this.getShape() instanceof CappedCylinder) this.reshapeCappedCylinder(terrainPosition, controlPoint); else if (this.getShape() instanceof CappedEllipticalCylinder) this.reshapeCappedEllipticalCylinder(terrainPosition, controlPoint); else if (this.getShape() instanceof Orbit) this.reshapeOrbit(terrainPosition, controlPoint); else if (this.getShape() instanceof Route) this.reshapeRoute(terrainPosition, controlPoint); else if (this.getShape() instanceof SphereAirspace) this.reshapeSphere(terrainPosition, controlPoint); else if (this.getShape() instanceof TrackAirspace) this.reshapeTrack(terrainPosition, controlPoint); } else if (this.getShape() instanceof SurfaceShape) { if (this.getShape() instanceof SurfacePolygon) this.reshapeSurfacePolygon(terrainPosition, controlPoint); else if (this.getShape() instanceof SurfacePolyline) this.reshapeSurfacePolygon(terrainPosition, controlPoint); else if (this.getShape() instanceof SurfaceCircle) this.reshapeSurfaceCircle(terrainPosition, controlPoint); else if (this.getShape() instanceof SurfaceSquare) this.reshapeSurfaceSquare(terrainPosition, controlPoint); else if (this.getShape() instanceof SurfaceQuad) this.reshapeSurfaceQuad(terrainPosition, controlPoint); else if (this.getShape() instanceof SurfaceEllipse) this.reshapeSurfaceEllipse(terrainPosition, controlPoint); } } /** * Updates the control points to the locations of the currently edited shape. Called each time a modification to the * shape is made. Subclasses should override this method to handle shape types not supported by the basic editor. */ protected void updateControlPoints() { if (this.getShape() instanceof Airspace) { if (this.getShape() instanceof Polygon || this.getShape() instanceof Curtain) this.updatePolygonAirspaceControlPoints(); else if (this.getShape() instanceof PartialCappedCylinder) this.updatePartialCappedCylinderControlPoints(); else if (this.getShape() instanceof CappedCylinder) this.updateCappedCylinderControlPoints(); else if (this.getShape() instanceof CappedEllipticalCylinder) this.updateCappedEllipticalCylinderControlPoints(); else if (this.getShape() instanceof Orbit) this.updateOrbitControlPoints(); else if (this.getShape() instanceof Route) this.updateRouteControlPoints(); else if (this.getShape() instanceof SphereAirspace) this.updateSphereControlPoints(); else if (this.getShape() instanceof TrackAirspace) this.updateTrackControlPoints(); } else if (this.getShape() instanceof SurfaceShape) { if (this.getShape() instanceof SurfacePolygon || this.getShape() instanceof SurfacePolyline) this.updateSurfacePolygonControlPoints(); else if (this.getShape() instanceof SurfaceCircle) this.updateSurfaceCircleControlPoints(); else if (this.getShape() instanceof SurfaceSquare) this.updateSurfaceSquareControlPoints(); else if (this.getShape() instanceof SurfaceQuad) this.updateSurfaceQuadControlPoints(); else if (this.getShape() instanceof SurfaceEllipse) this.updateSurfaceEllipseControlPoints(); } } /** * Updates the annotation indicating the edited shape's center. If the shape has no designated center, this method * prevents the annotation from displaying. */ protected void updateShapeAnnotation() { LatLon center = this.getShapeCenter(); if (center != null) { ControlPointMarker dummyMarker = this.makeControlPoint(new Position(center, 0), new BasicMarkerAttributes(), 0, ANNOTATION); this.updateAnnotation(dummyMarker); } else { this.updateAnnotation(null); } } /** * Returns the shape's center location, or null if it has no designated center. * * @return the shape's center location, or null if the shape has no designated center. */ protected LatLon getShapeCenter() { LatLon center = null; if (this.getShape() instanceof CappedCylinder) center = ((CappedCylinder) this.getShape()).getCenter(); else if (this.getShape() instanceof CappedEllipticalCylinder) center = ((CappedEllipticalCylinder) this.getShape()).getCenter(); else if (this.getShape() instanceof SphereAirspace) center = ((SphereAirspace) this.getShape()).getLocation(); else if (this.getShape() instanceof SurfaceEllipse) center = ((SurfaceEllipse) this.getShape()).getCenter(); else if (this.getShape() instanceof SurfaceQuad) center = ((SurfaceQuad) this.getShape()).getCenter(); return center; } /** * Updates the annotation associated with a specified control point. * * @param controlPoint the control point. */ protected void updateAnnotation(ControlPointMarker controlPoint) { if (controlPoint == null) { this.getAnnotationLayer().setEnabled(false); return; } this.getAnnotationLayer().setEnabled(true); this.getAnnotation().setPosition(controlPoint.getPosition()); String annotationText; if (controlPoint.size != null) annotationText = this.unitsFormat.length(null, controlPoint.size); else if (controlPoint.rotation != null) annotationText = this.unitsFormat.angle(null, controlPoint.rotation); else annotationText = this.unitsFormat.latLon2(controlPoint.getPosition()); this.getAnnotation().setText(annotationText); } /** * Updates the line designating the shape's central axis. * * @param centerPosition the shape's center location and altitude at which to place one of the line's end points. * @param controlPoint the shape orientation control point. */ protected void updateOrientationLine(Position centerPosition, Position controlPoint) { Path rotationLine = (Path) this.getAccessoryLayer().getRenderables().iterator().next(); double cAltitude = centerPosition.getAltitude(); double rAltitude = controlPoint.getAltitude(); if (this.getShapeAltitudeMode() == WorldWind.RELATIVE_TO_GROUND) { rotationLine.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND); rotationLine.setFollowTerrain(true); // Set the line's altitude relative to the ground. cAltitude = centerPosition.getAltitude() - this.getWwd().getModel().getGlobe().getElevation( centerPosition.getLatitude(), centerPosition.getLongitude()); rAltitude = controlPoint.getAltitude() - this.getWwd().getModel().getGlobe().getElevation( controlPoint.getLatitude(), controlPoint.getLongitude()); // Path does not incorporate vertical exaggeration, but airspace shapes do. Compensate for that difference here. cAltitude *= this.getWwd().getSceneController().getVerticalExaggeration(); rAltitude *= this.getWwd().getSceneController().getVerticalExaggeration(); // Add a little altitude so that the line isn't lost during depth buffering. cAltitude += 100; rAltitude += 100; } else if (this.getShapeAltitudeMode() == WorldWind.CLAMP_TO_GROUND) { rotationLine.setAltitudeMode(WorldWind.CLAMP_TO_GROUND); rotationLine.setFollowTerrain(true); } else { rotationLine.setAltitudeMode(WorldWind.ABSOLUTE); rotationLine.setFollowTerrain(false); } java.util.List linePositions = new ArrayList(2); linePositions.add(new Position(centerPosition, cAltitude)); linePositions.add(new Position(controlPoint, rAltitude)); rotationLine.setPositions(linePositions); } /** * Computes the appropriate absolute altitude at which to place a control point at a specified location. * * @param location the location of the control point. * * @return the appropriate altitude at which to place the control point. */ protected double getControlPointAltitude(LatLon location) { return this.doGetControlPointAltitude(location, this.getShape()); } protected double doGetControlPointAltitude(LatLon location, Renderable shape) { double altitude = 0; if (shape instanceof Airspace && !((Airspace) shape).isDrawSurfaceShape()) { Airspace airspace = (Airspace) shape; altitude = airspace.getAltitudes()[1]; if (airspace.getAltitudeDatum()[1].equals(AVKey.ABOVE_GROUND_LEVEL)) { LatLon refPos = airspace.getGroundReference(); if (refPos == null) refPos = location; altitude += getWwd().getModel().getGlobe().getElevation(refPos.getLatitude(), refPos.getLongitude()); } } else if (shape instanceof Path) { for (Position position : ((Path) shape).getPositions()) { if (new LatLon(position).equals(location)) { if (((Path) shape).getAltitudeMode() == WorldWind.ABSOLUTE) altitude = position.getAltitude(); else if (((Path) shape).getAltitudeMode() == WorldWind.RELATIVE_TO_GROUND) altitude = position.getAltitude() + this.getWwd().getModel().getGlobe().getElevation( location.getLatitude(), location.getLongitude()); } } } else if (shape instanceof gov.nasa.worldwind.render.Polygon) { for (Position position : ((gov.nasa.worldwind.render.Polygon) shape).outerBoundary()) { if (new LatLon(position).equals(location)) { if (((gov.nasa.worldwind.render.Polygon) shape).getAltitudeMode() == WorldWind.ABSOLUTE) altitude = position.getAltitude(); else if (((gov.nasa.worldwind.render.Polygon) shape).getAltitudeMode() == WorldWind.RELATIVE_TO_GROUND) altitude = position.getAltitude() + this.getWwd().getModel().getGlobe().getElevation( location.getLatitude(), location.getLongitude()); } } } return altitude; } /** * Indicates the current shape's altitude mode if the shape has one. * * @return the shape's altitude mode if it has one, otherwise WorldWind.ABSOLUTE. */ protected int getShapeAltitudeMode() { int altitudeMode = WorldWind.ABSOLUTE; if (this.getShape() instanceof Airspace && ((Airspace) this.getShape()).isDrawSurfaceShape()) { altitudeMode = WorldWind.CLAMP_TO_GROUND; } else if (this.getShape() instanceof Airspace) { if (((Airspace) this.getShape()).getAltitudeDatum()[1].equals(AVKey.ABOVE_GROUND_LEVEL)) altitudeMode = WorldWind.RELATIVE_TO_GROUND; } else if (this.getShape() instanceof SurfaceShape) { altitudeMode = WorldWind.CLAMP_TO_GROUND; } return altitudeMode; } /** * Creates a control point. * * @param position the control point position. * @param attributes the control point attributes. * @param id the control point ID. * @param purpose the control point purpose. * * @return the new control point. */ protected ControlPointMarker makeControlPoint(Position position, MarkerAttributes attributes, int id, String purpose) { return new ControlPointMarker(position, attributes, id, purpose); } /** * Creates a control point. * * @param position the control point position. * @param attributes the control point attributes. * @param id the control point ID. * @param leg the control point leg. * @param purpose the control point purpose. * * @return the new control point. */ protected ControlPointMarker makeControlPoint(Position position, MarkerAttributes attributes, int id, int leg, String purpose) { return new ControlPointMarker(position, attributes, id, leg, purpose); } /** * Computes the Cartesian difference between two control points. * * @param previousLocation the location nof the previous control point. * @param currentLocation the location of the current control point. * * @return the Cartesian difference between the two control points. */ protected Vec4 computeControlPointDelta(LatLon previousLocation, LatLon currentLocation) { // Compute how much the specified control point moved. Vec4 terrainPoint = getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(currentLocation); Vec4 previousPoint = getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(previousLocation); return terrainPoint.subtract3(previousPoint); } /** * Add a specified increment to an angle and normalize the result to be between 0 and 360 degrees. * * @param originalHeading the base angle. * @param deltaHeading the increment to add prior to normalizing. * * @return the normalized angle. */ protected Angle normalizedHeading(Angle originalHeading, Angle deltaHeading) { final double twoPI = 2 * Math.PI; double newHeading = originalHeading.getRadians() + deltaHeading.getRadians(); if (Math.abs(newHeading) > twoPI) newHeading = newHeading % twoPI; return Angle.fromRadians(newHeading >= 0 ? newHeading : newHeading + twoPI); } /** * Computes a control point location at the edge of a shape. * * @param center the shape's center. * @param location a location that forms a line from the shape's center along the shape's axis. The returned * location is on the edge indicated by the cross product of a vector normal to the surface at the * specified center and a vector from the center to this location. * @param length the distance of the edge from the shape's center. * * @return a location at the shape's edge at the same location along the shape's axis as the specified center * location. */ protected Position computeEdgeLocation(LatLon center, LatLon location, double length) { Vec4 centerPoint = getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(center); Vec4 surfaceNormal = getWwd().getModel().getGlobe().computeEllipsoidalNormalAtLocation( center.getLatitude(), center.getLongitude()); Vec4 point1 = getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(location); Vec4 vecToLocation = point1.subtract3(centerPoint).normalize3(); Vec4 vecToEdge = surfaceNormal.cross3(vecToLocation).normalize3().multiply3(length); LatLon edgeLocation = getWwd().getModel().getGlobe().computePositionFromEllipsoidalPoint( vecToEdge.add3(centerPoint)); double edgeAltitude = this.getControlPointAltitude(edgeLocation); return new Position(edgeLocation, edgeAltitude); } /** * Computes a control point location at the edge of a rectangular shape. * * @param begin the beginning of the shape's center. * @param end the end of the shape's center. * @param width the distance of the edge from the great circle arc between begin and end. * * @return a location centered along the edge parallel to the great circle arc between begin and end. */ protected Position computeRectangularEdgeLocation(LatLon begin, LatLon end, double width) { LatLon center = LatLon.interpolateGreatCircle(0.5, begin, end); Angle edgeAzimuth = LatLon.greatCircleAzimuth(center, end).add(Angle.POS90); Angle edgeLength = Angle.fromRadians(width / this.getWwd().getModel().getGlobe().getRadius()); LatLon edgeLocation = LatLon.greatCircleEndPosition(center, edgeAzimuth, edgeLength); double edgeAltitude = this.getControlPointAltitude(edgeLocation); return new Position(edgeLocation, edgeAltitude); } /** * Computes the point on a specified line segment that is nearest a specified point. * * @param p1 the line's first point. * @param p2 the line's second point. * @param point the point for which to determine a nearest point on the line segment. * * @return the nearest point on the line segment. */ protected Vec4 nearestPointOnSegment(Vec4 p1, Vec4 p2, Vec4 point) { Vec4 segment = p2.subtract3(p1); Vec4 dir = segment.normalize3(); double dot = point.subtract3(p1).dot3(dir); if (dot < 0.0) { return p1; } else if (dot > segment.getLength3()) { return p2; } else { return Vec4.fromLine3(p1, dot, dir); } } /** * Inserts the location nearest to a specified position on an edge of a specified list of locations into the * appropriate place in that list. * * @param terrainPosition the position to find a nearest point for. * @param altitude the altitude to use when determining the nearest point. Can be approximate and is not * necessarily the altitude of the terrain position. * @param locations the list of locations. This list is modified by this method to contain the new location on * an edge nearest the specified terrain position. * * @return the index at which the new location was inserted into the list. */ protected int addNearestLocation(Position terrainPosition, double altitude, List locations) { Globe globe = this.getWwd().getModel().getGlobe(); // Find the nearest edge to the picked point and insert a new position on that edge. Vec4 pointPicked = globe.computeEllipsoidalPointFromPosition(terrainPosition.getLatitude(), terrainPosition.getLongitude(), altitude); Vec4 nearestPoint = null; int nearestSegmentIndex = 0; double nearestDistance = Double.MAX_VALUE; for (int i = 1; i <= locations.size(); i++) // <= is intentional, to handle the closing segment { // Skip the closing segment if the shape is not a polygon. if (!(this.getShape() instanceof Polygon || this.getShape() instanceof SurfacePolygon) && i == locations.size()) continue; LatLon locationA = locations.get(i - 1); LatLon locationB = locations.get(i == locations.size() ? 0 : i); Vec4 pointA = globe.computeEllipsoidalPointFromPosition(locationA.getLatitude(), locationA.getLongitude(), altitude); Vec4 pointB = globe.computeEllipsoidalPointFromPosition(locationB.getLatitude(), locationB.getLongitude(), altitude); Vec4 pointOnEdge = this.nearestPointOnSegment(pointA, pointB, pointPicked); double distance = pointOnEdge.distanceTo3(pointPicked); if (distance < nearestDistance) { nearestPoint = pointOnEdge; nearestSegmentIndex = i; nearestDistance = distance; } } if (nearestPoint != null) { // Compute the location of the nearest point and add it to the shape. LatLon nearestLocation = globe.computePositionFromEllipsoidalPoint(nearestPoint); if (nearestSegmentIndex == locations.size()) locations.add(nearestLocation); else locations.add(nearestSegmentIndex, nearestLocation); this.getControlPointLayer().setMarkers(null); return nearestSegmentIndex; } return -1; } /** * Adds a location to either the beginning or the end of a specified list of locations. Which end to add to is * determined by a specified control point. * * @param controlPoint the control point of the shape's end. If the control point's ID is 0 the new location is * inserted to the beginning of the list. If the control point ID corresponds to the last * location in the list then the new location is appended to the list. Otherwise no operation * occurs. * @param locations the shape's locations. This list is modified upon return to include the new location. */ protected void appendLocation(ControlPointMarker controlPoint, List locations) { // Add a control point to the beginning or end of the shape. Globe globe = this.getWwd().getModel().getGlobe(); if (controlPoint.getId() == 0) // beginning of list { Vec4 pointA = globe.computeEllipsoidalPointFromLocation(locations.get(0)); Vec4 pointB = globe.computeEllipsoidalPointFromLocation(locations.get(1)); Vec4 newPoint = pointA.add3(pointA.subtract3(pointB).multiply3(0.1)); locations.add(0, globe.computePositionFromEllipsoidalPoint(newPoint)); } else if (controlPoint.getId() == locations.size() - 1) // end of list { Vec4 pointA = globe.computeEllipsoidalPointFromLocation(locations.get(locations.size() - 2)); Vec4 pointB = globe.computeEllipsoidalPointFromLocation(locations.get(locations.size() - 1)); Vec4 newPoint = pointB.add3(pointB.subtract3(pointA).multiply3(0.1)); locations.add(globe.computePositionFromEllipsoidalPoint(newPoint)); } } /** * Moves a control point location. * * @param controlPoint the control point being moved. * @param terrainPosition the position selected by the user. * @param locations the list of locations for the shape. */ protected void moveLocation(ControlPointMarker controlPoint, Position terrainPosition, List locations) { // Compute the new location for the polygon location associated with the incoming control point. Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); Vec4 markerPoint = getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( new Position(controlPoint.getPosition(), 0)); Position markerPosition = getWwd().getModel().getGlobe().computePositionFromEllipsoidalPoint( markerPoint.add3(delta)); // Update the polygon's locations. locations.set(controlPoint.getId(), markerPosition); } /** * Rotates a shape's locations. * * @param terrainPosition the position selected by the user. * @param locations the list of locations for the shape. */ protected void rotateLocations(Position terrainPosition, List locations) { // Rotate the positions. LatLon center = LatLon.getCenter(this.getWwd().getModel().getGlobe(), locations); // rotation axis Angle previousHeading = LatLon.greatCircleAzimuth(center, this.getPreviousPosition()); Angle deltaHeading = LatLon.greatCircleAzimuth(center, terrainPosition).subtract(previousHeading); this.currentHeading = this.normalizedHeading(this.getCurrentHeading(), deltaHeading); // Rotate the polygon's locations by the heading delta angle. for (int i = 0; i < locations.size(); i++) { LatLon location = locations.get(i); Angle heading = LatLon.greatCircleAzimuth(center, location); Angle distance = LatLon.greatCircleDistance(center, location); LatLon newLocation = LatLon.greatCircleEndPosition(center, heading.add(deltaHeading), distance); locations.set(i, newLocation); } } /** * Performs an edit for {@link gov.nasa.worldwind.render.airspaces.Polygon} shapes. * * @param controlPoint the control point selected. * @param terrainPosition the terrain position under the cursor. */ protected void reshapePolygonAirspace(Position terrainPosition, ControlPointMarker controlPoint) { Iterable currentLocations = null; if (this.getShape() instanceof Polygon) currentLocations = ((Polygon) this.getShape()).getLocations(); else if (this.getShape() instanceof Curtain) currentLocations = ((Curtain) this.getShape()).getLocations(); if (currentLocations == null) return; // Assemble a local copy of the polygon's locations. java.util.List locations = new ArrayList(); for (LatLon location : currentLocations) { locations.add(location); } if (controlPoint != null && controlPoint.getPurpose().equals(ROTATION)) { // Rotate the polygon. this.rotateLocations(terrainPosition, locations); } else if (controlPoint != null) // location change or add/delete control point { if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { int minSize = this.getShape() instanceof Polygon ? 3 : 2; if (locations.size() > minSize) { // Delete the control point. locations.remove(controlPoint.getId()); this.getControlPointLayer().setMarkers(null); } } else if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0 && this.isExtensionEnabled() && this.getShape() instanceof Curtain) { // Add a new control point. this.appendLocation(controlPoint, locations); this.getControlPointLayer().setMarkers(null); } else // control point location change { this.moveLocation(controlPoint, terrainPosition, locations); } } else if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { // Insert a new location along an edge of the polygon. double altitude = ((Airspace) this.getShape()).getAltitudes()[1]; this.addNearestLocation(terrainPosition, altitude, locations); } // Update the shape's locations. if (this.getShape() instanceof Polygon) ((Polygon) this.getShape()).setLocations(locations); else if (this.getShape() instanceof Curtain) ((Curtain) this.getShape()).setLocations(locations); } /** * Updates the control points and affordances for {@link gov.nasa.worldwind.render.airspaces.Polygon} shapes. */ protected void updatePolygonAirspaceControlPoints() { Iterable currentLocations = null; if (this.getShape() instanceof Polygon) currentLocations = ((Polygon) this.getShape()).getLocations(); else if (this.getShape() instanceof Curtain) currentLocations = ((Curtain) this.getShape()).getLocations(); if (currentLocations == null) return; java.util.List locations = new ArrayList(); for (LatLon location : currentLocations) { locations.add(location); } if (locations.size() < 2) return; Globe globe = this.getWwd().getModel().getGlobe(); LatLon polygonCenter = LatLon.getCenter(globe, locations); double centerAltitude = this.getControlPointAltitude(polygonCenter); // Compute the shape's heading and the rotation control location. Angle shapeRadius = LatLon.getAverageDistance(globe, polygonCenter, locations); shapeRadius = shapeRadius.multiply(1.2); Angle heading = this.getCurrentHeading(); LatLon rotationControlLocation = LatLon.greatCircleEndPosition(polygonCenter, heading, shapeRadius); double rotationControlAltitude = this.getControlPointAltitude(rotationControlLocation); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { // Create control points for the polygon locations. ArrayList controlPoints = new ArrayList(); int i = 0; for (LatLon location : locations) { double altitude = this.getControlPointAltitude(location); Position cpPosition = new Position(location, altitude); controlPoints.add( this.makeControlPoint(cpPosition, this.getLocationControlPointAttributes(), i++, LOCATION)); } // Create a control point for the rotation control. Position cpPosition = new Position(rotationControlLocation, rotationControlAltitude); controlPoints.add(this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), i, ROTATION)); this.getControlPointLayer().setMarkers(controlPoints); } else { // Update the polygon's location control points. Iterator markerIterator = markers.iterator(); for (LatLon location : locations) { double altitude = this.getControlPointAltitude(location); markerIterator.next().setPosition(new Position(location, altitude)); } // Update the polygon's rotation control point. markerIterator.next().setPosition(new Position(rotationControlLocation, rotationControlAltitude)); } // Update the heading annotation. Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); for (LatLon ignored : locations) { markerIterator.next(); } ((ControlPointMarker) markerIterator.next()).rotation = heading; // Update the rotation orientation line. this.updateOrientationLine(new Position(polygonCenter, centerAltitude), new Position(rotationControlLocation, rotationControlAltitude)); } /** * Performs an edit for {@link gov.nasa.worldwind.render.airspaces.CappedCylinder} shapes. * * @param controlPoint the control point selected. * @param terrainPosition the terrain position under the cursor. */ protected void reshapeCappedCylinder(Position terrainPosition, ControlPointMarker controlPoint) { if (controlPoint == null) return; // Cannot add locations to this shape. CappedCylinder cylinder = (CappedCylinder) this.getShape(); double[] radii = cylinder.getRadii(); Vec4 centerPoint = getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(cylinder.getCenter()); Vec4 markerPoint = getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( controlPoint.getPosition()); Vec4 vMarker = markerPoint.subtract3(centerPoint).normalize3(); Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); if (controlPoint.getPurpose().equals(OUTER_RADIUS)) radii[1] += delta.dot3(vMarker); else if (controlPoint.getPurpose().equals(INNER_RADIUS)) radii[0] += delta.dot3(vMarker); if (radii[0] >= 0 && radii[1] > 0 && radii[0] < radii[1]) cylinder.setRadii(radii[0], radii[1]); if (this.getShape() instanceof PartialCappedCylinder) { Angle oldHeading = LatLon.greatCircleAzimuth(cylinder.getCenter(), this.getPreviousPosition()); Angle deltaHeading = LatLon.greatCircleAzimuth(cylinder.getCenter(), terrainPosition).subtract(oldHeading); Angle[] azimuths = ((PartialCappedCylinder) cylinder).getAzimuths(); if (controlPoint.getPurpose().equals(LEFT_AZIMUTH)) azimuths[0] = this.normalizedHeading(azimuths[0], deltaHeading); else if (controlPoint.getPurpose().equals(RIGHT_AZIMUTH)) azimuths[1] = this.normalizedHeading(azimuths[1], deltaHeading); else if (controlPoint.getPurpose().equals(ROTATION)) { this.currentHeading = this.normalizedHeading(this.getCurrentHeading(), deltaHeading); azimuths[0] = this.normalizedHeading(azimuths[0], deltaHeading); azimuths[1] = this.normalizedHeading(azimuths[1], deltaHeading); } ((PartialCappedCylinder) cylinder).setAzimuths(azimuths[0], azimuths[1]); } } /** * Updates the control points and affordances for {@link gov.nasa.worldwind.render.airspaces.CappedCylinder} * shapes. */ protected void updateCappedCylinderControlPoints() { CappedCylinder cylinder = (CappedCylinder) this.getShape(); double[] radii = cylinder.getRadii(); boolean hasInnerRadius = radii[0] > 0; LatLon outerRadiusLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), Angle.fromDegrees(90), Angle.fromRadians(radii[1] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon innerRadiusLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), Angle.fromDegrees(90), Angle.fromRadians(radii[0] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); double outerRadiusAltitude = this.getControlPointAltitude(outerRadiusLocation); double innerRadiusAltitude = this.getControlPointAltitude(innerRadiusLocation); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { java.util.List markerList = new ArrayList(1); Position cpPosition = new Position(outerRadiusLocation, outerRadiusAltitude); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 0, OUTER_RADIUS)); if (hasInnerRadius) { cpPosition = new Position(innerRadiusLocation, innerRadiusAltitude); markerList.add( this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 1, INNER_RADIUS)); } this.getControlPointLayer().setMarkers(markerList); } else { Iterator markerIterator = markers.iterator(); markerIterator.next().setPosition(new Position(outerRadiusLocation, outerRadiusAltitude)); if (hasInnerRadius) markerIterator.next().setPosition(new Position(innerRadiusLocation, innerRadiusAltitude)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); ((ControlPointMarker) markerIterator.next()).size = radii[1]; if (hasInnerRadius) ((ControlPointMarker) markerIterator.next()).size = radii[0]; } /** * Updates the control points and affordances for {@link gov.nasa.worldwind.render.airspaces.PartialCappedCylinder} * shapes. */ protected void updatePartialCappedCylinderControlPoints() { PartialCappedCylinder cylinder = (PartialCappedCylinder) this.getShape(); double[] radii = cylinder.getRadii(); boolean hasInnerRadius = radii[0] > 0; double averageRadius = 0.5 * (radii[0] + radii[1]); Angle[] azimuths = cylinder.getAzimuths(); LatLon outerRadiusLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), azimuths[1], Angle.fromRadians(radii[1] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon innerRadiusLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), azimuths[1], Angle.fromRadians(radii[0] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon leftAzimuthLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), azimuths[0], Angle.fromRadians(averageRadius / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon rightAzimuthLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), azimuths[1], Angle.fromRadians(averageRadius / this.getWwd().getModel().getGlobe().getEquatorialRadius())); double outerRadiusAltitude = this.getControlPointAltitude(outerRadiusLocation); double innerRadiusAltitude = this.getControlPointAltitude(innerRadiusLocation); double rightAzimuthAltitude = this.getControlPointAltitude(rightAzimuthLocation); double leftAzimuthAltitude = this.getControlPointAltitude(leftAzimuthLocation); LatLon rotationControlLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), this.getCurrentHeading(), Angle.fromRadians(1.2 * radii[1] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); double rotationControlAltitude = this.getControlPointAltitude(rotationControlLocation); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { java.util.List markerList = new ArrayList(1); Position cpPosition = new Position(outerRadiusLocation, outerRadiusAltitude); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 0, OUTER_RADIUS)); if (hasInnerRadius) { cpPosition = new Position(innerRadiusLocation, innerRadiusAltitude); markerList.add( this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 1, INNER_RADIUS)); } cpPosition = new Position(leftAzimuthLocation, leftAzimuthAltitude); markerList.add( this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), 2, LEFT_AZIMUTH)); cpPosition = new Position(rightAzimuthLocation, rightAzimuthAltitude); markerList.add( this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), 3, RIGHT_AZIMUTH)); cpPosition = new Position(rotationControlLocation, rotationControlAltitude); markerList.add(this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), 4, ROTATION)); this.getControlPointLayer().setMarkers(markerList); } else { Iterator markerIterator = markers.iterator(); markerIterator.next().setPosition(new Position(outerRadiusLocation, outerRadiusAltitude)); if (hasInnerRadius) markerIterator.next().setPosition(new Position(innerRadiusLocation, rightAzimuthAltitude)); markerIterator.next().setPosition(new Position(leftAzimuthLocation, leftAzimuthAltitude)); markerIterator.next().setPosition(new Position(rightAzimuthLocation, rightAzimuthAltitude)); markerIterator.next().setPosition(new Position(rotationControlLocation, rotationControlAltitude)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); ((ControlPointMarker) markerIterator.next()).size = radii[1]; if (hasInnerRadius) ((ControlPointMarker) markerIterator.next()).size = radii[0]; ((ControlPointMarker) markerIterator.next()).rotation = azimuths[0]; ((ControlPointMarker) markerIterator.next()).rotation = azimuths[1]; ((ControlPointMarker) markerIterator.next()).rotation = this.getCurrentHeading(); // Update the rotation orientation line. double centerAltitude = this.getControlPointAltitude(cylinder.getCenter()); this.updateOrientationLine(new Position(cylinder.getCenter(), centerAltitude), new Position(rotationControlLocation, rotationControlAltitude)); } /** * Performs an edit for {@link gov.nasa.worldwind.render.airspaces.CappedCylinder} shapes. * * @param controlPoint the control point selected. * @param terrainPosition the terrain position under the cursor. */ protected void reshapeCappedEllipticalCylinder(Position terrainPosition, ControlPointMarker controlPoint) { if (controlPoint == null) return; // Cannot add locations to this shape. CappedEllipticalCylinder cylinder = (CappedEllipticalCylinder) this.getShape(); double[] radii = cylinder.getRadii(); Vec4 centerPoint = getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(cylinder.getCenter()); Vec4 markerPoint = getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( controlPoint.getPosition()); Vec4 vMarker = markerPoint.subtract3(centerPoint).normalize3(); Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); if (controlPoint.getPurpose().equals(INNER_MINOR_RADIUS)) radii[0] += delta.dot3(vMarker); else if (controlPoint.getPurpose().equals(INNER_MAJOR_RADIUS)) radii[1] += delta.dot3(vMarker); else if (controlPoint.getPurpose().equals(OUTER_MINOR_RADIUS)) radii[2] += delta.dot3(vMarker); else if (controlPoint.getPurpose().equals(OUTER_MAJOR_RADIUS)) radii[3] += delta.dot3(vMarker); else if (controlPoint.getPurpose().equals(ROTATION)) { Angle oldHeading = LatLon.greatCircleAzimuth(cylinder.getCenter(), this.getPreviousPosition()); Angle deltaHeading = LatLon.greatCircleAzimuth(cylinder.getCenter(), terrainPosition).subtract(oldHeading); cylinder.setHeading(this.normalizedHeading(cylinder.getHeading(), deltaHeading)); this.currentHeading = this.normalizedHeading(this.getCurrentHeading(), deltaHeading); } if (isRadiiValid(radii[0], radii[2]) && isRadiiValid(radii[1], radii[3])) cylinder.setRadii(radii[0], radii[1], radii[2], radii[3]); } protected static boolean isRadiiValid(double innerRadius, double outerRadius) { return innerRadius >= 0 && innerRadius < outerRadius; } /** * Updates the control points and affordances for {@link gov.nasa.worldwind.render.airspaces.CappedCylinder} * shapes. */ protected void updateCappedEllipticalCylinderControlPoints() { CappedEllipticalCylinder cylinder = (CappedEllipticalCylinder) this.getShape(); double[] radii = cylinder.getRadii(); boolean hasInnerRadius = radii[0] > 0 && radii[1] > 0; Angle heading = cylinder.getHeading(); LatLon innerMinorRadiusLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), Angle.fromDegrees(90).add(heading), Angle.fromRadians(radii[0] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon innerMajorRadiusLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), Angle.fromDegrees(0).add(heading), Angle.fromRadians(radii[1] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon outerMinorRadiusLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), Angle.fromDegrees(90).add(heading), Angle.fromRadians(radii[2] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon outerMajorRadiusLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), Angle.fromDegrees(0).add(heading), Angle.fromRadians(radii[3] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon rotationControlLocation = LatLon.greatCircleEndPosition(cylinder.getCenter(), this.getCurrentHeading(), Angle.fromRadians(1.4 * radii[3] / this.getWwd().getModel().getGlobe().getEquatorialRadius())); double rotationControlAltitude = this.getControlPointAltitude(rotationControlLocation); double innerMinorRadiusAltitude = this.getControlPointAltitude(innerMinorRadiusLocation); double innerMajorRadiusAltitude = this.getControlPointAltitude(innerMajorRadiusLocation); double outerMinorRadiusAltitude = this.getControlPointAltitude(outerMinorRadiusLocation); double outerMajorRadiusAltitude = this.getControlPointAltitude(outerMajorRadiusLocation); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { java.util.List markerList = new ArrayList(2); Position cpPosition = new Position(outerMinorRadiusLocation, outerMinorRadiusAltitude); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 2, OUTER_MINOR_RADIUS)); cpPosition = new Position(outerMajorRadiusLocation, outerMajorRadiusAltitude); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 3, OUTER_MAJOR_RADIUS)); if (hasInnerRadius) { cpPosition = new Position(innerMinorRadiusLocation, innerMinorRadiusAltitude); markerList.add( this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 0, INNER_MINOR_RADIUS)); cpPosition = new Position(innerMajorRadiusLocation, innerMajorRadiusAltitude); markerList.add( this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 1, INNER_MAJOR_RADIUS)); } cpPosition = new Position(rotationControlLocation, rotationControlAltitude); markerList.add(this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), 4, ROTATION)); this.getControlPointLayer().setMarkers(markerList); } else { Iterator markerIterator = markers.iterator(); markerIterator.next().setPosition(new Position(outerMinorRadiusLocation, outerMinorRadiusAltitude)); markerIterator.next().setPosition(new Position(outerMajorRadiusLocation, outerMajorRadiusAltitude)); if (hasInnerRadius) { markerIterator.next().setPosition(new Position(innerMinorRadiusLocation, innerMinorRadiusAltitude)); markerIterator.next().setPosition(new Position(innerMajorRadiusLocation, innerMajorRadiusAltitude)); } markerIterator.next().setPosition(new Position(rotationControlLocation, rotationControlAltitude)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); ((ControlPointMarker) markerIterator.next()).size = radii[2]; ((ControlPointMarker) markerIterator.next()).size = radii[3]; if (hasInnerRadius) { ((ControlPointMarker) markerIterator.next()).size = radii[0]; ((ControlPointMarker) markerIterator.next()).size = radii[1]; } ((ControlPointMarker) markerIterator.next()).rotation = this.getCurrentHeading(); // Update the rotation orientation line. double centerAltitude = this.getControlPointAltitude(cylinder.getCenter()); this.updateOrientationLine(new Position(cylinder.getCenter(), centerAltitude), new Position(rotationControlLocation, rotationControlAltitude)); } /** * Performs an edit for {@link gov.nasa.worldwind.render.airspaces.SphereAirspace} shapes. * * @param controlPoint the control point selected. * @param terrainPosition the terrain position under the cursor. */ protected void reshapeSphere(Position terrainPosition, ControlPointMarker controlPoint) { if (controlPoint == null) return; // Cannot add locations to this shape. SphereAirspace sphere = (SphereAirspace) this.getShape(); double radius = sphere.getRadius(); Vec4 centerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( sphere.getLocation()); Vec4 markerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( controlPoint.getPosition()); Vec4 vMarker = markerPoint.subtract3(centerPoint).normalize3(); Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); if (controlPoint.getPurpose().equals(OUTER_RADIUS)) radius += delta.dot3(vMarker); if (radius > 0) sphere.setRadius(radius); } /** * Updates the control points and affordances for {@link gov.nasa.worldwind.render.airspaces.SphereAirspace} * shapes. */ protected void updateSphereControlPoints() { SphereAirspace sphere = (SphereAirspace) this.getShape(); double radius = sphere.getRadius(); LatLon radiusLocation = LatLon.greatCircleEndPosition(sphere.getLocation(), Angle.fromDegrees(90), Angle.fromRadians(radius / this.getWwd().getModel().getGlobe().getEquatorialRadius())); double radiusAltitude = this.getControlPointAltitude(radiusLocation); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { java.util.List markerList = new ArrayList(1); Position cpPosition = new Position(radiusLocation, radiusAltitude); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 0, OUTER_RADIUS)); this.getControlPointLayer().setMarkers(markerList); } else { Iterator markerIterator = markers.iterator(); markerIterator.next().setPosition(new Position(radiusLocation, radiusAltitude)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); ((ControlPointMarker) markerIterator.next()).size = radius; } /** * Performs an edit for {@link gov.nasa.worldwind.render.airspaces.Orbit} shapes. * * @param controlPoint the control point selected. * @param terrainPosition the terrain position under the cursor. */ protected void reshapeOrbit(Position terrainPosition, ControlPointMarker controlPoint) { if (controlPoint == null) return; // Cannot add locations to this shape. Orbit orbit = (Orbit) this.getShape(); LatLon[] locations = orbit.getLocations(); double width = orbit.getWidth(); LatLon center = LatLon.interpolateGreatCircle(0.5, locations[0], locations[1]); Vec4 centerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(center); Vec4 markerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( new Position(controlPoint.getPosition(), 0)); if (controlPoint.getPurpose().equals(RIGHT_WIDTH)) { Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); Vec4 vMarker = markerPoint.subtract3(centerPoint).normalize3(); double newWidth = width + delta.dot3(vMarker); if (newWidth > 0) orbit.setWidth(width + delta.dot3(vMarker)); } else if (controlPoint.getPurpose().equals(ROTATION)) { Angle oldHeading = LatLon.greatCircleAzimuth(center, this.getPreviousPosition()); Angle deltaHeading = LatLon.greatCircleAzimuth(center, terrainPosition).subtract(oldHeading); for (int i = 0; i < 2; i++) { Angle heading = LatLon.greatCircleAzimuth(center, locations[i]); Angle distance = LatLon.greatCircleDistance(center, locations[i]); locations[i] = LatLon.greatCircleEndPosition(center, heading.add(deltaHeading), distance); } orbit.setLocations(locations[0], locations[1]); } else // location change { Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); Position markerPosition = this.getWwd().getModel().getGlobe().computePositionFromEllipsoidalPoint( markerPoint.add3(delta)); locations[controlPoint.getId()] = markerPosition; orbit.setLocations(locations[0], locations[1]); } } /** * Updates the control points and affordances for {@link gov.nasa.worldwind.render.airspaces.Orbit} shapes. */ protected void updateOrbitControlPoints() { Orbit orbit = (Orbit) this.getShape(); LatLon[] locations = orbit.getLocations(); double width = orbit.getWidth(); double location0Altitude = this.getControlPointAltitude(locations[0]); double location1Altitude = this.getControlPointAltitude(locations[1]); Angle orbitHeading = LatLon.greatCircleAzimuth(locations[0], locations[1]); LatLon center = LatLon.interpolateGreatCircle(0.5, locations[0], locations[1]); double centerAltitude = this.getControlPointAltitude(center); Position widthPosition = this.computeRectangularEdgeLocation(locations[0], locations[1], 0.5 * width); Globe globe = this.getWwd().getModel().getGlobe(); Vec4 centerPoint = globe.computeEllipsoidalPointFromLocation(center); Vec4 point0 = globe.computeEllipsoidalPointFromLocation(locations[1]); Vec4 vec = point0.subtract3(centerPoint); vec = vec.multiply3(1 + width / vec.getLength3()); LatLon rotationControlLocation = globe.computePositionFromEllipsoidalPoint(vec.add3(centerPoint)); double rotationControlAltitude = this.getControlPointAltitude(rotationControlLocation); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { java.util.List markerList = new ArrayList(1); Position cpPosition = new Position(locations[0], location0Altitude); markerList.add(this.makeControlPoint(cpPosition, this.getLocationControlPointAttributes(), 0, LOCATION)); cpPosition = new Position(locations[1], location1Altitude); markerList.add(this.makeControlPoint(cpPosition, this.getLocationControlPointAttributes(), 1, LOCATION)); cpPosition = new Position(widthPosition, widthPosition.getAltitude()); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 2, RIGHT_WIDTH)); cpPosition = new Position(rotationControlLocation, rotationControlAltitude); markerList.add(this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), 3, ROTATION)); this.getControlPointLayer().setMarkers(markerList); } else { Iterator markerIterator = markers.iterator(); markerIterator.next().setPosition(new Position(locations[0], location0Altitude)); markerIterator.next().setPosition(new Position(locations[1], location1Altitude)); markerIterator.next().setPosition(new Position(widthPosition, widthPosition.getAltitude())); markerIterator.next().setPosition(new Position(rotationControlLocation, rotationControlAltitude)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); markerIterator.next(); markerIterator.next(); ((ControlPointMarker) markerIterator.next()).size = width; ((ControlPointMarker) markerIterator.next()).rotation = this.normalizedHeading(orbitHeading, Angle.ZERO); this.updateOrientationLine(new Position(center, centerAltitude), new Position(rotationControlLocation, rotationControlAltitude)); } /** * Performs an edit for {@link gov.nasa.worldwind.render.airspaces.Route} shapes. * * @param controlPoint the control point selected. * @param terrainPosition the terrain position under the cursor. */ protected void reshapeRoute(Position terrainPosition, ControlPointMarker controlPoint) { Route route = (Route) this.getShape(); java.util.List locations = new ArrayList(); for (LatLon ll : route.getLocations()) { locations.add(ll); } if (controlPoint != null && controlPoint.getPurpose().equals(ROTATION)) { this.rotateLocations(terrainPosition, locations); route.setLocations(locations); } else if (controlPoint != null && (controlPoint.getPurpose().equals(LEFT_WIDTH) || controlPoint.getPurpose().equals(RIGHT_WIDTH))) { LatLon legCenter = LatLon.interpolateGreatCircle(0.5, locations.get(0), locations.get(1)); Vec4 centerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(legCenter); Vec4 markerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( new Position(controlPoint.getPosition(), 0)); Vec4 vMarker = markerPoint.subtract3(centerPoint).normalize3(); Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); double newWidth = route.getWidth() + delta.dot3(vMarker); if (newWidth >= 0) route.setWidth(newWidth); } else if (controlPoint != null) // location change or add/delete control point { if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { if (locations.size() > 2) { // Delete the control point. locations.remove(controlPoint.getId()); this.getControlPointLayer().setMarkers(null); } } else if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { this.appendLocation(controlPoint, locations); this.getControlPointLayer().setMarkers(null); } else // control point location change { this.moveLocation(controlPoint, terrainPosition, locations); } route.setLocations(locations); } else if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { // Insert a new position into the shape. double altitude = ((Airspace) this.getShape()).getAltitudes()[1]; this.addNearestLocation(terrainPosition, altitude, locations); route.setLocations(locations); } } /** * Updates the control points and affordances for {@link gov.nasa.worldwind.render.airspaces.Route} shapes. */ protected void updateRouteControlPoints() { Route route = (Route) this.getShape(); if (route.getLocations() == null) return; java.util.List locations = new ArrayList(); for (LatLon location : route.getLocations()) { locations.add(location); } if (locations.size() < 2) return; Globe globe = this.getWwd().getModel().getGlobe(); double width = route.getWidth(); Position leftWidthPosition = this.computeRectangularEdgeLocation(locations.get(0), locations.get(1), -0.5 * width); Position rightWidthPosition = this.computeRectangularEdgeLocation(locations.get(0), locations.get(1), 0.5 * width); LatLon routeCenter = LatLon.getCenter(globe, locations); double centerAltitude = this.getControlPointAltitude(routeCenter); // Compute the shape's heading and the rotation control location. Angle shapeRadius = LatLon.greatCircleDistance(routeCenter, locations.get(1)); shapeRadius = shapeRadius.add(Angle.fromRadians(route.getWidth() / globe.getEquatorialRadius())); Angle heading = this.getCurrentHeading(); LatLon rotationControlLocation = LatLon.greatCircleEndPosition(routeCenter, heading, shapeRadius); double rotationControlAltitude = this.getControlPointAltitude(rotationControlLocation); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { ArrayList controlPoints = new ArrayList(); int i = 0; for (LatLon cpPosition : locations) { double altitude = this.getControlPointAltitude(cpPosition); Position position = new Position(cpPosition, altitude); controlPoints.add( this.makeControlPoint(position, this.getLocationControlPointAttributes(), i++, LOCATION)); } Position position = new Position(leftWidthPosition, leftWidthPosition.getAltitude()); controlPoints.add(this.makeControlPoint(position, this.getSizeControlPointAttributes(), i++, RIGHT_WIDTH)); position = new Position(rightWidthPosition, rightWidthPosition.getAltitude()); controlPoints.add(this.makeControlPoint(position, this.getSizeControlPointAttributes(), i++, LEFT_WIDTH)); position = new Position(rotationControlLocation, rotationControlAltitude); controlPoints.add(this.makeControlPoint(position, this.getAngleControlPointAttributes(), i, ROTATION)); this.getControlPointLayer().setMarkers(controlPoints); } else { Iterator markerIterator = markers.iterator(); for (LatLon cpPosition : locations) { double altitude = this.getControlPointAltitude(cpPosition); markerIterator.next().setPosition(new Position(cpPosition, altitude)); } markerIterator.next().setPosition(new Position(leftWidthPosition, leftWidthPosition.getAltitude())); markerIterator.next().setPosition(new Position(rightWidthPosition, rightWidthPosition.getAltitude())); markerIterator.next().setPosition(new Position(rotationControlLocation, rotationControlAltitude)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); for (LatLon ignored : locations) // skip over the locations to get to the width and rotation control points { markerIterator.next(); } ((ControlPointMarker) markerIterator.next()).size = route.getWidth(); ((ControlPointMarker) markerIterator.next()).size = route.getWidth(); ((ControlPointMarker) markerIterator.next()).rotation = heading; this.updateOrientationLine(new Position(routeCenter, centerAltitude), new Position(rotationControlLocation, rotationControlAltitude)); } /** * Performs an edit for {@link gov.nasa.worldwind.render.airspaces.TrackAirspace} shapes. * * @param controlPoint the control point selected. * @param terrainPosition the terrain position under the cursor. */ protected void reshapeTrack(Position terrainPosition, ControlPointMarker controlPoint) { TrackAirspace track = (TrackAirspace) this.getShape(); List legs = track.getLegs(); if (controlPoint != null && controlPoint.getPurpose().equals(ROTATION)) { List trackLocations = new ArrayList(); for (Box leg : legs) { trackLocations.add(leg.getLocations()[0]); trackLocations.add(leg.getLocations()[1]); } LatLon center = LatLon.getCenter(this.getWwd().getModel().getGlobe(), trackLocations); Angle previousHeading = LatLon.greatCircleAzimuth(center, this.getPreviousPosition()); Angle deltaHeading = LatLon.greatCircleAzimuth(center, terrainPosition).subtract(previousHeading); this.currentHeading = this.normalizedHeading(this.getCurrentHeading(), deltaHeading); // Rotate all the legs. for (Box leg : legs) { LatLon[] locations = leg.getLocations(); Angle heading = LatLon.greatCircleAzimuth(center, locations[0]); Angle distance = LatLon.greatCircleDistance(center, locations[0]); locations[0] = LatLon.greatCircleEndPosition(center, heading.add(deltaHeading), distance); heading = LatLon.greatCircleAzimuth(center, locations[1]); distance = LatLon.greatCircleDistance(center, locations[1]); locations[1] = LatLon.greatCircleEndPosition(center, heading.add(deltaHeading), distance); leg.setLocations(locations[0], locations[1]); } track.setLegs(new ArrayList(track.getLegs())); } else if (controlPoint != null && (controlPoint.getPurpose().equals(LEFT_WIDTH) || controlPoint.getPurpose().equals(RIGHT_WIDTH))) { Box leg = legs.get(controlPoint.getLeg()); LatLon[] legLocations = leg.getLocations(); LatLon legCenter = LatLon.interpolateGreatCircle(0.5, legLocations[0], legLocations[1]); Vec4 centerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(legCenter); Vec4 markerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( new Position(controlPoint.getPosition(), 0)); Vec4 vMarker = markerPoint.subtract3(centerPoint).normalize3(); double[] widths = leg.getWidths(); double[] newWidths = new double[] {widths[0], widths[1]}; Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); if (controlPoint.getPurpose().equals(LEFT_WIDTH)) newWidths[0] += delta.dot3(vMarker); else newWidths[1] += delta.dot3(vMarker); if (newWidths[0] >= 0 && newWidths[1] >= 0) { leg.setWidths(newWidths[0], newWidths[1]); } track.setLegs(new ArrayList(track.getLegs())); } else if (controlPoint != null) { // Make a modifiable copy of the legs list. legs = new ArrayList(legs); if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { // Remove a control point. if (legs.size() < 2) // Can't remove a control point from a single-leg track. return; if (controlPoint.getLeg() == 0 && controlPoint.getId() == 0) { legs.remove(0); } else if (controlPoint.getLeg() == legs.size() - 1 && controlPoint.getId() == 1) { legs.remove(legs.size() - 1); } else { if (controlPoint.getLeg() == 0) // need to treat the second control point of leg 0 specially { legs.get(0).setLocations(legs.get(0).getLocations()[0], legs.get(1).getLocations()[1]); legs.remove(1); } else // remove an internal control point { Box leftLeg = controlPoint.getLeg() == 0 ? legs.get(0) : legs.get(controlPoint.getLeg() - 1); Box rightLeg = legs.get(controlPoint.getLeg() + 1); rightLeg.setLocations(leftLeg.getLocations()[1], rightLeg.getLocations()[1]); legs.remove(controlPoint.getLeg()); } } track.setLegs(legs); this.determineTrackAdjacency(); this.getControlPointLayer().setMarkers(null); } else if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { // Append a location to the beginning or end of the track. Globe globe = this.getWwd().getModel().getGlobe(); if (controlPoint.getLeg() == 0 && controlPoint.getId() == 0) // first control point { Vec4 pointA = globe.computeEllipsoidalPointFromLocation(legs.get(0).getLocations()[0]); Vec4 pointB = globe.computeEllipsoidalPointFromLocation(legs.get(0).getLocations()[1]); Vec4 newPoint = pointA.add3(pointA.subtract3(pointB).multiply3(0.1)); LatLon newLocation = globe.computePositionFromEllipsoidalPoint(newPoint); Box newLeg = new Box(legs.get(0)); newLeg.setLocations(newLocation, legs.get(0).getLocations()[0]); legs.add(0, newLeg); } else if (controlPoint.getLeg() == legs.size() - 1 && controlPoint.getId() == 1) // last control point { Box lastLeg = legs.get(legs.size() - 1); Vec4 pointA = globe.computeEllipsoidalPointFromLocation(lastLeg.getLocations()[1]); Vec4 pointB = globe.computeEllipsoidalPointFromLocation(lastLeg.getLocations()[0]); Vec4 newPoint = pointA.add3(pointA.subtract3(pointB).multiply3(0.1)); LatLon newLocation = globe.computePositionFromEllipsoidalPoint(newPoint); Box newLeg = new Box(lastLeg); newLeg.setLocations(lastLeg.getLocations()[1], newLocation); legs.add(newLeg); } else { return; // the point is internal rather than at the end } track.setLegs(legs); this.determineTrackAdjacency(); this.getControlPointLayer().setMarkers(null); } else // control point location change { Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); Vec4 markerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( new Position(controlPoint.getPosition(), 0)); Position markerPosition = this.getWwd().getModel().getGlobe().computePositionFromEllipsoidalPoint( markerPoint.add3(delta)); Box leg = track.getLegs().get(controlPoint.getLeg()); if (controlPoint.getId() == 0) leg.setLocations(markerPosition, leg.getLocations()[1]); else leg.setLocations(leg.getLocations()[0], markerPosition); track.setLegs(new ArrayList(track.getLegs())); } } else if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { // Make a modifiable copy of the legs list. legs = new ArrayList(legs); List locations = new ArrayList(); for (Box leg : legs) { locations.add(leg.getLocations()[0]); } locations.add(legs.get(legs.size() - 1).getLocations()[1]); // add the last point of the last leg. int segmentIndex = this.addNearestLocation(terrainPosition, track.getAltitudes()[1], locations); LatLon newLocation = locations.get(segmentIndex); int legIndex = segmentIndex - 1; Box leg = legs.get(legIndex); Box newLeg = new Box(leg); if (legIndex > 0) { newLeg.setLocations(leg.getLocations()[0], newLocation); leg.setLocations(newLocation, leg.getLocations()[1]); legs.add(legIndex, newLeg); } else { newLeg.setLocations(newLocation, leg.getLocations()[1]); leg.setLocations(leg.getLocations()[0], newLocation); legs.add(1, newLeg); } track.setLegs(new ArrayList(legs)); this.determineTrackAdjacency(); this.getControlPointLayer().setMarkers(null); } } /** * Updates the control points and affordances for {@link gov.nasa.worldwind.render.airspaces.TrackAirspace} shapes. */ protected void updateTrackControlPoints() { TrackAirspace track = (TrackAirspace) this.getShape(); List legs = track.getLegs(); if (legs == null) return; // Update the location control points. ArrayList controlPoints = new ArrayList(); Iterable markers = this.getControlPointLayer().getMarkers(); Iterator markerIterator = markers != null ? markers.iterator() : null; for (int i = 0; i < legs.size(); i++) { Box leg = legs.get(i); LatLon[] legLocations = leg.getLocations(); double altitude; if (markers == null) { if (!this.trackAdjacencyList.contains(leg)) { altitude = this.getControlPointAltitude(legLocations[0]); ControlPointMarker cp = this.makeControlPoint(new Position(legLocations[0], altitude), this.getLocationControlPointAttributes(), 0, i, LOCATION); controlPoints.add(cp); } altitude = this.getControlPointAltitude(legLocations[1]); ControlPointMarker cp = this.makeControlPoint(new Position(legLocations[1], altitude), this.getLocationControlPointAttributes(), 1, i, LOCATION); controlPoints.add(cp); } else { if (!this.trackAdjacencyList.contains(leg)) { altitude = this.getControlPointAltitude(legLocations[0]); markerIterator.next().setPosition(new Position(legLocations[0], altitude)); } altitude = this.getControlPointAltitude(legLocations[1]); markerIterator.next().setPosition(new Position(legLocations[1], altitude)); } } // Update the width control points. for (int i = 0; i < legs.size(); i++) { Box leg = legs.get(i); LatLon[] legLocations = leg.getLocations(); double[] widths = leg.getWidths(); Position cwLPosition = this.computeRectangularEdgeLocation(legLocations[0], legLocations[1], -widths[0]); Position cwRPosition = this.computeRectangularEdgeLocation(legLocations[0], legLocations[1], widths[1]); if (markers == null) { Position cpPosition = new Position(cwLPosition, cwLPosition.getAltitude()); controlPoints.add( this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 2, i, LEFT_WIDTH)); cpPosition = new Position(cwRPosition, cwRPosition.getAltitude()); controlPoints.add( this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 3, i, RIGHT_WIDTH)); } else { //noinspection ConstantConditions markerIterator.next().setPosition(new Position(cwLPosition, cwLPosition.getAltitude())); markerIterator.next().setPosition(new Position(cwRPosition, cwRPosition.getAltitude())); } } // Update the rotation control points. List trackLocations = new ArrayList(); for (Box leg : legs) { trackLocations.add(leg.getLocations()[0]); trackLocations.add(leg.getLocations()[1]); } Globe globe = this.getWwd().getModel().getGlobe(); LatLon trackCenter = LatLon.getCenter(globe, trackLocations); double trackCenterAltitude = this.getControlPointAltitude(trackCenter); Angle trackRadius = LatLon.getAverageDistance(globe, trackCenter, trackLocations); double[] widths = legs.get(0).getWidths(); trackRadius = trackRadius.addRadians((widths[0] + widths[1]) / globe.getEquatorialRadius()); Angle heading = this.getCurrentHeading(); LatLon rotationLocation = LatLon.greatCircleEndPosition(trackCenter, heading, trackRadius); double rotationAltitude = this.getControlPointAltitude(rotationLocation); if (markers == null) { Position cpPosition = new Position(rotationLocation, rotationAltitude); controlPoints.add(this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), 4, ROTATION)); } else { //noinspection ConstantConditions markerIterator.next().setPosition(new Position(rotationLocation, rotationAltitude)); } if (markers == null) this.getControlPointLayer().setMarkers(controlPoints); this.updateOrientationLine(new Position(trackCenter, trackCenterAltitude), new Position(rotationLocation, rotationAltitude)); markers = this.getControlPointLayer().getMarkers(); for (Marker marker : markers) { ControlPointMarker cp = (ControlPointMarker) marker; if (cp.getId() == 2) cp.size = legs.get(cp.getLeg()).getWidths()[0]; else if (cp.getId() == 3) cp.size = legs.get(cp.getLeg()).getWidths()[1]; else if (cp.getId() == 4) cp.rotation = heading; } } protected void reshapeSurfacePolygon(Position terrainPosition, ControlPointMarker controlPoint) { Iterable corners = this.getShape() instanceof SurfacePolygon ? ((SurfacePolygon) this.getShape()).getLocations() : ((SurfacePolyline) this.getShape()).getLocations(); java.util.List locations = new ArrayList(); for (LatLon ll : corners) { locations.add(ll); } if (controlPoint != null && controlPoint.getPurpose().equals(ROTATION)) { // Rotate the polygon. this.rotateLocations(terrainPosition, locations); } else if (controlPoint != null) // control point location change or add/delete a control point { if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { int minSize = this.getShape() instanceof SurfacePolygon ? 3 : 2; if (locations.size() > minSize) { // Delete the control point. locations.remove(controlPoint.getId()); this.getControlPointLayer().setMarkers(null); } } else if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0 && this.isExtensionEnabled() && this.getShape() instanceof SurfacePolyline) { this.appendLocation(controlPoint, locations); this.getControlPointLayer().setMarkers(null); } else // location change { this.moveLocation(controlPoint, terrainPosition, locations); } } else if ((this.getCurrentEvent().getMouseEvent().getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0 && this.isExtensionEnabled()) { this.addNearestLocation(terrainPosition, 0, locations); } if (this.getShape() instanceof SurfacePolygon) ((SurfacePolygon) this.getShape()).setLocations(locations); else ((SurfacePolyline) this.getShape()).setLocations(locations); } protected void updateSurfacePolygonControlPoints() { Iterable locationsIterable = null; if (this.getShape() instanceof SurfacePolygon) locationsIterable = ((SurfacePolygon) this.getShape()).getLocations(); else if (this.getShape() instanceof SurfacePolyline) locationsIterable = ((SurfacePolyline) this.getShape()).getLocations(); if (locationsIterable == null) return; java.util.List locations = new ArrayList(); for (LatLon location : locationsIterable) { locations.add(location); } if (locations.size() < 2) return; Globe globe = this.getWwd().getModel().getGlobe(); LatLon polygonCenter = LatLon.getCenter(globe, locations); Angle shapeRadius = LatLon.getAverageDistance(globe, polygonCenter, locations); shapeRadius = shapeRadius.multiply(1.2); Angle heading = this.getCurrentHeading(); LatLon rotationControlLocation = LatLon.greatCircleEndPosition(polygonCenter, heading, shapeRadius); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { ArrayList controlPoints = new ArrayList(); int i = 0; for (LatLon corner : locations) { Position cpPosition = new Position(corner, 0); controlPoints.add( this.makeControlPoint(cpPosition, this.getLocationControlPointAttributes(), i++, LOCATION)); } // Create a control point for the rotation control. Position cpPosition = new Position(rotationControlLocation, 0); controlPoints.add(this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), i, ROTATION)); this.getControlPointLayer().setMarkers(controlPoints); } else { Iterator markerIterator = markers.iterator(); for (LatLon cpPosition : locations) { markerIterator.next().setPosition(new Position(cpPosition, 0)); } // Update the polygon's rotation control point. markerIterator.next().setPosition(new Position(rotationControlLocation, 0)); } // Update the heading annotation. Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); for (LatLon ignored : locations) { markerIterator.next(); } ((ControlPointMarker) markerIterator.next()).rotation = heading; // Update the rotation orientation line. this.updateOrientationLine(new Position(polygonCenter, 0), new Position(rotationControlLocation, 0)); } protected void reshapeSurfaceCircle(Position terrainPosition, ControlPointMarker controlPoint) { if (controlPoint == null) return; // Cannot add locations to this shape. SurfaceCircle circle = (SurfaceCircle) this.getShape(); Vec4 delta = this.computeControlPointDelta(this.getPreviousPosition(), terrainPosition); Vec4 centerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(circle.getCenter()); Vec4 markerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( controlPoint.getPosition()); Vec4 vMarker = markerPoint.subtract3(centerPoint).normalize3(); double radius = circle.getRadius() + delta.dot3(vMarker); if (radius > 0) circle.setRadius(radius); } protected void updateSurfaceCircleControlPoints() { SurfaceCircle circle = (SurfaceCircle) this.getShape(); LatLon radiusLocation = LatLon.greatCircleEndPosition(circle.getCenter(), Angle.fromDegrees(90), Angle.fromRadians(circle.getRadius() / this.getWwd().getModel().getGlobe().getEquatorialRadius())); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { java.util.List markerList = new ArrayList(1); Position cpPosition = new Position(radiusLocation, 0); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 0, OUTER_RADIUS)); this.getControlPointLayer().setMarkers(markerList); } else { markers.iterator().next().setPosition(new Position(radiusLocation, 0)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); ((ControlPointMarker) markerIterator.next()).size = circle.getRadius(); } protected void reshapeSurfaceSquare(Position terrainPosition, ControlPointMarker controlPoint) { if (controlPoint == null) return; // Cannot add locations to this shape. SurfaceSquare square = (SurfaceSquare) this.getShape(); Vec4 terrainPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(terrainPosition); Vec4 previousPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( this.getPreviousPosition()); Vec4 delta = terrainPoint.subtract3(previousPoint); Vec4 centerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(square.getCenter()); Vec4 markerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( controlPoint.getPosition()); Vec4 vMarker = markerPoint.subtract3(centerPoint); if (controlPoint.getPurpose().equals(RIGHT_WIDTH)) { double size = square.getSize() + delta.dot3(vMarker.normalize3()); if (size > 0) square.setSize(size); } else // rotation { Angle oldHeading = LatLon.greatCircleAzimuth(square.getCenter(), this.getPreviousPosition()); Angle deltaHeading = LatLon.greatCircleAzimuth(square.getCenter(), terrainPosition).subtract(oldHeading); square.setHeading(this.normalizedHeading(square.getHeading(), deltaHeading)); } } protected void updateSurfaceSquareControlPoints() { SurfaceSquare square = (SurfaceSquare) this.getShape(); LatLon sizeLocation = LatLon.greatCircleEndPosition(square.getCenter(), Angle.fromDegrees(90 + square.getHeading().degrees), Angle.fromRadians(0.5 * square.getSize() / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon rotationLocation = LatLon.greatCircleEndPosition(square.getCenter(), Angle.fromDegrees(square.getHeading().degrees), Angle.fromRadians(0.7 * square.getSize() / this.getWwd().getModel().getGlobe().getEquatorialRadius())); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { java.util.List markerList = new ArrayList(1); Position cpPosition = new Position(sizeLocation, 0); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 0, RIGHT_WIDTH)); cpPosition = new Position(rotationLocation, 0); markerList.add(this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), 1, ROTATION)); this.getControlPointLayer().setMarkers(markerList); } else { Iterator markerIterator = markers.iterator(); markerIterator.next().setPosition(new Position(sizeLocation, 0)); markerIterator.next().setPosition(new Position(rotationLocation, 0)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); ((ControlPointMarker) markerIterator.next()).size = square.getSize(); ((ControlPointMarker) markerIterator.next()).rotation = square.getHeading(); this.updateOrientationLine(new Position(square.getCenter(), 0), new Position(rotationLocation, 0)); } protected void reshapeSurfaceQuad(Position terrainPosition, ControlPointMarker controlPoint) { if (controlPoint == null) return; // Cannot add locations to this shape. SurfaceQuad quad = (SurfaceQuad) this.getShape(); Vec4 terrainPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(terrainPosition); Vec4 previousPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( this.getPreviousPosition()); Vec4 delta = terrainPoint.subtract3(previousPoint); Vec4 centerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(quad.getCenter()); Vec4 markerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( controlPoint.getPosition()); Vec4 vMarker = markerPoint.subtract3(centerPoint).normalize3(); if (controlPoint.getPurpose().equals(WIDTH) || controlPoint.getPurpose().equals(HEIGHT)) { double width = quad.getWidth() + (controlPoint.getId() == 0 ? delta.dot3(vMarker) : 0); double height = quad.getHeight() + (controlPoint.getId() == 1 ? delta.dot3(vMarker) : 0); if (width > 0 && height > 0) quad.setSize(width, height); } else { Angle oldHeading = LatLon.greatCircleAzimuth(quad.getCenter(), this.getPreviousPosition()); Angle deltaHeading = LatLon.greatCircleAzimuth(quad.getCenter(), terrainPosition).subtract(oldHeading); quad.setHeading(this.normalizedHeading(quad.getHeading(), deltaHeading)); } } protected void updateSurfaceQuadControlPoints() { SurfaceQuad quad = (SurfaceQuad) this.getShape(); LatLon widthLocation = LatLon.greatCircleEndPosition(quad.getCenter(), Angle.fromDegrees(90 + quad.getHeading().degrees), Angle.fromRadians(0.5 * quad.getWidth() / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon heightLocation = LatLon.greatCircleEndPosition(quad.getCenter(), Angle.fromDegrees(quad.getHeading().degrees), Angle.fromRadians(0.5 * quad.getHeight() / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon rotationLocation = LatLon.greatCircleEndPosition(quad.getCenter(), Angle.fromDegrees(quad.getHeading().degrees), Angle.fromRadians(0.7 * quad.getHeight() / this.getWwd().getModel().getGlobe().getEquatorialRadius())); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { java.util.List markerList = new ArrayList(2); Position cpPosition = new Position(widthLocation, 0); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 0, WIDTH)); cpPosition = new Position(heightLocation, 0); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 1, HEIGHT)); cpPosition = new Position(rotationLocation, 0); markerList.add(this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), 2, ROTATION)); this.getControlPointLayer().setMarkers(markerList); } else { Iterator markerIterator = markers.iterator(); markerIterator.next().setPosition(new Position(widthLocation, 0)); markerIterator.next().setPosition(new Position(heightLocation, 0)); markerIterator.next().setPosition(new Position(rotationLocation, 0)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); ((ControlPointMarker) markerIterator.next()).size = quad.getWidth(); ((ControlPointMarker) markerIterator.next()).size = quad.getHeight(); ((ControlPointMarker) markerIterator.next()).rotation = quad.getHeading(); this.updateOrientationLine(new Position(quad.getCenter(), 0), new Position(rotationLocation, 0)); } protected void reshapeSurfaceEllipse(Position terrainPosition, ControlPointMarker controlPoint) { if (controlPoint == null) return; // Cannot add locations to this shape. SurfaceEllipse ellipse = (SurfaceEllipse) this.getShape(); Vec4 terrainPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(terrainPosition); Vec4 previousPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( this.getPreviousPosition()); Vec4 delta = terrainPoint.subtract3(previousPoint); Vec4 centerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation(ellipse.getCenter()); Vec4 markerPoint = this.getWwd().getModel().getGlobe().computeEllipsoidalPointFromLocation( controlPoint.getPosition()); Vec4 vMarker = markerPoint.subtract3(centerPoint).normalize3(); if (controlPoint.getPurpose().equals(WIDTH) || controlPoint.getPurpose().equals(HEIGHT)) { double majorRadius = ellipse.getMajorRadius() + (controlPoint.getId() == 0 ? delta.dot3(vMarker) : 0); double minorRadius = ellipse.getMinorRadius() + (controlPoint.getId() == 1 ? delta.dot3(vMarker) : 0); if (majorRadius > 0 && minorRadius > 0) ellipse.setRadii(majorRadius, minorRadius); } else { Angle oldHeading = LatLon.greatCircleAzimuth(ellipse.getCenter(), this.getPreviousPosition()); Angle deltaHeading = LatLon.greatCircleAzimuth(ellipse.getCenter(), terrainPosition).subtract(oldHeading); ellipse.setHeading(this.normalizedHeading(ellipse.getHeading(), deltaHeading)); } this.updateAnnotation(controlPoint); } protected void updateSurfaceEllipseControlPoints() { SurfaceEllipse ellipse = (SurfaceEllipse) this.getShape(); LatLon majorLocation = LatLon.greatCircleEndPosition(ellipse.getCenter(), Angle.fromDegrees(90 + ellipse.getHeading().degrees), Angle.fromRadians(ellipse.getMajorRadius() / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon minorLocation = LatLon.greatCircleEndPosition(ellipse.getCenter(), Angle.fromDegrees(ellipse.getHeading().degrees), Angle.fromRadians(ellipse.getMinorRadius() / this.getWwd().getModel().getGlobe().getEquatorialRadius())); LatLon rotationLocation = LatLon.greatCircleEndPosition(ellipse.getCenter(), Angle.fromDegrees(ellipse.getHeading().degrees), Angle.fromRadians( 1.15 * ellipse.getMinorRadius() / this.getWwd().getModel().getGlobe().getEquatorialRadius())); Iterable markers = this.getControlPointLayer().getMarkers(); if (markers == null) { java.util.List markerList = new ArrayList(2); Position cpPosition = new Position(majorLocation, 0); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 0, WIDTH)); this.getControlPointLayer().setMarkers(markerList); cpPosition = new Position(minorLocation, 0); markerList.add(this.makeControlPoint(cpPosition, this.getSizeControlPointAttributes(), 1, HEIGHT)); cpPosition = new Position(rotationLocation, 0); markerList.add(this.makeControlPoint(cpPosition, this.getAngleControlPointAttributes(), 2, ROTATION)); this.getControlPointLayer().setMarkers(markerList); } else { Iterator markerIterator = markers.iterator(); markerIterator.next().setPosition(new Position(majorLocation, 0)); markerIterator.next().setPosition(new Position(minorLocation, 0)); markerIterator.next().setPosition(new Position(rotationLocation, 0)); } Iterator markerIterator = this.getControlPointLayer().getMarkers().iterator(); ((ControlPointMarker) markerIterator.next()).size = ellipse.getMajorRadius(); ((ControlPointMarker) markerIterator.next()).size = ellipse.getMinorRadius(); ((ControlPointMarker) markerIterator.next()).rotation = ellipse.getHeading(); this.updateOrientationLine(new Position(ellipse.getCenter(), 0), new Position(rotationLocation, 0)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy