/* Copyright (C) 2001, 2011 United States Government as represented by
the Administrator of the National Aeronautics and Space Administration.
All Rights Reserved.
*/
package org.geoforge.worldwind.builder.editor;
import gov.nasa.worldwind.View;
import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.geom.LatLon;
import gov.nasa.worldwind.geom.Line;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.geom.Vec4;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.render.Path;
import gov.nasa.worldwind.render.airspaces.editor.AirspaceEditorUtil;
import gov.nasa.worldwind.terrain.Terrain;
import java.awt.Point;
import java.util.ArrayList;
import org.geoforge.worldwind.builder.marker.GfrMrkMoveAbs;
import org.geoforge.worldwind.builder.marker.GfrMrkMoveHoriz;
/**
* @author bantchao
*
* Based on WWD's ExtrudedPolygonEditor.java
* excluded inner-classes
*/
public class GfrEditorObjShpPlnOpn extends GfrEditorObjShpPlnAbs
{
public GfrEditorObjShpPlnOpn()
{
super();
}
@Override
protected void _assembleVertexControlPoints_(DrawContext dc)
{
Terrain terrain = dc.getTerrain();
Path pol = (Path) super._pol_;
Position refPos = pol.getReferencePosition();
Vec4 refPoint = terrain.getSurfacePoint(refPos.getLatitude(), refPos.getLongitude(), 0);
int altitudeMode = pol.getAltitudeMode();
double height = 1000; // pol.getHeight();
Vec4 vaa = null;
double vaaLength = 0; // used to compute independent length of each cap vertex
double vaLength = 0;
int i = 0;
for (LatLon location : pol.getPositions())
{
Vec4 vert;
// Compute the top/cap point.
if (altitudeMode == WorldWind.CONSTANT || !(location instanceof Position))
{
if (vaa == null)
{
// Compute the vector lengths of the top and bottom points at the reference position.
vaa = refPoint.multiply3(height / refPoint.getLength3());
vaaLength = vaa.getLength3();
vaLength = refPoint.getLength3();
}
// Compute the bottom point, which is on the terrain.
vert = terrain.getSurfacePoint(location.getLatitude(), location.getLongitude(), 0);
double delta = vaLength - vert.dot3(refPoint) / vaLength;
vert = vert.add3(vaa.multiply3(1d + delta / vaaLength));
}
else if (altitudeMode == WorldWind.RELATIVE_TO_GROUND)
{
vert = terrain.getSurfacePoint(location.getLatitude(), location.getLongitude(),
((Position) location).getAltitude());
}
else // WorldWind.ABSOLUTE
{
vert = terrain.getGlobe().computePointFromPosition(location.getLatitude(), location.getLongitude(),
((Position) location).getAltitude() * terrain.getVerticalExaggeration());
}
Position vertexPosition = super._wwd.getModel().getGlobe().computePositionFromPoint(vert);
GfrMrkMoveHoriz pnt = new GfrMrkMoveHoriz(vertexPosition, vert,
this._bmaControlVertex, i);
this._lstMarkerControlPoints.add(pnt);
i++;
}
}
//*************************************************************
// ***************** Polygon manipulation *********************
//*************************************************************
@Override
protected void moveControlPoint(GfrMrkMoveHoriz controlPoint, Point lastMousePoint, Point moveToPoint)
{
View view = super._wwd.getView();
Globe globe = super._wwd.getModel().getGlobe();
Position refPos = controlPoint.getPosition();
if (refPos == null)
return;
Line ray = view.computeRayFromScreenPoint(moveToPoint.getX(), moveToPoint.getY());
Line previousRay = view.computeRayFromScreenPoint(lastMousePoint.getX(), lastMousePoint.getY());
Vec4 vec = AirspaceEditorUtil.intersectGlobeAt(super._wwd, refPos.getElevation(), ray);
Vec4 previousVec = AirspaceEditorUtil.intersectGlobeAt(super._wwd, refPos.getElevation(), previousRay);
if (vec == null || previousVec == null)
{
return;
}
Position pos = globe.computePositionFromPoint(vec);
Position previousPos = globe.computePositionFromPoint(previousVec);
LatLon change = pos.subtract(previousPos);
java.util.List<LatLon> boundary = new ArrayList<LatLon>();
for (LatLon ll : ((Path) this._pol_).getPositions())
{
boundary.add(ll);
}
int intIndexControlPoint = controlPoint.getIndex();
/*
* There is a bug here :
* refPos.getAltitude() gives the altitude of the control node :
* e.g.
* 3250m in a mountain,
* -4232m in the ocean.
* But the posNew, while rendering is threated the elevation of the
* control node ABOVE the elevation of the ground level.
*
* Nice example of this bug between France and Italy with a Path
* from the Alps to Ligurian Sea.
* The control node in the mountain increases its altitude everytime it is moved,
* while the control node in the sea decreases its altitude.
*
* => possible fix : hard code a fix value instead of refPos.getAltitude()
* use the value of refPos.getAltitude() as a fix altitude, and not the
* height above ground level.
*/
Position posNew = new Position(pos.add(change), refPos.getAltitude());
boundary.set(intIndexControlPoint, posNew);
// Path ensures that the last boundary position is the same as the first. Remove the last point
// before setting the boundary.
java.util.List<Position> boundary2 = new ArrayList<Position>();
for (LatLon ll : boundary)
{
boundary2.add(new Position(ll, ((Position) ll).getElevation()));
}
((Path) this._pol_).setPositions(boundary2);
}
@Override
protected void setHeightPolyline(Point previousMousePoint, Point mousePoint)
{
// Find the closest points between the rays through each screen point, and the ray from the control point
// and in the direction of the globe's surface normal. Compute the elevation difference between these two
// points, and use that as the change in polygon height.
Position referencePos = this._pol_.getReferencePosition();
if (referencePos == null)
return;
Vec4 referencePoint = super._wwd.getModel().getGlobe().computePointFromPosition(referencePos);
Vec4 surfaceNormal = super._wwd.getModel().getGlobe().computeSurfaceNormalAtLocation(referencePos.getLatitude(),
referencePos.getLongitude());
Line verticalRay = new Line(referencePoint, surfaceNormal);
Line screenRay = super._wwd.getView().computeRayFromScreenPoint(mousePoint.getX(), mousePoint.getY());
Line previousScreenRay = super._wwd.getView().computeRayFromScreenPoint(previousMousePoint.getX(),
previousMousePoint.getY());
Vec4 pointOnLine = AirspaceEditorUtil.nearestPointOnLine(verticalRay, screenRay);
Vec4 previousPointOnLine = AirspaceEditorUtil.nearestPointOnLine(verticalRay, previousScreenRay);
Position pos = super._wwd.getModel().getGlobe().computePositionFromPoint(pointOnLine);
Position previousPos = super._wwd.getModel().getGlobe().computePositionFromPoint(previousPointOnLine);
double elevationChange = pos.getElevation() - previousPos.getElevation();
java.util.List<Position> boundary = new ArrayList<Position>();
for (LatLon ll : ((Path) this._pol_).getPositions())
{
boundary.add(new Position(ll, ((Position) ll).getElevation() + elevationChange));
}
((Path) this._pol_).setPositions(boundary);
}
/**
* Add a vertex to the polygon's outer boundary.
*
* @param _pntMouse the point at which the mouse was clicked. The new vertex will be placed as near as possible to
* this point, at the elevation of the polygon.
*/
@Override
protected void addVertex(Point mousePoint)
{
// Try to find the edge that is closest to a ray passing through the screen point. We're trying to determine
// the user's intent as to which edge a new two control points should be added to.
Line ray = super._wwd.getView().computeRayFromScreenPoint(mousePoint.getX(), mousePoint.getY());
Vec4 pickPoint = super._intersectPolygonAltitudeAt(ray);
double nearestDistance = Double.MAX_VALUE;
int newVertexIndex = 0;
// Loop through the control points and determine which edge is closest to the pick point
for (int i = 0; i < this._lstMarkerControlPoints.size()-1; i++)
{
GfrMrkMoveAbs thisMarker = (GfrMrkMoveAbs) this._lstMarkerControlPoints.get(i);
GfrMrkMoveAbs nextMarker = (GfrMrkMoveAbs) this._lstMarkerControlPoints.get(
(i + 1) % this._lstMarkerControlPoints.size());
Vec4 pointOnEdge = AirspaceEditorUtil.nearestPointOnSegment(thisMarker.getPoint(), nextMarker.getPoint(), pickPoint);
if (!AirspaceEditorUtil.isPointBehindLineOrigin(ray, pointOnEdge))
{
double d = pointOnEdge.distanceTo3(pickPoint);
if (d < nearestDistance)
{
newVertexIndex = i + 1;
nearestDistance = d;
}
}
}
Position newPosition = super._wwd.getModel().getGlobe().computePositionFromPoint(pickPoint);
// Copy the outer boundary list
ArrayList<Position> positionList = new ArrayList<Position>(this._lstMarkerControlPoints.size());
for (LatLon position : ((Path) super._pol_).getPositions())
{
positionList.add((Position) position);
}
// Add the new vertex
try
{
positionList.add(newVertexIndex, newPosition);
}
catch(Exception exc)
{
exc.printStackTrace();
return;
}
((Path) super._pol_).setPositions(positionList);
}
/**
* Remove a vertex from the polygon.
*
* @param vertexToRemove the vertex to remove.
*/
@Override
protected boolean removeVertex(GfrMrkMoveHoriz vertexToRemove)
{
if (this._lstMarkerControlPoints.size() == 3) // 2 + 1 for height control
{
System.out.println("this._lstMarkerControlPoints_.size() == 3, preventing remove");
return false;
}
Path polygon = (Path) super._pol_;
ArrayList<LatLon> locations = new ArrayList<LatLon>(this._lstMarkerControlPoints.size() - 1);
for (LatLon latLon : polygon.getPositions())
{
locations.add(latLon);
}
locations.remove(vertexToRemove.getIndex());
//polygon.setPositions(locations);
java.util.List<Position> boundary2 = new ArrayList<Position>();
for (LatLon ll : locations)
{
boundary2.add(new Position(ll, ((Position) ll).getElevation()));
}
polygon.setPositions(boundary2);
return true;
}
}