gov.nasa.worldwind.symbology.AbstractTacticalGraphic Maven / Gradle / Ivy
The newest version!
/*
* 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.symbology;
import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.drag.*;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;
import java.awt.*;
import java.util.*;
import java.util.List;
/**
* Base class for tactical graphics. See the TacticalGraphic Usage Guide for
* instructions on using TacticalGraphic in an application. This base class provides functionality for creating and
* rendering a graphic that is made up of one or more shapes, and text labels.
*
* Implementations must implement at least {@link #doRenderGraphic(gov.nasa.worldwind.render.DrawContext)
* doRenderGraphic} and {@link #applyDelegateOwner(Object)}.
*
* @author pabercrombie
* @version $Id: AbstractTacticalGraphic.java 560 2012-04-26 16:28:24Z pabercrombie $
*/
public abstract class AbstractTacticalGraphic extends AVListImpl implements TacticalGraphic, Renderable, Draggable
{
/** The default highlight color. */
protected static final Material DEFAULT_HIGHLIGHT_MATERIAL = Material.WHITE;
/**
* Opacity of label interiors. This value is multiplied by the label text opacity to determine the final interior
* opacity.
*/
protected static final double DEFAULT_LABEL_INTERIOR_OPACITY = 0.7;
/**
* The graphic's text string. This field corresponds to the {@link SymbologyConstants#UNIQUE_DESIGNATION} modifier.
* Note that this field is not used if an Iterable is specified as the unique designation.
*/
protected String text;
/** Indicates whether or not the graphic is highlighted. */
protected boolean highlighted;
/** Indicates whether or not to render the graphic. */
protected boolean visible = true;
/** Indicates whether or not to render text modifiers. */
protected boolean showTextModifiers = true;
/** Indicates whether or not to render graphic modifiers. */
protected boolean showGraphicModifiers = true;
/** Indicates whether this object can be dragged. */
protected boolean dragEnabled = true;
/** Provides additional information for dragging regarding this particular object. */
protected DraggableSupport draggableSupport = null;
// Implementation note: by default, show the hostile indicator (the letters "ENY"). Note that this default is
// different from MilStd2525TacticalSymbol, which does not display the hostile indicator by default. Section 5.5.1.1
// (pg. 37) of MIL-STD-2525C states that the indicator is not required if color is used in the display. We choose to
// display the indicator by default following the principle that by default hostile entities should look as
// hostile as possible (to avoid being mistaken for friendly entities). In the case of tactical symbols, however,
// the indicator is redundant to both the symbol frame and fill, so it is not displayed by default.
/** Indicates whether or not to render the hostile/enemy modifier. This modifier is displayed by default. */
protected boolean showHostileIndicator = true;
/** Indicates whether or not to render the location modifier. */
protected boolean showLocation = true;
/** Object returned during picking to represent this graphic. */
protected Object delegateOwner;
/** Unit format used to format location and altitude for text modifiers. */
protected UnitsFormat unitsFormat = AbstractTacticalSymbol.DEFAULT_UNITS_FORMAT;
/**
* Attributes to apply when the graphic is not highlighted. These attributes override defaults determined by the
* graphic's symbol code.
*/
protected TacticalGraphicAttributes normalAttributes;
/**
* Attributes to apply when the graphic is highlighted. These attributes override defaults determined by the
* graphic's symbol code.
*/
protected TacticalGraphicAttributes highlightAttributes;
/** Offset applied to the graphic's main label. */
protected Offset labelOffset;
/** Labels to render with the graphic. */
protected List labels;
/**
* Map of modifiers applied to this graphic. Note that implementations may not store all modifiers in this map. Some
* modifiers may be handled specially.
*/
protected AVList modifiers;
/** Current frame timestamp. */
protected long frameTimestamp = -1L;
/** Override attributes for the current frame. */
protected TacticalGraphicAttributes activeOverrides = new BasicTacticalGraphicAttributes();
/**
* Shape attributes shared by all shapes that make up this graphic. The graphic's active attributes are copied into
* this attribute bundle on each frame.
*/
protected ShapeAttributes activeShapeAttributes = new BasicShapeAttributes();
/** Flag to indicate that labels must be recreated before the graphic is rendered. */
protected boolean mustCreateLabels = true;
/**
* Render this graphic, without modifiers.
*
* @param dc Current draw context.
*
* @see #doRenderTextModifiers(gov.nasa.worldwind.render.DrawContext)
* @see #doRenderGraphicModifiers(gov.nasa.worldwind.render.DrawContext)
*/
protected abstract void doRenderGraphic(DrawContext dc);
/**
* Invoked each frame to apply to the current delegate owner to all renderable objects used to draw the graphic.
* This base class will apply the delegate owner to Label objects. Subclasses must implement this method to apply
* the delegate owner to any Renderables that they will draw in order to render the graphic.
*
* @param owner Current delegate owner.
*/
protected abstract void applyDelegateOwner(Object owner);
/** {@inheritDoc} */
public Object getModifier(String modifier)
{
return this.modifiers != null ? this.modifiers.getValue(modifier) : null;
}
/** {@inheritDoc} */
public void setModifier(String modifier, Object value)
{
if (this.modifiers == null)
this.modifiers = new AVListImpl();
this.modifiers.setValue(modifier, value);
this.onModifierChanged();
}
/** {@inheritDoc} */
public boolean isShowTextModifiers()
{
return this.showTextModifiers;
}
/** {@inheritDoc} */
public void setShowTextModifiers(boolean showModifiers)
{
this.showTextModifiers = showModifiers;
}
/** {@inheritDoc} */
public boolean isShowGraphicModifiers()
{
return this.showGraphicModifiers;
}
/** {@inheritDoc} */
public void setShowGraphicModifiers(boolean showModifiers)
{
this.showGraphicModifiers = showModifiers;
}
/** {@inheritDoc} */
public boolean isShowHostileIndicator()
{
return this.showHostileIndicator;
}
/** {@inheritDoc} */
public void setShowHostileIndicator(boolean showHostileIndicator)
{
this.showHostileIndicator = showHostileIndicator;
this.onModifierChanged();
}
/** {@inheritDoc} */
public boolean isShowLocation()
{
return this.showLocation;
}
/** {@inheritDoc} */
public void setShowLocation(boolean showLocation)
{
this.showLocation = showLocation;
this.onModifierChanged();
}
/** {@inheritDoc} */
public String getText()
{
return this.text;
}
/** {@inheritDoc} */
public void setText(String text)
{
this.text = text;
this.onModifierChanged();
}
/** {@inheritDoc} */
public boolean isVisible()
{
return this.visible;
}
/** {@inheritDoc} */
public void setVisible(boolean visible)
{
this.visible = visible;
}
/** {@inheritDoc} */
public TacticalGraphicAttributes getAttributes()
{
return this.normalAttributes;
}
/** {@inheritDoc} */
public void setAttributes(TacticalGraphicAttributes attributes)
{
this.normalAttributes = attributes;
}
/** {@inheritDoc} */
public TacticalGraphicAttributes getHighlightAttributes()
{
return this.highlightAttributes;
}
/** {@inheritDoc} */
public void setHighlightAttributes(TacticalGraphicAttributes attributes)
{
this.highlightAttributes = attributes;
}
/** {@inheritDoc} */
public Object getDelegateOwner()
{
return this.delegateOwner;
}
/** {@inheritDoc} */
public void setDelegateOwner(Object owner)
{
this.delegateOwner = owner;
}
/** {@inheritDoc} */
public UnitsFormat getUnitsFormat()
{
return this.unitsFormat;
}
/** {@inheritDoc} */
public void setUnitsFormat(UnitsFormat unitsFormat)
{
if (unitsFormat == null)
{
String msg = Logging.getMessage("nullValue.Format");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
this.unitsFormat = unitsFormat;
}
/** {@inheritDoc} */
public Offset getLabelOffset()
{
return this.labelOffset;
}
/** {@inheritDoc} */
public void setLabelOffset(Offset labelOffset)
{
this.labelOffset = labelOffset;
}
/** {@inheritDoc} */
public boolean isHighlighted()
{
return this.highlighted;
}
/** {@inheritDoc} */
public void setHighlighted(boolean highlighted)
{
this.highlighted = highlighted;
}
/////////////////////////////
// Movable interface
/////////////////////////////
/** {@inheritDoc} */
public void move(Position delta)
{
if (delta == null)
{
String msg = Logging.getMessage("nullValue.PositionIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
Position refPos = this.getReferencePosition();
// The reference position is null if this shape has no positions. In this case moving the shape by a
// relative delta is meaningless. Therefore we fail softly by exiting and doing nothing.
if (refPos == null)
return;
this.moveTo(refPos.add(delta));
}
/** {@inheritDoc} */
public void moveTo(Position position)
{
if (position == null)
{
String msg = Logging.getMessage("nullValue.PositionIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
Position oldPosition = this.getReferencePosition();
// The reference position is null if this shape has no positions. In this case moving the shape to a new
// reference position is meaningless. Therefore we fail softly by exiting and doing nothing.
if (oldPosition == null)
return;
List newPositions = Position.computeShiftedPositions(oldPosition, position, this.getPositions());
if (newPositions != null)
this.setPositions(newPositions);
}
@Override
public boolean isDragEnabled()
{
return this.dragEnabled;
}
@Override
public void setDragEnabled(boolean enabled)
{
this.dragEnabled = enabled;
}
@Override
public void drag(DragContext dragContext)
{
if (!this.dragEnabled)
return;
if (this.draggableSupport == null)
this.draggableSupport = new DraggableSupport(this, WorldWind.CLAMP_TO_GROUND);
this.doDrag(dragContext);
}
protected void doDrag(DragContext dragContext)
{
this.draggableSupport.dragGlobeSizeConstant(dragContext);
}
/////////////
// Rendering
/////////////
/** {@inheritDoc} */
public void render(DrawContext dc)
{
if (!this.isVisible())
{
return;
}
this.determinePerFrameAttributes(dc);
this.doRenderGraphic(dc);
if (this.isShowTextModifiers())
this.doRenderTextModifiers(dc);
if (this.isShowGraphicModifiers())
this.doRenderGraphicModifiers(dc);
}
/**
* Determine geometry and attributes for this frame. This method only determines attributes the first time that it
* is called for each frame. Multiple calls in the same frame will have no effect.
*
* @param dc Current draw context.
*/
protected void determinePerFrameAttributes(DrawContext dc)
{
long timeStamp = dc.getFrameTimeStamp();
if (this.frameTimestamp != timeStamp)
{
// Allow the subclass to create labels, if necessary
if (this.mustCreateLabels)
{
if (this.labels != null)
{
this.labels.clear();
}
this.createLabels();
this.mustCreateLabels = false;
}
this.determineActiveAttributes();
this.determineDelegateOwner();
this.computeGeometry(dc);
this.frameTimestamp = timeStamp;
}
}
/**
* Render the text modifiers.
*
* @param dc Current draw context.
*/
protected void doRenderTextModifiers(DrawContext dc)
{
if (this.labels == null)
return;
for (TacticalGraphicLabel label : this.labels)
{
label.render(dc);
}
}
/**
* Render the graphic modifiers. This base class does not render anything, but subclasses may override this method
* to draw graphic modifiers.
*
* @param dc Current draw context.
*/
protected void doRenderGraphicModifiers(DrawContext dc)
{
// Do nothing, but allow subclasses to override to add graphic modifiers.
}
/**
* Invoked when a modifier is changed. This implementation marks the label text as invalid causing it to be
* recreated based on the new modifiers.
*/
protected void onModifierChanged()
{
// Text may need to change to reflect new modifiers.
this.mustCreateLabels = true;
}
/**
* Determine positions for the start and end labels.
*
* @param dc Current draw context.
*/
protected void determineLabelPositions(DrawContext dc)
{
// Do nothing, but allow subclasses to override
}
protected void createLabels()
{
// Do nothing, but allow subclasses to override
}
protected TacticalGraphicLabel addLabel(String text)
{
if (this.labels == null)
this.labels = new ArrayList();
TacticalGraphicLabel label = new TacticalGraphicLabel();
label.setText(text);
label.setDelegateOwner(this.getActiveDelegateOwner());
label.setTextAlign(AVKey.CENTER);
this.labels.add(label);
return label;
}
protected void computeGeometry(DrawContext dc)
{
// Allow the subclass to decide where to put the labels
this.determineLabelPositions(dc);
}
/**
* Determine the delegate owner for the current frame, and apply the owner to all renderable objects used to draw
* the graphic.
*/
protected void determineDelegateOwner()
{
Object owner = this.getActiveDelegateOwner();
// Apply the delegate owner to all label objects.
if (this.labels != null)
{
for (TacticalGraphicLabel label : this.labels)
{
label.setDelegateOwner(owner);
}
}
// Give subclasses a chance to apply the delegate owner to shapes they own.
this.applyDelegateOwner(owner);
}
/**
* Indicates the object attached to the pick list to represent this graphic.
*
* @return Delegate owner, if specified, or {@code this} if an owner is not specified.
*/
protected Object getActiveDelegateOwner()
{
Object owner = this.getDelegateOwner();
return owner != null ? owner : this;
}
/** Determine active attributes for this frame. */
protected void determineActiveAttributes()
{
// Apply defaults for this graphic
this.applyDefaultAttributes(this.activeShapeAttributes);
if (this.isHighlighted())
{
TacticalGraphicAttributes highlightAttributes = this.getHighlightAttributes();
// If the application specified overrides to the highlight attributes, then apply the overrides
if (highlightAttributes != null)
{
this.activeOverrides.copy(highlightAttributes);
// Apply overrides specified by application
this.applyOverrideAttributes(highlightAttributes, this.activeShapeAttributes);
}
else
{
// If no highlight attributes have been specified we need to use the normal attributes but adjust them
// to cause highlighting.
this.activeShapeAttributes.setOutlineMaterial(DEFAULT_HIGHLIGHT_MATERIAL);
this.activeShapeAttributes.setInteriorMaterial(DEFAULT_HIGHLIGHT_MATERIAL);
this.activeShapeAttributes.setInteriorOpacity(1.0);
this.activeShapeAttributes.setOutlineOpacity(1.0);
}
}
else
{
// Apply overrides specified by application
TacticalGraphicAttributes normalAttributes = this.getAttributes();
if (normalAttributes != null)
{
this.activeOverrides.copy(normalAttributes);
this.applyOverrideAttributes(normalAttributes, this.activeShapeAttributes);
}
}
this.applyLabelAttributes();
}
/** Apply the active attributes to the graphic's labels. */
protected void applyLabelAttributes()
{
if (WWUtil.isEmpty(this.labels))
return;
Material labelMaterial = this.getLabelMaterial();
Font font = this.activeOverrides.getTextModifierFont();
if (font == null)
font = TacticalGraphicLabel.DEFAULT_FONT;
double opacity = this.getActiveShapeAttributes().getInteriorOpacity();
// Compute an interior opacity for the label. Note that not all tactical graphic labels are drawn with an
// interior.
double labelInteriorOpacity = this.computeLabelInteriorOpacity(opacity);
for (TacticalGraphicLabel label : this.labels)
{
label.setMaterial(labelMaterial);
label.setFont(font);
label.setOpacity(opacity);
label.setInteriorOpacity(labelInteriorOpacity);
}
// Apply the offset to the main label.
Offset offset = this.getLabelOffset();
if (offset == null)
offset = this.getDefaultLabelOffset();
this.labels.get(0).setOffset(offset);
}
/**
* Compute the opacity for the label interior. By default, the label interior is opacity is computed as 70% of the
* text opacity.
*
* @param textOpacity Opacity of the label text.
*
* @return Opacity of the label interior as a floating point number between 0.0 and 1.0.
*/
protected double computeLabelInteriorOpacity(double textOpacity)
{
return textOpacity * DEFAULT_LABEL_INTERIOR_OPACITY;
}
/**
* Indicates the default offset applied to the graphic's main label. This offset may be overridden by the graphic
* attributes.
*
* @return Offset to apply to the main label.
*/
protected Offset getDefaultLabelOffset()
{
return TacticalGraphicLabel.DEFAULT_OFFSET;
}
/**
* Get the override attributes that are active for this frame.
*
* @return Override attributes. Values set in this bundle override defaults specified by the symbol set.
*/
protected TacticalGraphicAttributes getActiveOverrideAttributes()
{
return this.activeOverrides;
}
/**
* Get the active shape attributes for this frame. The active attributes are created by applying application
* specified overrides to the default attributes specified by the symbol set.
*
* @return Active shape attributes.
*/
protected ShapeAttributes getActiveShapeAttributes()
{
return this.activeShapeAttributes;
}
/**
* Get the Material that should be used to draw labels. If no override material has been specified, the graphic's
* outline Material is used for the labels.
*
* @return The Material that should be used when drawing labels. May change each frame.
*/
protected Material getLabelMaterial()
{
Material material = this.activeOverrides.getTextModifierMaterial();
if (material != null)
return material;
else
return this.activeShapeAttributes.getOutlineMaterial();
}
/**
* Apply defaults to the active attributes bundle. The default attributes are determined by the type of graphic.
* This method is called each frame to reset the active shape attributes to the appropriate default state. Override
* attributes specified by the application may be applied after the defaults have been set.
*
* @param attributes Attributes bundle to receive defaults.
*/
protected void applyDefaultAttributes(ShapeAttributes attributes)
{
// Do nothing but allow subclasses to override
}
/**
* Apply override attributes specified in a TacticalGraphicAttributes bundle to the active ShapeAttributes. Any
* non-null properties of {@code graphicAttributes} will be applied to {@code shapeAttributes}.
*
* @param graphicAttributes Override attributes.
* @param shapeAttributes Shape attributes to receive overrides.
*/
protected void applyOverrideAttributes(TacticalGraphicAttributes graphicAttributes, ShapeAttributes shapeAttributes)
{
Material material = graphicAttributes.getInteriorMaterial();
if (material != null)
{
shapeAttributes.setInteriorMaterial(material);
}
material = graphicAttributes.getOutlineMaterial();
if (material != null)
{
shapeAttributes.setOutlineMaterial(material);
}
Double value = graphicAttributes.getInteriorOpacity();
if (value != null)
{
shapeAttributes.setInteriorOpacity(value);
}
value = graphicAttributes.getOutlineOpacity();
if (value != null)
{
shapeAttributes.setOutlineOpacity(value);
}
value = graphicAttributes.getOutlineWidth();
if (value != null)
{
shapeAttributes.setOutlineWidth(value);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy