/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.geoforge.worldwind.builder;
import gov.nasa.worldwind.Movable;
import gov.nasa.worldwind.View;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.awt.WorldWindowGLCanvas;
import gov.nasa.worldwind.event.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.EllipsoidalGlobe;
import gov.nasa.worldwind.layers.LayerList;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.pick.PickedObject;
import gov.nasa.worldwind.pick.PickedObjectList;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.Logging;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
/**
*
* @author bantchao@gmail.com
*
* based on wwdw's SectorSelector.java
*/
public class GfrBldObjDrgShpSct extends GfrBldObjAbs implements
SelectListener,
MouseMotionListener,
RenderingListener
{
final static public String STR_SECTOR_PROPERTY = "gov.nasa.worldwind.SectorSelector";
final static private int _INT_NONE_ = 0;
final static private int _INT_MOVING_ = 1;
final static private int _INT_SIZING_ = 2;
final static private int _INT_NORTH_ = 1;
final static private int _INT_SOUTH_ = 2;
final static private int _INT_EAST_ = 4;
final static private int _INT_WEST_ = 8;
final static private int _INT_NORTHWEST_ = _INT_NORTH_ + _INT_WEST_;
final static private int _INT_NORTHEAST_ = _INT_NORTH_ + _INT_EAST_;
final static private int _INT_SOUTHWEST_ = _INT_SOUTH_ + _INT_WEST_;
final static private int _INT_SOUTHEAST_ = _INT_SOUTH_ + _INT_EAST_;
static private double _s_getValueAbslute_(double a)
{
return a >= 0 ? a : -a;
}
// ---
private /*final*/ RegionShape _rgeShape_ = null;
private double _dblEdgeFactor_ = 0.10;
// state tracking fields
private boolean _blnIsArmed_ = false;
private int _intOperation_ = _INT_NONE_;
private int _intSide_ = _INT_NONE_;
private Position _posPrevious_ = null;
private Sector previousSector = null;
@Override
public void setReady(boolean bln)
{
if (bln)
_enable_();
else
_disable_();
}
@Override
public Object getValue()
{
Sector sec = this._getSector_();
if (sec == null)
return (ArrayList<Point2D.Double>) null;
Angle angMinLat = sec.getMinLatitude();
Angle angMinLon = sec.getMinLongitude();
Angle angMaxLat = sec.getMaxLatitude();
Angle angMaxLon = sec.getMaxLongitude();
LatLon llnMin = new LatLon(angMinLat, angMinLon);
LatLon llnMax = new LatLon(angMaxLat, angMaxLon);
ArrayList<LatLon> lstSecMinMax = new ArrayList<LatLon>();
lstSecMinMax.add(llnMin);
lstSecMinMax.add(llnMax);
return lstSecMinMax;
}
public GfrBldObjDrgShpSct(
WorldWindowGLCanvas glcWwd,
PropertyChangeListener lstPropertyChange
)
{
super(glcWwd);
// ---
this._rgeShape_ = new RegionShape();
((RenderableLayer) this._rlr).addRenderable(this._rgeShape_);
if (lstPropertyChange != null)
super.addPropertyChangeListener(lstPropertyChange);
}
@Override
public boolean init()
{
return true;
}
//
// Selection events are used to resize and move the region
//
@Override
public void selected(SelectEvent evtSelect)
{
if (evtSelect == null)
{
String msg = Logging.getMessage("nullValue.EventIsNull");
Logging.logger().log(java.util.logging.Level.FINE, msg);
throw new IllegalArgumentException(msg);
}
if (this._intOperation_ == _INT_NONE_ &&
evtSelect.getTopObject() != null &&
!(evtSelect.getTopPickedObject().getParentLayer() == this._rlr)
)
{
this._setCursor_(null);
return;
}
if (evtSelect.getEventAction().equals(SelectEvent.LEFT_PRESS))
{
this._posPrevious_ = super._glcWwd.getCurrentPosition();
}
else if (evtSelect.getEventAction().equals(SelectEvent.DRAG))
{
DragSelectEvent dragEvent = (DragSelectEvent) evtSelect;
Object topObject = dragEvent.getTopObject();
if (topObject == null)
return;
RegionShape dragObject = this._rgeShape_;
if (this._intOperation_ == _INT_SIZING_)
{
Sector newSector = this._resizeShape_(dragObject, this._intSide_);
if (newSector != null)
dragObject.setSector(newSector);
evtSelect.consume();
}
else
{
this._intSide_ = this._determineAdjustmentSide_(dragObject, this._dblEdgeFactor_);
if (this._intSide_ == _INT_NONE_ || this._intOperation_ == _INT_MOVING_)
{
this._intOperation_ = _INT_MOVING_;
this._dragWholeShape_(dragEvent, dragObject);
}
else
{
Sector newSector = this._resizeShape_(dragObject, this._intSide_);
if (newSector != null)
dragObject.setSector(newSector);
this._intOperation_ = _INT_SIZING_;
}
evtSelect.consume();
}
this._posPrevious_ = super._glcWwd.getCurrentPosition();
this._notifySectorChanged_();
}
else if (evtSelect.getEventAction().equals(SelectEvent.DRAG_END))
{
this._intOperation_ = _INT_NONE_;
this._posPrevious_ = null;
}
else if (evtSelect.getEventAction().equals(SelectEvent.ROLLOVER) && this._intOperation_ == _INT_NONE_)
{
if (!(super._glcWwd instanceof Component))
return;
if (evtSelect.getTopObject() == null || evtSelect.getTopPickedObject().isTerrain())
{
this._setCursor_(null);
return;
}
if (!(evtSelect.getTopObject() instanceof Movable))
return;
this._setCursor_(this._determineAdjustmentSide_((Movable) evtSelect.getTopObject(), this._dblEdgeFactor_));
}
}
@Override
public void mousePressed(MouseEvent evtMouse)
{
if (MouseEvent.BUTTON1_DOWN_MASK != evtMouse.getModifiersEx())
return;
if (! this._blnIsArmed_)
return;
this._rgeShape_.setResizeable(true);
this._rgeShape_.setStartPosition(null);
this._blnIsArmed_ = false;
evtMouse.consume();
}
@Override
public void mouseReleased(MouseEvent evtMouse)
{
if (MouseEvent.BUTTON1 != evtMouse.getButton())
return;
if (this._rgeShape_.isResizeable())
this._setCursor_(null);
this._rgeShape_.setResizeable(false);
evtMouse.consume(); // prevent view operations
this.firePropertyChange(STR_SECTOR_PROPERTY, this.previousSector, null);
}
@Override
public void mouseDragged(MouseEvent evtMouse)
{
if (MouseEvent.BUTTON1_DOWN_MASK != evtMouse.getModifiersEx())
return;
if (this._rgeShape_.isResizeable())
evtMouse.consume(); // prevent view operations
}
@Override
public void mouseMoved(MouseEvent e) {}
@Override
public void stageChanged(RenderingEvent evtRendering)
{
if (!evtRendering.getStage().equals(RenderingEvent.AFTER_BUFFER_SWAP))
return;
// We notify of changes during this rendering stage because the sector is updated within the region shape's
// render method.
this._notifySectorChanged_();
}
private void _setCursor_(Cursor cursor)
{
((Component) super._glcWwd).setCursor(cursor != null ? cursor : Cursor.getDefaultCursor());
}
private Sector _getSector_()
{
return this._rgeShape_.hasSelection() ? this._rgeShape_.getSector() : null;
// TODO: Determine how to handle date-line spanning sectors.
}
private void _enable_()
{
this._rgeShape_.setStartPosition(null);
LayerList layers = super._glcWwd.getModel().getLayers();
if (!layers.contains(super._rlr))
layers.add(super._rlr);
if (! super._rlr.isEnabled())
super._rlr.setEnabled(true);
this._blnIsArmed_ = true;
super._glcWwd.addRenderingListener(this);
super._glcWwd.addSelectListener(this);
super._glcWwd.getInputHandler().addMouseListener(this);
super._glcWwd.getInputHandler().addMouseMotionListener(this);
this._setCursor_(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}
private void _disable_()
{
super._glcWwd.removeRenderingListener(this);
super._glcWwd.removeSelectListener(this);
super._glcWwd.getInputHandler().removeMouseListener(this);
super._glcWwd.getInputHandler().removeMouseMotionListener(this);
super._glcWwd.getModel().getLayers().remove(super._rlr);
this._rgeShape_.clear();
}
private Sector _resizeShape_(Movable dragObject, int side)
{
if (dragObject instanceof SurfaceSector)
{
SurfaceSector quad = (SurfaceSector) dragObject;
Sector s = quad.getSector(); // TODO: go over all sectors
Position p = super._glcWwd.getCurrentPosition();
if (p == null || this._posPrevious_ == null)
{
return null;
}
Angle dLat = p.getLatitude().subtract(this._posPrevious_.getLatitude());
Angle dLon = p.getLongitude().subtract(this._posPrevious_.getLongitude());
Angle newMinLat = s.getMinLatitude();
Angle newMinLon = s.getMinLongitude();
Angle newMaxLat = s.getMaxLatitude();
Angle newMaxLon = s.getMaxLongitude();
if (side == _INT_NORTH_)
{
newMaxLat = s.getMaxLatitude().add(dLat);
}
else if (side == _INT_SOUTH_)
{
newMinLat = s.getMinLatitude().add(dLat);
}
else if (side == _INT_EAST_)
{
newMaxLon = s.getMaxLongitude().add(dLon);
}
else if (side == _INT_WEST_)
{
newMinLon = s.getMinLongitude().add(dLon);
}
else if (side == _INT_NORTHWEST_)
{
newMaxLat = s.getMaxLatitude().add(dLat);
newMinLon = s.getMinLongitude().add(dLon);
}
else if (side == _INT_NORTHEAST_)
{
newMaxLat = s.getMaxLatitude().add(dLat);
newMaxLon = s.getMaxLongitude().add(dLon);
}
else if (side == _INT_SOUTHWEST_)
{
newMinLat = s.getMinLatitude().add(dLat);
newMinLon = s.getMinLongitude().add(dLon);
}
else if (side == _INT_SOUTHEAST_)
{
newMinLat = s.getMinLatitude().add(dLat);
newMaxLon = s.getMaxLongitude().add(dLon);
}
return new Sector(newMinLat, newMaxLat, newMinLon, newMaxLon);
}
return null;
}
private int _determineAdjustmentSide_(Movable dragObject, double factor)
{
if (dragObject instanceof SurfaceSector)
{
SurfaceSector quad = (SurfaceSector) dragObject;
Sector s = quad.getSector(); // TODO: go over all sectors
Position p = super._glcWwd.getCurrentPosition();
if (p == null)
{
return _INT_NONE_;
}
double dN = _s_getValueAbslute_(s.getMaxLatitude().subtract(p.getLatitude()).degrees);
double dS = _s_getValueAbslute_(s.getMinLatitude().subtract(p.getLatitude()).degrees);
double dW = _s_getValueAbslute_(s.getMinLongitude().subtract(p.getLongitude()).degrees);
double dE = _s_getValueAbslute_(s.getMaxLongitude().subtract(p.getLongitude()).degrees);
double sLat = factor * s.getDeltaLatDegrees();
double sLon = factor * s.getDeltaLonDegrees();
if (dN < sLat && dW < sLon)
return _INT_NORTHWEST_;
if (dN < sLat && dE < sLon)
return _INT_NORTHEAST_;
if (dS < sLat && dW < sLon)
return _INT_SOUTHWEST_;
if (dS < sLat && dE < sLon)
return _INT_SOUTHEAST_;
if (dN < sLat)
return _INT_NORTH_;
if (dS < sLat)
return _INT_SOUTH_;
if (dW < sLon)
return _INT_WEST_;
if (dE < sLon)
return _INT_EAST_;
}
return _INT_NONE_;
}
private void _dragWholeShape_(DragSelectEvent dragEvent, Movable dragObject)
{
View view = super._glcWwd.getView();
EllipsoidalGlobe globe = (EllipsoidalGlobe) super._glcWwd.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(p);
}
}
private void _setCursor_(int sideName)
{
Cursor cursor = null;
switch (sideName)
{
case _INT_NONE_:
cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
break;
case _INT_NORTH_:
cursor = Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR);
break;
case _INT_SOUTH_:
cursor = Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
break;
case _INT_EAST_:
cursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
break;
case _INT_WEST_:
cursor = Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR);
break;
case _INT_NORTHWEST_:
cursor = Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR);
break;
case _INT_NORTHEAST_:
cursor = Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR);
break;
case _INT_SOUTHWEST_:
cursor = Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR);
break;
case _INT_SOUTHEAST_:
cursor = Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR);
break;
}
this._setCursor_(cursor);
}
private void _notifySectorChanged_()
{
if (this._rgeShape_.hasSelection() &&
this._getSector_() != null &&
!this._getSector_().equals(this.previousSector))
{
this.firePropertyChange(STR_SECTOR_PROPERTY, this.previousSector, this._rgeShape_.getSector());
this.previousSector = this._getSector_();
}
}
// beg inner-class
protected static class RegionShape extends SurfaceSector
{
private boolean resizeable = false;
private Position startPosition;
private Position endPosition;
private SurfaceSector borderShape;
protected RegionShape()
{
super(Sector.EMPTY_SECTOR);
// Create the default border shape.
this.setBorder(new SurfaceSector(sector));
// The edges of the region shape should be constant lines of latitude and longitude.
this.setPathType(AVKey.LINEAR);
this.getBorder().setPathType(AVKey.LINEAR);
// Setup default interior rendering attributes. Note that the interior rendering attributes are
// configured so only the SurfaceSector's interior is rendered.
ShapeAttributes interiorAttrs = new BasicShapeAttributes();
interiorAttrs.setDrawOutline(false);
interiorAttrs.setInteriorMaterial(new Material(Color.WHITE));
interiorAttrs.setInteriorOpacity(0.1);
this.setAttributes(interiorAttrs);
this.setHighlightAttributes(interiorAttrs);
// Setup default border rendering attributes. Note that the border rendering attributes are configured
// so that only the SurfaceSector's outline is rendered.
ShapeAttributes borderAttrs = new BasicShapeAttributes();
borderAttrs.setDrawInterior(false);
borderAttrs.setOutlineMaterial(new Material(Color.RED));
borderAttrs.setOutlineOpacity(0.7);
borderAttrs.setOutlineWidth(3);
this.getBorder().setAttributes(borderAttrs);
this.getBorder().setHighlightAttributes(borderAttrs);
// ---
Color colSInteriorShape = new Color(1f, 1f, 1f, 0.1f);
setInteriorColor(colSInteriorShape);
Color colBorderShape = new Color(1f, 0f, 0f, 0.5f);
setBorderColor(colBorderShape);
double dblWidthShape = 3d;
setBorderWidth(dblWidthShape);
}
public Color getInteriorColor()
{
return this.getAttributes().getInteriorMaterial().getDiffuse();
}
public void setInteriorColor(Color color)
{
ShapeAttributes attr = this.getAttributes();
attr.setInteriorMaterial(new Material(color));
this.setAttributes(attr);
}
public Color getBorderColor()
{
return this.getBorder().getAttributes().getOutlineMaterial().getDiffuse();
}
public void setBorderColor(Color color)
{
ShapeAttributes attr = this.getBorder().getAttributes();
attr.setOutlineMaterial(new Material(color));
this.getBorder().setAttributes(attr);
}
public double getInteriorOpacity()
{
return this.getAttributes().getInteriorOpacity();
}
public void setInteriorOpacity(double opacity)
{
ShapeAttributes attr = this.getAttributes();
attr.setInteriorOpacity(opacity);
this.setAttributes(attr);
}
public double getBorderOpacity()
{
return this.getBorder().getAttributes().getOutlineOpacity();
}
public void setBorderOpacity(double opacity)
{
ShapeAttributes attr = this.getBorder().getAttributes();
attr.setOutlineOpacity(opacity);
this.getBorder().setAttributes(attr);
}
public double getBorderWidth()
{
return this.getBorder().getAttributes().getOutlineWidth();
}
public void setBorderWidth(double width)
{
ShapeAttributes attr = this.getBorder().getAttributes();
attr.setOutlineWidth(width);
this.getBorder().setAttributes(attr);
}
@Override
public void setSector(Sector secNew)
{
super.setSector(secNew);
this.getBorder().setSector(secNew);
}
protected boolean isResizeable()
{
return resizeable;
}
protected void setResizeable(boolean resizeable)
{
this.resizeable = resizeable;
}
protected Position getStartPosition()
{
return startPosition;
}
protected void setStartPosition(Position startPosition)
{
this.startPosition = startPosition;
}
protected Position getEndPosition()
{
return endPosition;
}
protected void setEndPosition(Position endPosition)
{
this.endPosition = endPosition;
}
protected SurfaceSector getBorder()
{
return borderShape;
}
protected void setBorder(SurfaceSector shape)
{
if (shape == null)
{
String message = Logging.getMessage("nullValue.Shape");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.borderShape = shape;
}
protected boolean hasSelection()
{
return getStartPosition() != null && getEndPosition() != null;
}
protected void clear()
{
this.setStartPosition(null);
this.setEndPosition(null);
this.setSector(Sector.EMPTY_SECTOR);
}
@Override
public void preRender(DrawContext dc)
{
// This is called twice: once during normal rendering, then again during ordered surface rendering. During
// normal renering we pre-render both the interior and border shapes. During ordered surface rendering, both
// shapes are already added to the DrawContext and both will be individually processed. Therefore we just
// call our superclass behavior
if (dc.isOrderedRenderingMode())
{
super.preRender(dc);
return;
}
this.doPreRender(dc);
}
@Override
public void render(DrawContext dc)
{
if (dc.isPickingMode() && this.isResizeable())
return;
// This is called twice: once during normal rendering, then again during ordered surface rendering. During
// normal renering we render both the interior and border shapes. During ordered surface rendering, both
// shapes are already added to the DrawContext and both will be individually processed. Therefore we just
// call our superclass behavior
if (dc.isOrderedRenderingMode())
{
super.render(dc);
return;
}
if (!this.isResizeable())
{
if (this.hasSelection())
{
this.doRender(dc);
}
return;
}
PickedObjectList pos = dc.getPickedObjects();
PickedObject terrainObject = pos != null ? pos.getTerrainObject() : null;
if (terrainObject == null)
return;
if (this.getStartPosition() != null)
{
Position end = terrainObject.getPosition();
if (!this.getStartPosition().equals(end))
{
this.setEndPosition(end);
this.setSector(Sector.boundingSector(this.getStartPosition(), this.getEndPosition()));
this.doRender(dc);
}
}
else
{
this.setStartPosition(pos.getTerrainObject().getPosition());
}
}
protected void doPreRender(DrawContext dc)
{
this.doPreRenderInterior(dc);
this.doPreRenderBorder(dc);
}
protected void doPreRenderInterior(DrawContext dc)
{
super.preRender(dc);
}
protected void doPreRenderBorder(DrawContext dc)
{
this.getBorder().preRender(dc);
}
protected void doRender(DrawContext dc)
{
this.doRenderInterior(dc);
this.doRenderBorder(dc);
}
protected void doRenderInterior(DrawContext dc)
{
super.render(dc);
}
protected void doRenderBorder(DrawContext dc)
{
this.getBorder().render(dc);
}
}
// end inner-class
}