src.gov.nasa.worldwind.render.airspaces.TrackAirspace Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwindx Show documentation
Show all versions of worldwindx Show documentation
World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.render.airspaces;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;
import java.util.*;
import gov.nasa.worldwind.render.airspaces.Box;
/**
* @author garakl
* @version $Id: TrackAirspace.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public class TrackAirspace extends AbstractAirspace
{
protected List legs = new ArrayList();
protected boolean enableInnerCaps = true;
/**
* Denotes the the threshold that defines whether the angle between two adjacent legs is small. Initially 22.5
* degrees.
*/
protected Angle smallAngleThreshold = Angle.fromDegrees(22.5);
public TrackAirspace(Collection legs)
{
this.addLegs(legs);
}
public TrackAirspace(AirspaceAttributes attributes)
{
super(attributes);
}
public TrackAirspace()
{
}
public List getLegs()
{
return Collections.unmodifiableList(this.legs);
}
public void setLegs(Collection legs)
{
this.legs.clear();
this.addLegs(legs);
}
protected void addLegs(Iterable newLegs)
{
if (newLegs != null)
{
for (Box b : newLegs)
{
if (b != null)
this.addLeg(b);
}
this.setLegsOutOfDate();
}
}
public Box addLeg(LatLon start, LatLon end, double lowerAltitude, double upperAltitude,
double leftWidth, double rightWidth)
{
if (start == null)
{
String message = "nullValue.StartIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (end == null)
{
String message = "nullValue.EndIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
boolean[] terrainConformant = this.isTerrainConforming();
Box leg = new Box();
leg.setAltitudes(lowerAltitude, upperAltitude);
leg.setTerrainConforming(terrainConformant[0], terrainConformant[1]);
leg.setLocations(start, end);
leg.setWidths(leftWidth, rightWidth);
this.addLeg(leg);
return leg;
}
protected void addLeg(Box leg)
{
if (leg == null)
{
String message = "nullValue.LegIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
leg.setForceCullFace(true);
this.legs.add(leg);
this.setExtentOutOfDate();
this.setLegsOutOfDate();
}
public void removeAllLegs()
{
this.legs.clear();
}
public boolean isEnableInnerCaps()
{
return this.enableInnerCaps;
}
public void setEnableInnerCaps(boolean draw)
{
this.enableInnerCaps = draw;
this.setLegsOutOfDate();
}
/**
* Desnotes the threshold that defines whether the angle between two adjacent legs is small. This threshold is used
* to determine the best method for adjusting the vertices of adjacent legs.
*
* @return the angle used to determine when the angle between two adjacent legs is small.
*
* @see #setSmallAngleThreshold(gov.nasa.worldwind.geom.Angle)
*/
public Angle getSmallAngleThreshold()
{
return smallAngleThreshold;
}
/**
* Specifies the threshold that defines whether the angle between two adjacent legs is small. This threshold is used
* to determine the best method for adjusting the vertices of adjacent legs.
*
* When the angle between adjacent legs is small, the standard method of joining the leg's vertices forms a very
* large peak pointing away from the leg's common point. In this case TrackAirspace
uses a method that
* avoids this peak and produces a seamless transition between the adjacent legs.
*
* @param angle the angle to use when determining when the angle between two adjacent legs is small.
*
* @throws IllegalArgumentException if angle
is null
.
* @see #getSmallAngleThreshold()
*/
public void setSmallAngleThreshold(Angle angle)
{
if (angle == null)
{
String message = Logging.getMessage("nullValue.AngleIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.smallAngleThreshold = angle;
}
public void setAltitudes(double lowerAltitude, double upperAltitude)
{
super.setAltitudes(lowerAltitude, upperAltitude);
for (Box l : this.legs)
{
l.setAltitudes(lowerAltitude, upperAltitude);
}
this.setLegsOutOfDate();
}
public void setTerrainConforming(boolean lowerTerrainConformant, boolean upperTerrainConformant)
{
super.setTerrainConforming(lowerTerrainConformant, upperTerrainConformant);
for (Box l : this.legs)
{
l.setTerrainConforming(lowerTerrainConformant, upperTerrainConformant);
}
this.setLegsOutOfDate();
}
public boolean isAirspaceVisible(DrawContext dc)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// If the parent TrackAirspace is not visible, then return false immediately without testing the child legs.
if (!super.isAirspaceVisible(dc))
return false;
boolean visible = false;
// The parent TrackAirspace is visible. Since the parent TrackAirspace's extent potentially contains volumes
// where no child geometry exists, test that at least one of the child legs are visible.
for (Box b : this.legs)
{
if (b.isAirspaceVisible(dc))
{
visible = true;
break;
}
}
return visible;
}
public Position getReferencePosition()
{
ArrayList locations = new ArrayList(2 * this.legs.size());
for (Box box : this.legs)
{
LatLon[] ll = box.getLocations();
locations.add(ll[0]);
locations.add(ll[1]);
}
return this.computeReferencePosition(locations, this.getAltitudes());
}
@Override
protected Extent computeExtent(DrawContext dc)
{
// Update the child leg vertices if they're out of date. Since the leg vertices are input to the parent
// TrackAirspace's extent computation, they must be current before computing the parent's extent.
if (this.isLegsOutOfDate(dc))
{
this.doUpdateLegs(dc);
}
return super.computeExtent(dc);
}
protected Extent computeExtent(Globe globe, double verticalExaggeration)
{
List trackLegs = this.getLegs();
if (trackLegs == null || trackLegs.isEmpty())
{
return null;
}
else if (trackLegs.size() == 0)
{
return trackLegs.get(0).computeExtent(globe, verticalExaggeration);
}
else
{
ArrayList extents = new ArrayList();
for (Box leg : trackLegs)
{
extents.add(leg.computeExtent(globe, verticalExaggeration));
}
return gov.nasa.worldwind.geom.Box.union(extents);
}
}
@Override
protected List computeMinimalGeometry(Globe globe, double verticalExaggeration)
{
return null; // Track is a geometry container, and therefore has no geometry itself.
}
protected void doMoveTo(Position oldRef, Position newRef)
{
if (oldRef == null)
{
String message = "nullValue.OldRefIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (newRef == null)
{
String message = "nullValue.NewRefIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Don't call super.moveTo(). Each box should move itself according to the properties it was constructed with.
for (Box box : this.legs)
{
box.doMoveTo(oldRef, newRef);
}
this.setExtentOutOfDate();
this.setLegsOutOfDate();
}
protected boolean isLegsOutOfDate(DrawContext dc)
{
for (Box leg : this.legs)
{
if (!leg.isVerticesValid(dc.getGlobe()))
return true;
}
return false;
}
protected void setLegsOutOfDate()
{
for (Box leg : this.legs)
{
leg.clearVertices();
}
}
protected void doUpdateLegs(DrawContext dc)
{
Globe globe = dc.getGlobe();
double verticalExaggeration = dc.getVerticalExaggeration();
// Assign the standard eight vertices to each box and enable the starting and ending caps. We start by assuming
// that each leg is independent, then adjacent adjacent legs to give the appearance of a continuous track.
for (Box leg : this.legs)
{
if (leg == null) // This should never happen, but we check anyway.
continue;
leg.setEnableCaps(true);
Vec4[] vertices = Box.computeStandardVertices(globe, verticalExaggeration, leg);
if (vertices != null && vertices.length == 8)
leg.setVertices(globe, vertices);
}
// If there's more than one leg, we potentially align the vertices of adjacent legs to give the appearance of
// a continuous track. This loop never executes if the list of legs has less than two elements. Each iteration
// works on the adjacent vertices of the current leg and the next leg. Therefore this does not modify the
// starting vertices of the first leg, or the ending vertices of the last leg.
for (int i = 0; i < this.legs.size() - 1; i++)
{
Box leg = this.legs.get(i);
Box nextLeg = this.legs.get(i + 1);
if (leg == null || nextLeg == null) // This should never happen, but we check anyway.
continue;
// If the two legs have equivalent same locations, altitude, and altitude mode where they meet, then
// adjust each leg's vertices so the two legs appear to make a continuous shape.
if (this.mustJoinLegs(leg, nextLeg))
this.joinLegs(globe, verticalExaggeration, leg, nextLeg);
}
}
/**
* Specifies whether the legs must have their adjacent vertices joined. leg1
must precede
* leg2
. A track's legs must be joined when two adjacent legs share a common location. In this case,
* the geometry of the two adjacent boxes contains a gap on one side and an intersection on the other. Joining the
* legs modifies the vertices of each leg at their common location to produce a seamless transition from the first
* leg to the second.
*
* @param leg1 the first leg.
* @param leg2 the second leg.
*
* @return true
if the legs must be joined, otherwise false
.
*/
protected boolean mustJoinLegs(Box leg1, Box leg2)
{
LatLon[] leg1Loc = leg1.getLocations();
LatLon[] leg2Loc = leg2.getLocations();
double[] leg1Altitudes = leg1.getAltitudes();
double[] leg2Altitudes = leg2.getAltitudes();
boolean[] leg1TerrainConformance = leg1.isTerrainConforming();
boolean[] leg2TerrainConformance = leg2.isTerrainConforming();
if (!leg1Loc[1].equals(leg2Loc[0]))
return false;
if (leg1Altitudes[0] != leg2Altitudes[0] || leg1Altitudes[1] != leg2Altitudes[1])
return false;
//noinspection RedundantIfStatement
if (leg1TerrainConformance[0] != leg2TerrainConformance[0]
|| leg1TerrainConformance[1] != leg2TerrainConformance[1])
return false;
return true;
}
/**
* Modifies the vertices of the specified adjacent legs to produce a seamless transition from the first leg to the
* second. leg1
must precede leg2
, and they must share a common location at the end of
* leg1
and the beginning of leg2
. Without joining the adjacent vertices, the geometry of
* two adjacent boxes contains a gap on one side and an intersection on the other.
*
* This does nothing if the legs cannot be joined for any reason.
*
* @param globe the Globe
the legs are related to.
* @param verticalExaggeration the vertical exaggeration of the scene.
* @param leg1 the first leg.
* @param leg2 the second leg.
*/
protected void joinLegs(Globe globe, double verticalExaggeration, Box leg1, Box leg2)
{
Vec4[] leg1Vertices = leg1.getVertices(globe);
Vec4[] leg2Vertices = leg2.getVertices(globe);
Plane bisectingPlane = this.computeBisectingPlane(globe, leg1, leg2);
// If the two legs overlap, their bisecting plane intersects either the starting cap of the first leg, or the
// ending cap of the second leg. In this case, we cannot join the leg's vertices and exit without changing
// anything.
if (bisectingPlane.intersect(leg1Vertices[Box.A_LOW_LEFT], leg1Vertices[Box.A_LOW_RIGHT]) != null ||
bisectingPlane.intersect(leg2Vertices[Box.B_LOW_LEFT], leg2Vertices[Box.B_LOW_RIGHT]) != null)
{
return;
}
// If the angle between the legs is small, then using the bisecting plane to join them causes the leg's
// connecting vertices to form a very large peak away from the common point. Therefore we use a different
// approach to join acute legs as follows:
// * The first leg is extended to cover the second leg, and has its end cap enabled.
// * The second leg is clipped when it intersects the first leg, and has its start cap enables if and only if
// inner caps are enabled.
if (this.isSmallAngle(globe, leg1, leg2))
{
Plane[] leg1Planes = Box.computeStandardPlanes(globe, verticalExaggeration, leg1);
Plane[] leg2Planes = Box.computeStandardPlanes(globe, verticalExaggeration, leg2);
Line low_left_line = Line.fromSegment(leg2Vertices[Box.B_LOW_LEFT], leg2Vertices[Box.A_LOW_LEFT]);
Line low_right_line = Line.fromSegment(leg2Vertices[Box.B_LOW_RIGHT], leg2Vertices[Box.A_LOW_RIGHT]);
Line up_left_line = Line.fromSegment(leg2Vertices[Box.B_UPR_LEFT], leg2Vertices[Box.A_UPR_LEFT]);
Line up_right_line = Line.fromSegment(leg2Vertices[Box.B_UPR_RIGHT], leg2Vertices[Box.A_UPR_RIGHT]);
if (this.isRightTurn(globe, leg1, leg2))
{
Line low = Line.fromSegment(leg1Vertices[Box.A_LOW_RIGHT], leg1Vertices[Box.B_LOW_RIGHT]);
Line up = Line.fromSegment(leg1Vertices[Box.A_UPR_RIGHT], leg1Vertices[Box.B_UPR_RIGHT]);
leg1Vertices[Box.B_LOW_RIGHT] = leg2Planes[Box.FACE_LEFT].intersect(low);
leg1Vertices[Box.B_UPR_RIGHT] = leg2Planes[Box.FACE_LEFT].intersect(up);
leg2Vertices[Box.A_LOW_LEFT] = leg1Planes[Box.FACE_RIGHT].intersect(low_left_line);
leg2Vertices[Box.A_LOW_RIGHT] = leg1Planes[Box.FACE_RIGHT].intersect(low_right_line);
leg2Vertices[Box.A_UPR_LEFT] = leg1Planes[Box.FACE_RIGHT].intersect(up_left_line);
leg2Vertices[Box.A_UPR_RIGHT] = leg1Planes[Box.FACE_RIGHT].intersect(up_right_line);
}
else
{
Line low = Line.fromSegment(leg1Vertices[Box.A_LOW_LEFT], leg1Vertices[Box.B_LOW_LEFT]);
Line up = Line.fromSegment(leg1Vertices[Box.A_UPR_LEFT], leg1Vertices[Box.B_UPR_LEFT]);
leg1Vertices[Box.B_LOW_LEFT] = leg2Planes[Box.FACE_RIGHT].intersect(low);
leg1Vertices[Box.B_UPR_LEFT] = leg2Planes[Box.FACE_RIGHT].intersect(up);
leg2Vertices[Box.A_LOW_LEFT] = leg1Planes[Box.FACE_LEFT].intersect(low_left_line);
leg2Vertices[Box.A_LOW_RIGHT] = leg1Planes[Box.FACE_LEFT].intersect(low_right_line);
leg2Vertices[Box.A_UPR_LEFT] = leg1Planes[Box.FACE_LEFT].intersect(up_left_line);
leg2Vertices[Box.A_UPR_RIGHT] = leg1Planes[Box.FACE_LEFT].intersect(up_right_line);
}
leg1.setEnableEndCap(true);
leg2.setEnableStartCap(this.isEnableInnerCaps());
leg1.setVertices(globe, leg1Vertices);
leg2.setVertices(globe, leg2Vertices);
}
else
{
Line low_left_line = Line.fromSegment(leg1Vertices[Box.A_LOW_LEFT], leg1Vertices[Box.B_LOW_LEFT]);
Line low_right_line = Line.fromSegment(leg1Vertices[Box.A_LOW_RIGHT], leg1Vertices[Box.B_LOW_RIGHT]);
Line up_left_line = Line.fromSegment(leg1Vertices[Box.A_UPR_LEFT], leg1Vertices[Box.B_UPR_LEFT]);
Line up_right_line = Line.fromSegment(leg1Vertices[Box.A_UPR_RIGHT], leg1Vertices[Box.B_UPR_RIGHT]);
leg1Vertices[Box.B_LOW_LEFT] = bisectingPlane.intersect(low_left_line);
leg1Vertices[Box.B_LOW_RIGHT] = bisectingPlane.intersect(low_right_line);
leg1Vertices[Box.B_UPR_LEFT] = bisectingPlane.intersect(up_left_line);
leg1Vertices[Box.B_UPR_RIGHT] = bisectingPlane.intersect(up_right_line);
low_left_line = Line.fromSegment(leg2Vertices[Box.B_LOW_LEFT], leg2Vertices[Box.A_LOW_LEFT]);
low_right_line = Line.fromSegment(leg2Vertices[Box.B_LOW_RIGHT], leg2Vertices[Box.A_LOW_RIGHT]);
up_left_line = Line.fromSegment(leg2Vertices[Box.B_UPR_LEFT], leg2Vertices[Box.A_UPR_LEFT]);
up_right_line = Line.fromSegment(leg2Vertices[Box.B_UPR_RIGHT], leg2Vertices[Box.A_UPR_RIGHT]);
leg2Vertices[Box.A_LOW_LEFT] = bisectingPlane.intersect(low_left_line);
leg2Vertices[Box.A_LOW_RIGHT] = bisectingPlane.intersect(low_right_line);
leg2Vertices[Box.A_UPR_LEFT] = bisectingPlane.intersect(up_left_line);
leg2Vertices[Box.A_UPR_RIGHT] = bisectingPlane.intersect(up_right_line);
leg1.setEnableEndCap(this.isEnableInnerCaps());
leg2.setEnableStartCap(this.isEnableInnerCaps());
leg1.setVertices(globe, leg1Vertices);
leg2.setVertices(globe, leg2Vertices);
}
}
/**
* Returns a Plane
that bisects the angle between the two legs at the point at their common location.
* leg1
must precede leg2
, and they must share a common location at the end of
* leg1
and the beginning of leg2
. This returns null
if the legs overlap and
* cannot be bisected.
*
* @param globe the Globe
the legs are related to.
* @param leg1 the first leg.
* @param leg2 the second leg.
*
* @return a Plane
that bisects the geometry of the two legs.
*/
protected Plane computeBisectingPlane(Globe globe, Box leg1, Box leg2)
{
LatLon[] leg1Loc = leg1.getLocations();
LatLon[] leg2Loc = leg2.getLocations();
double[] leg1Altitudes = leg1.getAltitudes();
double[] leg2Altitudes = leg2.getAltitudes();
// Compute the Cartesian point of the the first leg's starting location, the two leg's common location, and the
// second leg's ending location. Use the lower altitude, because we're only interested in the angles between the
// two legs.
Vec4 a = globe.computePointFromPosition(leg1Loc[0], leg1Altitudes[0]);
Vec4 b = globe.computePointFromPosition(leg1Loc[1], leg1Altitudes[0]);
Vec4 c = globe.computePointFromPosition(leg2Loc[1], leg2Altitudes[0]);
// Compute a vector that lies on a plane that bisects the angle between the two legs at their common location.
// This vector is perpendicular to the vectors connecting both leg's locations.
Vec4 ab = a.subtract3(b).normalize3();
Vec4 cb = c.subtract3(b).normalize3();
Vec4 ab_plus_cb = ab.add3(cb);
Vec4 n;
if (ab_plus_cb.getLength3() < 0.0000001)
{
// If the legs are parallel or nearly parallel, computing their bisecting plane using the cross product of
// their length vectors can produce unexpected results due to floating point rounding. In this case it's
// safe to treat legs as parallel and use the vector connecting the first leg's locations as the bisecting
// plane.
n = ab.normalize3();
}
else
{
// Otherwise, we compute the bisecting plane as the plane that contains the bisecting vector and the Globe's
// normal at the two legs' common location.
Vec4 bNormal = globe.computeSurfaceNormalAtPoint(b);
n = bNormal.cross3(ab_plus_cb).normalize3();
}
double d = -b.dot3(n);
return new Plane(n.getX(), n.getY(), n.getZ(), d);
}
/**
* Specifies whether the angle between the two adjacent legs is less than a specified threshold. The threshold is
* configured by calling {@link #setSmallAngleThreshold(gov.nasa.worldwind.geom.Angle)}. leg1
must
* precede leg2
, and they must share a common location at the end of leg1
and the
* beginning of leg2
.
*
* @param globe the Globe
the legs are related to.
* @param leg1 the first leg.
* @param leg2 the second leg.
*
* @return true
if the angle between the two legs is small, otherwise false
.
*/
protected boolean isSmallAngle(Globe globe, Box leg1, Box leg2)
{
LatLon[] leg1Loc = leg1.getLocations();
LatLon[] leg2Loc = leg2.getLocations();
double[] leg1Altitudes = leg1.getAltitudes();
double[] leg2Altitudes = leg2.getAltitudes();
// Compute the lower center point of leg1's starting, the lower center point on the two leg's common location,
// and the lower center point of the leg2's ending.
Vec4 a = globe.computePointFromPosition(leg1Loc[0], leg1Altitudes[0]);
Vec4 b = globe.computePointFromPosition(leg1Loc[1], leg1Altitudes[0]);
Vec4 c = globe.computePointFromPosition(leg2Loc[1], leg2Altitudes[0]);
Vec4 ba = a.subtract3(b);
Vec4 bc = c.subtract3(b);
Angle angle = ba.angleBetween3(bc);
return angle.compareTo(this.getSmallAngleThreshold()) <= 0;
}
/**
* Specifies whether the two adjacent legs make a right turn relative to the surface of the specified
* globe
. leg1
must precede leg2
, and they must share a common location at
* the end of leg1
and the beginning of leg2
.
*
* @param globe the Globe
the legs are related to.
* @param leg1 the first leg.
* @param leg2 the second leg.
*
* @return @return true
if the legs make a right turn on the specified globe
, otherwise
* false
.
*/
protected boolean isRightTurn(Globe globe, Box leg1, Box leg2)
{
LatLon[] leg1Loc = leg1.getLocations();
LatLon[] leg2Loc = leg2.getLocations();
double[] leg1Altitudes = leg1.getAltitudes();
double[] leg2Altitudes = leg2.getAltitudes();
// Compute the lower center point of leg1's starting, the lower center point on the two leg's common location,
// and the lower center point of the leg2's ending.
Vec4 a = globe.computePointFromPosition(leg1Loc[0], leg1Altitudes[0]);
Vec4 b = globe.computePointFromPosition(leg1Loc[1], leg1Altitudes[0]);
Vec4 c = globe.computePointFromPosition(leg2Loc[1], leg2Altitudes[0]);
Vec4 ba = a.subtract3(b);
Vec4 bc = c.subtract3(b);
Vec4 cross = ba.cross3(bc);
Vec4 n = globe.computeSurfaceNormalAtLocation(leg1Loc[1].getLatitude(), leg1Loc[1].getLongitude());
return cross.dot3(n) >= 0;
}
//**************************************************************//
//******************** Geometry Rendering ********************//
//**************************************************************//
public void makeOrderedRenderable(DrawContext dc, AirspaceRenderer renderer)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (renderer == null)
{
String message = Logging.getMessage("nullValue.RendererIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Update the child leg vertices if they're out of date. Since the leg vertices are used to determine how each
// leg is shaped with respect to its neighbors, the vertices must be current before rendering each leg.
if (this.isLegsOutOfDate(dc))
{
this.doUpdateLegs(dc);
}
for (Box leg : this.getLegs())
{
if (!leg.isVisible())
continue;
if (!leg.isAirspaceVisible(dc))
continue;
// The leg is responsible for applying its own attributes, so we override its attributes with our own just
// before rendering.
leg.setAttributes(this.getAttributes());
// Create an ordered renderable that draws each layer, but specifies this Track as the picked object.
OrderedRenderable or = renderer.createOrderedRenderable(dc, leg, leg.computeEyeDistance(dc), this);
dc.addOrderedRenderable(or);
}
}
protected void doRenderGeometry(DrawContext dc, String drawStyle)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Called by AirspaceRenderer's drawNow and pickNow methods. These methods do not use the ordered renderable
// queue, so TrackAirspace must explicitly initiate drawing its legs. When airspace rendering is initiated via
// AirspaceRenderer drawOrdered or pickOrdered, TrackAirspace does not add an ordered renderable for itself, so
// this method is never initiated.
// Update the child leg vertices if they're out of date. Since the leg vertices are used to determine how each
// leg is shaped with respect to its neighbors, the vertices must be current before rendering each leg.
if (this.isLegsOutOfDate(dc))
{
this.doUpdateLegs(dc);
}
for (Box b : this.getLegs())
{
if (!b.isVisible())
continue;
if (!b.isAirspaceVisible(dc))
continue;
b.renderGeometry(dc, drawStyle);
}
}
protected void doRenderExtent(DrawContext dc)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Called by AirspaceRenderer's drawNow and pickNow methods. These methods do not use the ordered renderable
// queue, so TrackAirspace must explicitly initiate drawing its legs. When airspace rendering is initiated via
// AirspaceRenderer drawOrdered or pickOrdered, TrackAirspace does not add an ordered renderable for itself, so
// this method is never initiated.
for (Box b : this.legs)
{
b.renderExtent(dc);
}
}
//**************************************************************//
//******************** END Geometry Rendering ****************//
//**************************************************************//
@Override
protected void doGetRestorableState(RestorableSupport rs, RestorableSupport.StateObject context)
{
super.doGetRestorableState(rs, context);
rs.addStateValueAsBoolean(context, "enableInnerCaps", this.isEnableInnerCaps());
RestorableSupport.StateObject so = rs.addStateObject(context, "legs");
for (Box leg : this.legs)
{
RestorableSupport.StateObject lso = rs.addStateObject(so, "leg");
leg.doGetRestorableState(rs, lso);
}
}
@Override
protected void doRestoreState(RestorableSupport rs, RestorableSupport.StateObject context)
{
super.doRestoreState(rs, context);
Boolean b = rs.getStateValueAsBoolean(context, "enableInnerCaps");
if (b != null)
this.setEnableInnerCaps(b);
RestorableSupport.StateObject so = rs.getStateObject(context, "legs");
if (so == null)
return;
RestorableSupport.StateObject[] lsos = rs.getAllStateObjects(so, "leg");
if (lsos == null || lsos.length == 0)
return;
ArrayList legList = new ArrayList(lsos.length);
for (RestorableSupport.StateObject lso : lsos)
{
if (lso != null)
{
Box leg = new Box();
leg.doRestoreState(rs, lso);
legList.add(leg);
}
}
this.setLegs(legList);
}
}