Advertisement
Guest User

Tactical Symbol Terrain Line

a guest
Jun 18th, 2016
464
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 94.05 KB | None | 0 0
  1. /*
  2.  * Copyright (C) 2012 United States Government as represented by the Administrator of the
  3.  * National Aeronautics and Space Administration.
  4.  * All Rights Reserved.
  5.  */
  6.  
  7. package gov.nasa.worldwind.symbology;
  8.  
  9. import com.jogamp.opengl.util.texture.*;
  10. import com.jogamp.opengl.util.texture.awt.AWTTextureIO;
  11.  
  12. import gov.nasa.worldwind.*;
  13. import gov.nasa.worldwind.avlist.*;
  14. import gov.nasa.worldwind.geom.*;
  15. import gov.nasa.worldwind.layers.Layer;
  16. import gov.nasa.worldwind.pick.*;
  17. import gov.nasa.worldwind.render.*;
  18. import gov.nasa.worldwind.render.PointPlacemark.OrderedPlacemark;
  19. import gov.nasa.worldwind.util.*;
  20.  
  21. import javax.media.opengl.*;
  22.  
  23. import java.awt.*;
  24. import java.awt.geom.*;
  25. import java.awt.image.*;
  26. import java.util.*;
  27. import java.util.List;
  28.  
  29. /**
  30.  * @author dcollins
  31.  * @version $Id: AbstractTacticalSymbol.java 2366 2014-10-02 23:16:31Z tgaskins $
  32.  */
  33. public abstract class AbstractTacticalSymbol extends WWObjectImpl implements TacticalSymbol, Movable
  34. {
  35.     protected static class IconSource
  36.     {
  37.         protected IconRetriever retriever;
  38.         protected String symbolId;
  39.         protected AVList retrieverParams;
  40.  
  41.         public IconSource(IconRetriever retriever, String symbolId, AVList retrieverParams)
  42.         {
  43.             this.retriever = retriever;
  44.             this.symbolId = symbolId;
  45.  
  46.             if (retrieverParams != null)
  47.             {
  48.                 // If the specified parameters are non-null, then store a copy of the parameters in this key's params
  49.                 // property to insulate it from changes made by the caller. This params list must not change after
  50.                 // construction this key's properties must be immutable.
  51.                 this.retrieverParams = new AVListImpl();
  52.                 this.retrieverParams.setValues(retrieverParams);
  53.             }
  54.         }
  55.  
  56.         public IconRetriever getRetriever()
  57.         {
  58.             return this.retriever;
  59.         }
  60.  
  61.         public String getSymbolId()
  62.         {
  63.             return this.symbolId;
  64.         }
  65.  
  66.         public AVList getRetrieverParams()
  67.         {
  68.             return this.retrieverParams;
  69.         }
  70.  
  71.         @Override
  72.         public boolean equals(Object o)
  73.         {
  74.             if (this == o)
  75.                 return true;
  76.             if (o == null || getClass() != o.getClass())
  77.                 return false;
  78.  
  79.             IconSource that = (IconSource) o;
  80.  
  81.             if (this.retriever != null ? !this.retriever.equals(that.retriever)
  82.                 : that.retriever != null)
  83.                 return false;
  84.             if (this.symbolId != null ? !this.symbolId.equals(that.symbolId) : that.symbolId != null)
  85.                 return false;
  86.  
  87.             if (this.retrieverParams != null && that.retrieverParams != null)
  88.             {
  89.                 Set<Map.Entry<String, Object>> theseEntries = this.retrieverParams.getEntries();
  90.                 Set<Map.Entry<String, Object>> thoseEntries = that.retrieverParams.getEntries();
  91.  
  92.                 return theseEntries.equals(thoseEntries);
  93.             }
  94.             return (this.retrieverParams == null && that.retrieverParams == null);
  95.         }
  96.  
  97.         @Override
  98.         public int hashCode()
  99.         {
  100.             int result = this.retriever != null ? this.retriever.hashCode() : 0;
  101.             result = 31 * result + (this.symbolId != null ? this.symbolId.hashCode() : 0);
  102.             result = 31 * result + (this.retrieverParams != null ? this.retrieverParams.getEntries().hashCode() : 0);
  103.             return result;
  104.         }
  105.  
  106.         @Override
  107.         public String toString()
  108.         {
  109.             return this.symbolId;
  110.         }
  111.     }
  112.  
  113.     // Use an IconKey as the texture's image source. The image source is what defines the contents of this texture,
  114.     // and is used as an address for the texture's contents in the cache.
  115.     protected static class IconTexture extends LazilyLoadedTexture
  116.     {
  117.         public IconTexture(IconSource imageSource)
  118.         {
  119.             super(imageSource);
  120.         }
  121.  
  122.         public IconTexture(IconSource imageSource, boolean useMipMaps)
  123.         {
  124.             super(imageSource, useMipMaps);
  125.         }
  126.  
  127.         protected boolean loadTextureData()
  128.         {
  129.             TextureData td = this.createIconTextureData();
  130.  
  131.             if (td != null)
  132.                 this.setTextureData(td);
  133.  
  134.             return td != null;
  135.         }
  136.  
  137.         protected TextureData createIconTextureData()
  138.         {
  139.             try
  140.             {
  141.                 IconSource source = (IconSource) this.getImageSource();
  142.                 BufferedImage image = source.getRetriever().createIcon(source.getSymbolId(),
  143.                     source.getRetrieverParams());
  144.  
  145.                 if (image == null)
  146.                 {
  147.                     // IconRetriever returns null if the symbol identifier is not recognized, or if the parameter list
  148.                     // specified an empty icon. In either case, we mark the texture initialization as having failed to
  149.                     // suppress  any further requests.
  150.                     this.textureInitializationFailed = true;
  151.                     return null;
  152.                 }
  153.  
  154.                 return AWTTextureIO.newTextureData(Configuration.getMaxCompatibleGLProfile(), image,
  155.                     this.isUseMipMaps());
  156.             }
  157.             catch (Exception e)
  158.             {
  159.                 String msg = Logging.getMessage("Symbology.ExceptionRetrievingTacticalIcon", this.getImageSource());
  160.                 Logging.logger().log(java.util.logging.Level.SEVERE, msg, e);
  161.                 this.textureInitializationFailed = true; // Suppress subsequent requests for this tactical icon.
  162.                 return null;
  163.             }
  164.         }
  165.  
  166.         @Override
  167.         protected Runnable createRequestTask()
  168.         {
  169.             return new IconRequestTask(this);
  170.         }
  171.  
  172.         protected static class IconRequestTask implements Runnable
  173.         {
  174.             protected final IconTexture texture;
  175.  
  176.             protected IconRequestTask(IconTexture texture)
  177.             {
  178.                 if (texture == null)
  179.                 {
  180.                     String message = Logging.getMessage("nullValue.TextureIsNull");
  181.                     Logging.logger().severe(message);
  182.                     throw new IllegalArgumentException(message);
  183.                 }
  184.  
  185.                 this.texture = texture;
  186.             }
  187.  
  188.             public void run()
  189.             {
  190.                 if (Thread.currentThread().isInterrupted())
  191.                     return; // the task was cancelled because it's a duplicate or for some other reason
  192.  
  193.                 if (this.texture.loadTextureData())
  194.                     this.texture.notifyTextureLoaded();
  195.             }
  196.  
  197.             public boolean equals(Object o)
  198.             {
  199.                 if (this == o)
  200.                     return true;
  201.                 if (o == null || getClass() != o.getClass())
  202.                     return false;
  203.  
  204.                 final IconRequestTask that = (IconRequestTask) o;
  205.                 return this.texture != null ? this.texture.equals(that.texture) : that.texture == null;
  206.             }
  207.  
  208.             public int hashCode()
  209.             {
  210.                 return (this.texture != null ? this.texture.hashCode() : 0);
  211.             }
  212.  
  213.             public String toString()
  214.             {
  215.                 return this.texture.getImageSource().toString();
  216.             }
  217.         }
  218.     }
  219.  
  220.     protected static class IconAtlasElement extends TextureAtlasElement
  221.     {
  222.         protected Point point;
  223.         /** Indicates the last time, in milliseconds, the element was requested or added. */
  224.         protected long lastUsed = System.currentTimeMillis();
  225.  
  226.         public IconAtlasElement(TextureAtlas atlas, IconSource source)
  227.         {
  228.             super(atlas, source);
  229.         }
  230.  
  231.         public Point getPoint()
  232.         {
  233.             return this.point;
  234.         }
  235.  
  236.         public void setPoint(Point point)
  237.         {
  238.             this.point = point;
  239.         }
  240.  
  241.         @Override
  242.         protected boolean loadImage()
  243.         {
  244.             BufferedImage image = this.createModifierImage();
  245.  
  246.             if (image != null)
  247.                 this.setImage(image);
  248.  
  249.             return image != null;
  250.         }
  251.  
  252.         protected BufferedImage createModifierImage()
  253.         {
  254.             try
  255.             {
  256.                 IconSource source = (IconSource) this.getImageSource();
  257.                 BufferedImage image = source.getRetriever().createIcon(source.getSymbolId(),
  258.                     source.getRetrieverParams());
  259.  
  260.                 if (image == null)
  261.                 {
  262.                     // ModifierRetriever returns null if the modifier or its value is not recognized. In either case, we
  263.                     // mark the image initialization as having failed to suppress any further requests.
  264.                     this.imageInitializationFailed = true;
  265.                     return null;
  266.                 }
  267.  
  268.                 return image;
  269.             }
  270.             catch (Exception e)
  271.             {
  272.                 String msg = Logging.getMessage("Symbology.ExceptionRetrievingGraphicModifier",
  273.                     this.getImageSource());
  274.                 Logging.logger().log(java.util.logging.Level.SEVERE, msg, e);
  275.                 this.imageInitializationFailed = true; // Suppress subsequent requests for this modifier.
  276.                 return null;
  277.             }
  278.         }
  279.     }
  280.  
  281.     protected static class Label
  282.     {
  283.         protected String text;
  284.         protected Point point;
  285.         protected Font font;
  286.         protected Color color;
  287.  
  288.         public Label(String text, Point point, Font font, Color color)
  289.         {
  290.             if (text == null)
  291.             {
  292.                 String msg = Logging.getMessage("nullValue.StringIsNull");
  293.                 Logging.logger().severe(msg);
  294.                 throw new IllegalArgumentException(msg);
  295.             }
  296.  
  297.             if (point == null)
  298.             {
  299.                 String msg = Logging.getMessage("nullValue.PointIsNull");
  300.                 Logging.logger().severe(msg);
  301.                 throw new IllegalArgumentException(msg);
  302.             }
  303.  
  304.             if (font == null)
  305.             {
  306.                 String msg = Logging.getMessage("nullValue.FontIsNull");
  307.                 Logging.logger().severe(msg);
  308.                 throw new IllegalArgumentException(msg);
  309.             }
  310.  
  311.             if (color == null)
  312.             {
  313.                 String msg = Logging.getMessage("nullValue.ColorIsNull");
  314.                 Logging.logger().severe(msg);
  315.                 throw new IllegalArgumentException(msg);
  316.             }
  317.  
  318.             this.text = text;
  319.             this.point = point;
  320.             this.font = font;
  321.             this.color = color;
  322.         }
  323.  
  324.         public String getText()
  325.         {
  326.             return this.text;
  327.         }
  328.  
  329.         public Point getPoint()
  330.         {
  331.             return this.point;
  332.         }
  333.  
  334.         public Font getFont()
  335.         {
  336.             return this.font;
  337.         }
  338.  
  339.         public Color getColor()
  340.         {
  341.             return this.color;
  342.         }
  343.     }
  344.  
  345.     protected static class Line
  346.     {
  347.         protected Iterable<? extends Point2D> points;
  348.  
  349.         public Line()
  350.         {
  351.         }
  352.  
  353.         public Line(Iterable<? extends Point2D> points)
  354.         {
  355.             if (points == null)
  356.             {
  357.                 String msg = Logging.getMessage("nullValue.IterableIsNull");
  358.                 Logging.logger().severe(msg);
  359.                 throw new IllegalArgumentException(msg);
  360.             }
  361.  
  362.             this.points = points;
  363.         }
  364.  
  365.         public Iterable<? extends Point2D> getPoints()
  366.         {
  367.             return points;
  368.         }
  369.  
  370.         public void setPoints(Iterable<? extends Point2D> points)
  371.         {
  372.             this.points = points;
  373.         }
  374.     }
  375.  
  376.     protected class OrderedSymbol implements OrderedRenderable
  377.     {
  378.         /**
  379.          * Per-frame Cartesian point corresponding to this symbol's position. Calculated each frame in {@link
  380.          * gov.nasa.worldwind.symbology.AbstractTacticalSymbol#computeSymbolPoints(gov.nasa.worldwind.render.DrawContext,
  381.          * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}. Initially <code>null</code>.
  382.          */
  383.         public Vec4 placePoint;
  384.         /**
  385.          * Per-frame screen point corresponding to the projection of the placePoint in the viewport (on the screen).
  386.          * Calculated each frame in {@link gov.nasa.worldwind.symbology.AbstractTacticalSymbol#computeSymbolPoints(gov.nasa.worldwind.render.DrawContext,
  387.          * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}. Initially <code>null</code>.
  388.          */
  389.         public Vec4 screenPoint;
  390.         /**
  391.          * Per-frame distance corresponding to the distance between the placePoint and the View's eye point. Used to
  392.          * order the symbol as an ordered renderable, and is returned by getDistanceFromEye. Calculated each frame in
  393.          * {@link gov.nasa.worldwind.symbology.AbstractTacticalSymbol#computeSymbolPoints(gov.nasa.worldwind.render.DrawContext,
  394.          * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}. Initially 0.
  395.          */
  396.         public double eyeDistance;
  397.         /**
  398.          * Per-frame screen scale indicating this symbol's x-scale relative to the screen offset. Calculated each frame
  399.          * in {@link #computeTransform(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}.
  400.          * Initially 0.
  401.          */
  402.         public double sx;
  403.         /**
  404.          * Per-frame screen scale indicating this symbol's y-scale relative to the screen offset. Calculated each frame
  405.          * in {@link #computeTransform(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}.
  406.          * Initially 0.
  407.          */
  408.         public double sy;
  409.         /**
  410.          * Per-frame screen offset indicating this symbol's x-offset relative to the screenPoint. Calculated each frame
  411.          * in {@link #computeTransform(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}.
  412.          * Initially 0.
  413.          */
  414.         public double dx;
  415.         /**
  416.          * Per-frame screen offset indicating this symbol's y-offset relative to the screenPoint. Calculated each frame
  417.          * in {@link #computeTransform(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}.
  418.          * Initially 0.
  419.          */
  420.         public double dy;
  421.  
  422.         public Rectangle layoutRect;
  423.         public Rectangle screenRect;
  424.  
  425.         /** iconRect with scaling applied, used to lay out text. */
  426.         public Rectangle iconRectScaled;
  427.         /** layoutRect with scaling applied, used to lay out text. */
  428.         public Rectangle layoutRectScaled;
  429.         public Vec4 terrainPoint;
  430.  
  431.         @Override
  432.         public double getDistanceFromEye()
  433.         {
  434.             return this.eyeDistance;
  435.         }
  436.  
  437.         @Override
  438.         public void pick(DrawContext dc, Point pickPoint)
  439.         {
  440.             AbstractTacticalSymbol.this.pick(dc, pickPoint, this);
  441.         }
  442.  
  443.         @Override
  444.         public void render(DrawContext dc)
  445.         {
  446.             AbstractTacticalSymbol.this.drawOrderedRenderable(dc, this);
  447.         }
  448.  
  449.         public boolean isEnableBatchRendering()
  450.         {
  451.             return AbstractTacticalSymbol.this.isEnableBatchRendering();
  452.         }
  453.  
  454.         protected void doDrawOrderedRenderable(DrawContext dc, PickSupport pickCandidates)
  455.         {
  456.             AbstractTacticalSymbol.this.doDrawOrderedRenderable(dc, pickCandidates, this);
  457.         }
  458.  
  459.         public boolean isEnableBatchPicking()
  460.         {
  461.             return AbstractTacticalSymbol.this.isEnableBatchPicking();
  462.         }
  463.  
  464.         public Layer getPickLayer()
  465.         {
  466.             return AbstractTacticalSymbol.this.pickLayer;
  467.         }
  468.     }
  469.  
  470.     /** Default unit format. */
  471.     public static final UnitsFormat DEFAULT_UNITS_FORMAT = new UnitsFormat();
  472.  
  473.     /** The image file displayed while the icon is loading. */
  474.     public static final String LOADING_IMAGE_PATH =
  475.         Configuration.getStringValue("gov.nasa.worldwind.avkey.MilStd2525LoadingIconPath",
  476.             "images/doc-loading-128x128.png");
  477.  
  478.     protected static final String LAYOUT_ABSOLUTE = "gov.nasa.worldwind.symbology.TacticalSymbol.LayoutAbsolute";
  479.     protected static final String LAYOUT_RELATIVE = "gov.nasa.worldwind.symbology.TacticalSymbol.LayoutRelative";
  480.     protected static final String LAYOUT_NONE = "gov.nasa.worldwind.symbology.TacticalSymbol.LayoutNone";
  481.     /**
  482.      * The default depth offset in device independent depth units: -8200. This value is configured to match the depth
  483.      * offset produced by existing screen elements such as PointPlacemark. This value was determined empirically.
  484.      */
  485.     protected static final double DEFAULT_DEPTH_OFFSET = -8200;
  486.     protected static final long DEFAULT_MAX_TIME_SINCE_LAST_USED = 10000;
  487.     /**
  488.      * The default glyph texture atlas. This texture atlas holds all glyph images loaded by calls to
  489.      * <code>layoutGlyphModifier</code>. Initialized with initial dimensions of 1024x128 and maximum dimensions of
  490.      * 2048x2048. Configured to remove the least recently used texture elements when more space is needed.
  491.      */
  492.     protected static final TextureAtlas DEFAULT_GLYPH_ATLAS = new TextureAtlas(1024, 128, 2048, 2048);
  493.     /**
  494.      * Maximum expected size of a symbol, used to estimate screen bounds for view frustum culling. This value is
  495.      * configured a bit higher than a symbol is likely to be drawn in practice to err on the side of not culling a
  496.      * symbol that is not visible, rather culling one that is visible.
  497.      */
  498.     protected static final int MAX_SYMBOL_DIMENSION = 256;
  499.     /** The default number of label lines to expect when computing the minimum size of the text layout rectangle. */
  500.     protected static final int DEFAULT_LABEL_LINES = 5;
  501.  
  502.     /** The attributes used if attributes are not specified. */
  503.     protected static TacticalSymbolAttributes defaultAttrs;
  504.  
  505.     static
  506.     {
  507.         // Create and populate the default attributes.
  508.         defaultAttrs = new BasicTacticalSymbolAttributes();
  509.         defaultAttrs.setOpacity(BasicTacticalSymbolAttributes.DEFAULT_OPACITY);
  510.         defaultAttrs.setScale(BasicTacticalSymbolAttributes.DEFAULT_SCALE);
  511.         defaultAttrs.setTextModifierMaterial(BasicTacticalSymbolAttributes.DEFAULT_TEXT_MODIFIER_MATERIAL);
  512.  
  513.         // Configure the atlas to remove old texture elements that are likely no longer used to make room for new
  514.         // modifiers when the atlas is full.
  515.         DEFAULT_GLYPH_ATLAS.setEvictOldElements(true);
  516.     }
  517.  
  518.     /**
  519.      * Indicates whether this symbol is drawn when in view. <code>true</code> if this symbol is drawn when in view,
  520.      * otherwise <code>false</code>. Initially <code>true</code>.
  521.      */
  522.     protected boolean visible = true;
  523.     /**
  524.      * Indicates whether this symbol is highlighted. <code>true</code> if this symbol is highlighted, otherwise
  525.      * <code>false</code>. Initially <code>false</code>.
  526.      */
  527.     protected boolean highlighted;
  528.     /**
  529.      * Indicates this symbol's geographic position. See {@link #setPosition(gov.nasa.worldwind.geom.Position)} for a
  530.      * description of how tactical symbols interpret their position. Must be non-null, and is initialized during
  531.      * construction.
  532.      */
  533.     protected Position position;
  534.     /**
  535.      * Indicates this symbol's altitude mode. See {@link #setAltitudeMode(int)} for a description of the valid altitude
  536.      * modes. Initially Worldwind.ABSOLUTE.
  537.      */
  538.     protected int altitudeMode = WorldWind.ABSOLUTE;
  539.     /**
  540.      * Indicates whether this symbol draws its supplemental graphic modifiers. <code>true</code> if this symbol draws
  541.      * its graphic modifiers, otherwise <code>false</code>. Initially <code>true</code>.
  542.      */
  543.     protected boolean showGraphicModifiers = true;
  544.     /**
  545.      * Indicates whether this symbol draws its supplemental text modifiers. <code>true</code> if this symbol draws its
  546.      * text modifiers, otherwise <code>false</code>. Initially <code>true</code>.
  547.      */
  548.     protected boolean showTextModifiers = true;
  549.  
  550.     /** Indicates an object to attach to the picked object list instead of this symbol. */
  551.     protected Object delegateOwner;
  552.     protected boolean enableBatchRendering = true;
  553.     protected boolean enableBatchPicking = true;
  554.     /** Indicates whether or not to display the implicit location modifier. */
  555.     protected boolean showLocation = true;
  556.     /** Indicates whether or not to display the implicit hostile indicator modifier. */
  557.     protected boolean showHostileIndicator;
  558.     /**
  559.      * Indicates the current text and graphic modifiers assigned to this symbol. This list of key-value pairs contains
  560.      * both the modifiers specified by the string identifier during construction, and those specified by calling {@link
  561.      * #setModifier(String, Object)}. Initialized to a new AVListImpl, and populated during construction from values in
  562.      * the string identifier and the modifiers list.
  563.      */
  564.     protected AVList modifiers = new AVListImpl();
  565.     /**
  566.      * Modifiers active this frame. This list is determined by copying {@link #modifiers}, and applying changings in
  567.      * {@link #applyImplicitModifiers(gov.nasa.worldwind.avlist.AVList)}.
  568.      */
  569.     protected AVList activeModifiers = new AVListImpl();
  570.     /**
  571.      * Indicates this symbol's normal (as opposed to highlight) attributes. May be <code>null</code>, indicating that
  572.      * the default attributes are used. Initially <code>null</code>.
  573.      */
  574.     protected TacticalSymbolAttributes normalAttrs;
  575.     /**
  576.      * Indicates this symbol's highlight attributes. May be <code>null</code>, indicating that the default attributes
  577.      * are used. Initially <code>null</code>.
  578.      */
  579.     protected TacticalSymbolAttributes highlightAttrs;
  580.     /**
  581.      * Indicates this symbol's currently active attributes. Updated in {@link #determineActiveAttributes}. Initialized
  582.      * to a new BasicTacticalSymbolAttributes.
  583.      */
  584.     protected TacticalSymbolAttributes activeAttrs = new BasicTacticalSymbolAttributes();
  585.     protected Offset offset;
  586.     protected Offset iconOffset;
  587.     protected Size iconSize;
  588.     protected Double depthOffset;
  589.     protected IconRetriever iconRetriever;
  590.     protected IconRetriever modifierRetriever;
  591.  
  592.     /**
  593.      * The frame used to calculate this symbol's per-frame values. Set to the draw context's frame number each frame.
  594.      * Initially -1.
  595.      */
  596.     protected long frameNumber = -1;
  597.     protected OrderedSymbol thisFramesOrderedSymbol;
  598.  
  599.     protected Rectangle iconRect;
  600.  
  601.     /**
  602.      * Screen rect computed from the icon and static modifiers. This rectangle is cached and only recomputed when the
  603.      * icon or modifiers change.
  604.      */
  605.     protected Rectangle staticScreenRect;
  606.     /**
  607.      * Layout rect computed from the icon and static modifiers. This rectangle is cached and only recomputed when the
  608.      * icon or modifiers change.
  609.      */
  610.     protected Rectangle staticLayoutRect;
  611.  
  612.     /** Indicates that one or more glyphs have not been resolved. */
  613.     protected boolean unresolvedGlyph;
  614.  
  615.     protected List<IconAtlasElement> currentGlyphs = new ArrayList<IconAtlasElement>();
  616.     protected List<Label> currentLabels = new ArrayList<Label>();
  617.     protected List<Line> currentLines = new ArrayList<Line>();
  618.  
  619.     protected WWTexture iconTexture;
  620.     protected WWTexture activeIconTexture;
  621.     protected TextureAtlas glyphAtlas;
  622.     protected Map<String, IconAtlasElement> glyphMap = new HashMap<String, IconAtlasElement>();
  623.     protected long maxTimeSinceLastUsed = DEFAULT_MAX_TIME_SINCE_LAST_USED;
  624.  
  625.     /** Unit format used to format location and altitude for text modifiers. */
  626.     protected UnitsFormat unitsFormat = DEFAULT_UNITS_FORMAT;
  627.     /** Current symbol position, formatted using the current unit format. */
  628.     protected String formattedPosition;
  629.  
  630.     /**
  631.      * Support for setting up and restoring OpenGL state during rendering. Initialized to a new OGLStackHandler, and
  632.      * used in {@link #beginDrawing(gov.nasa.worldwind.render.DrawContext, int)} and {@link
  633.      * #endDrawing(gov.nasa.worldwind.render.DrawContext)}.
  634.      */
  635.     protected OGLStackHandler BEogsh = new OGLStackHandler();
  636.     /**
  637.      * Support for setting up and restoring picking state, and resolving the picked object. Initialized to a new
  638.      * PickSupport, and used in {@link #pick(gov.nasa.worldwind.render.DrawContext, java.awt.Point,
  639.      * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)}.
  640.      */
  641.     protected PickSupport pickSupport = new PickSupport();
  642.     /**
  643.      * Per-frame layer indicating this symbol's layer when its ordered renderable was created. Assigned each frame in
  644.      * {@link #makeOrderedRenderable(gov.nasa.worldwind.render.DrawContext)}. Used to define the picked object's layer
  645.      * during pick resolution. Initially <code>null</code>.
  646.      */
  647.     protected Layer pickLayer;
  648.     /**
  649.      * The LOD selector specified by the application, or null if none specified (the default).
  650.      */
  651.     protected LODSelector LODSelector;
  652.  
  653.     /** Constructs a new symbol with no position. */
  654.     protected AbstractTacticalSymbol()
  655.     {
  656.         this.setGlyphAtlas(DEFAULT_GLYPH_ATLAS);
  657.     }
  658.  
  659.     /**
  660.      * Constructs a new symbol with the specified position. The position specifies the latitude, longitude, and altitude
  661.      * where this symbol is drawn on the globe. The position's altitude component is interpreted according to the
  662.      * altitudeMode.
  663.      *
  664.      * @param position the latitude, longitude, and altitude where the symbol is drawn.
  665.      *
  666.      * @throws IllegalArgumentException if the position is <code>null</code>.
  667.      */
  668.     protected AbstractTacticalSymbol(Position position)
  669.     {
  670.         if (position == null)
  671.         {
  672.             String msg = Logging.getMessage("nullValue.PositionIsNull");
  673.             Logging.logger().severe(msg);
  674.             throw new IllegalArgumentException(msg);
  675.         }
  676.  
  677.         this.position = position;
  678.         this.setGlyphAtlas(DEFAULT_GLYPH_ATLAS);
  679.     }
  680.  
  681.     /** {@inheritDoc} */
  682.     public boolean isVisible()
  683.     {
  684.         return this.visible;
  685.     }
  686.  
  687.     /** {@inheritDoc} */
  688.     public void setVisible(boolean visible)
  689.     {
  690.         this.visible = visible;
  691.     }
  692.  
  693.     /** {@inheritDoc} */
  694.     public boolean isHighlighted()
  695.     {
  696.         return this.highlighted;
  697.     }
  698.  
  699.     /** {@inheritDoc} */
  700.     public void setHighlighted(boolean highlighted)
  701.     {
  702.         this.highlighted = highlighted;
  703.     }
  704.  
  705.     /** {@inheritDoc} */
  706.     public Position getPosition()
  707.     {
  708.         return this.position;
  709.     }
  710.  
  711.     /** {@inheritDoc} */
  712.     public void setPosition(Position position)
  713.     {
  714.         if (position == null)
  715.         {
  716.             String msg = Logging.getMessage("nullValue.PositionIsNull");
  717.             Logging.logger().severe(msg);
  718.             throw new IllegalArgumentException(msg);
  719.         }
  720.  
  721.         // If a new position is set then it must be reformatted.
  722.         if (!position.equals(this.position))
  723.             this.formattedPosition = null;
  724.  
  725.         this.position = position;
  726.     }
  727.  
  728.     /** {@inheritDoc} */
  729.     public int getAltitudeMode()
  730.     {
  731.         return this.altitudeMode;
  732.     }
  733.  
  734.     /** {@inheritDoc} */
  735.     public void setAltitudeMode(int altitudeMode)
  736.     {
  737.         this.altitudeMode = altitudeMode;
  738.     }
  739.  
  740.     /** {@inheritDoc} */
  741.     public boolean isShowGraphicModifiers()
  742.     {
  743.         return this.showGraphicModifiers;
  744.     }
  745.  
  746.     /** {@inheritDoc} */
  747.     public void setShowGraphicModifiers(boolean showGraphicModifiers)
  748.     {
  749.         if (this.showGraphicModifiers == showGraphicModifiers)
  750.             return;
  751.  
  752.         this.showGraphicModifiers = showGraphicModifiers;
  753.         this.reset();
  754.     }
  755.  
  756.     /** {@inheritDoc} */
  757.     public boolean isShowTextModifiers()
  758.     {
  759.         return this.showTextModifiers;
  760.     }
  761.  
  762.     /** {@inheritDoc} */
  763.     public void setShowTextModifiers(boolean showTextModifiers)
  764.     {
  765.         if (this.showTextModifiers == showTextModifiers)
  766.             return;
  767.  
  768.         this.showTextModifiers = showTextModifiers;
  769.         this.reset();
  770.     }
  771.  
  772.     /** {@inheritDoc} */
  773.     public boolean isShowLocation()
  774.     {
  775.         return this.showLocation;
  776.     }
  777.  
  778.     /** {@inheritDoc} */
  779.     public void setShowLocation(boolean show)
  780.     {
  781.         this.showLocation = show;
  782.     }
  783.  
  784.     /** {@inheritDoc} */
  785.     public boolean isShowHostileIndicator()
  786.     {
  787.         return this.showHostileIndicator;
  788.     }
  789.  
  790.     /** {@inheritDoc} */
  791.     public void setShowHostileIndicator(boolean show)
  792.     {
  793.         this.showHostileIndicator = show;
  794.     }
  795.  
  796.     public boolean isEnableBatchRendering()
  797.     {
  798.         return this.enableBatchRendering;
  799.     }
  800.  
  801.     public void setEnableBatchRendering(boolean enableBatchRendering)
  802.     {
  803.         this.enableBatchRendering = enableBatchRendering;
  804.     }
  805.  
  806.     public boolean isEnableBatchPicking()
  807.     {
  808.         return this.enableBatchPicking;
  809.     }
  810.  
  811.     public void setEnableBatchPicking(boolean enableBatchPicking)
  812.     {
  813.         this.enableBatchPicking = enableBatchPicking;
  814.     }
  815.  
  816.     /** {@inheritDoc} */
  817.     public Object getModifier(String modifier)
  818.     {
  819.         if (modifier == null)
  820.         {
  821.             String msg = Logging.getMessage("nullValue.ModifierIsNull");
  822.             Logging.logger().severe(msg);
  823.             throw new IllegalArgumentException(msg);
  824.         }
  825.  
  826.         return this.modifiers.getValue(modifier);
  827.     }
  828.  
  829.     /** {@inheritDoc} */
  830.     public void setModifier(String modifier, Object value)
  831.     {
  832.         if (modifier == null)
  833.         {
  834.             String msg = Logging.getMessage("nullValue.ModifierIsNull");
  835.             Logging.logger().severe(msg);
  836.             throw new IllegalArgumentException(msg);
  837.         }
  838.  
  839.         this.modifiers.setValue(modifier, value);
  840.     }
  841.  
  842.     /** {@inheritDoc} */
  843.     public TacticalSymbolAttributes getAttributes()
  844.     {
  845.         return this.normalAttrs;
  846.     }
  847.  
  848.     /** {@inheritDoc} */
  849.     public void setAttributes(TacticalSymbolAttributes normalAttrs)
  850.     {
  851.         this.normalAttrs = normalAttrs; // Null is accepted, and indicates the default attributes are used.
  852.     }
  853.  
  854.     /** {@inheritDoc} */
  855.     public TacticalSymbolAttributes getHighlightAttributes()
  856.     {
  857.         return this.highlightAttrs;
  858.     }
  859.  
  860.     /** {@inheritDoc} */
  861.     public void setHighlightAttributes(TacticalSymbolAttributes highlightAttrs)
  862.     {
  863.         this.highlightAttrs = highlightAttrs; // Null is accepted, and indicates the default highlight attributes.
  864.     }
  865.  
  866.     /** {@inheritDoc} */
  867.     public Object getDelegateOwner()
  868.     {
  869.         return this.delegateOwner;
  870.     }
  871.  
  872.     /** {@inheritDoc} */
  873.     public void setDelegateOwner(Object owner)
  874.     {
  875.         this.delegateOwner = owner;
  876.     }
  877.  
  878.     /** {@inheritDoc} */
  879.     public UnitsFormat getUnitsFormat()
  880.     {
  881.         return this.unitsFormat;
  882.     }
  883.  
  884.     /** {@inheritDoc} */
  885.     public void setUnitsFormat(UnitsFormat unitsFormat)
  886.     {
  887.         if (unitsFormat == null)
  888.         {
  889.             String msg = Logging.getMessage("nullValue.Format");
  890.             Logging.logger().severe(msg);
  891.             throw new IllegalArgumentException(msg);
  892.         }
  893.  
  894.         // If the unit format is changing then the position needs to be reformatted.
  895.         if (this.unitsFormat != unitsFormat)
  896.             this.formattedPosition = null;
  897.  
  898.         this.unitsFormat = unitsFormat;
  899.     }
  900.  
  901.     @Override
  902.     public LODSelector getLODSelector()
  903.     {
  904.         return LODSelector;
  905.     }
  906.  
  907.     @Override
  908.     public void setLODSelector(LODSelector LODSelector)
  909.     {
  910.         this.LODSelector = LODSelector;
  911.     }
  912.  
  913.     /** {@inheritDoc} */
  914.     public Position getReferencePosition()
  915.     {
  916.         return this.getPosition();
  917.     }
  918.  
  919.     /** {@inheritDoc} */
  920.     public void move(Position delta)
  921.     {
  922.         if (delta == null)
  923.         {
  924.             String msg = Logging.getMessage("nullValue.PositionIsNull");
  925.             Logging.logger().severe(msg);
  926.             throw new IllegalArgumentException(msg);
  927.         }
  928.  
  929.         Position refPos = this.getReferencePosition();
  930.  
  931.         // The reference position is null if this shape has positions. With PointPlacemark, this should never happen
  932.         // because its position must always be non-null. We check and this case anyway to handle a subclass overriding
  933.         // getReferencePosition and returning null. In this case moving the shape by a relative delta is meaningless
  934.         // because the shape has no geographic location. Therefore we fail softly by exiting and doing nothing.
  935.         if (refPos == null)
  936.             return;
  937.  
  938.         this.moveTo(refPos.add(delta));
  939.     }
  940.  
  941.     /** {@inheritDoc} */
  942.     public void moveTo(Position position)
  943.     {
  944.         if (position == null)
  945.         {
  946.             String msg = Logging.getMessage("nullValue.PositionIsNull");
  947.             Logging.logger().severe(msg);
  948.             throw new IllegalArgumentException(msg);
  949.         }
  950.  
  951.         this.setPosition(position);
  952.     }
  953.  
  954.     /**
  955.      * Indicates a location within the symbol to align with the symbol point. See {@link
  956.      * #setOffset(gov.nasa.worldwind.render.Offset) setOffset} for more information.
  957.      *
  958.      * @return the hot spot controlling the symbol's placement relative to the symbol point. null indicates default
  959.      * alignment.
  960.      */
  961.     public Offset getOffset()
  962.     {
  963.         return this.offset;
  964.     }
  965.  
  966.     /**
  967.      * Specifies a location within the tactical symbol to align with the symbol point. By default, ground symbols are
  968.      * aligned at the bottom center of the symbol, and other symbols are aligned to the center of the symbol. {@code
  969.      * setOffset(Offset.CENTER)} aligns the center of the symbol with the symbol point, and {@code
  970.      * setOffset(Offset.BOTTOM_CENTER)} aligns the center of the bottom edge with the symbol point.
  971.      *
  972.      * @param offset the hot spot controlling the symbol's placement relative to the symbol point. May be null to
  973.      *               indicate default alignment.
  974.      */
  975.     public void setOffset(Offset offset)
  976.     {
  977.         this.offset = offset;
  978.     }
  979.  
  980.     /**
  981.      * Indicates the symbol's current position, formatted according to the current UnitsFormat.
  982.      *
  983.      * @return The current position formatted according to the current unit format. Returns null if the position is
  984.      * null.
  985.      */
  986.     protected String getFormattedPosition()
  987.     {
  988.         Position position = this.getPosition();
  989.         if (position == null)
  990.             return null;
  991.  
  992.         // Format the position to a string only when necessary. formattedPosition is set to null when either the
  993.         // position or the units format is changed.
  994.         if (this.formattedPosition == null)
  995.             this.formattedPosition = this.getUnitsFormat().latLon(position);
  996.  
  997.         return this.formattedPosition;
  998.     }
  999.  
  1000.     protected Double getDepthOffset()
  1001.     {
  1002.         return this.depthOffset;
  1003.     }
  1004.  
  1005.     protected void setDepthOffset(Double depthOffset)
  1006.     {
  1007.         this.depthOffset = depthOffset; // Null is accepted, and indicates the default depth offset is used.
  1008.     }
  1009.  
  1010.     protected IconRetriever getIconRetriever()
  1011.     {
  1012.         return this.iconRetriever;
  1013.     }
  1014.  
  1015.     protected void setIconRetriever(IconRetriever retriever)
  1016.     {
  1017.         this.iconRetriever = retriever;
  1018.     }
  1019.  
  1020.     protected IconRetriever getModifierRetriever()
  1021.     {
  1022.         return this.modifierRetriever;
  1023.     }
  1024.  
  1025.     protected void setModifierRetriever(IconRetriever retriever)
  1026.     {
  1027.         this.modifierRetriever = retriever;
  1028.         this.reset();
  1029.     }
  1030.  
  1031.     protected TextureAtlas getGlyphAtlas()
  1032.     {
  1033.         return this.glyphAtlas;
  1034.     }
  1035.  
  1036.     protected void setGlyphAtlas(TextureAtlas atlas)
  1037.     {
  1038.         // Note that we do not explicitly remove this symbol's glyphs from the old atlas. The modifier texture atlas
  1039.         // should be  configured to evict the oldest glyphs when the atlas is full. Leaving this symbol's glyphs in the
  1040.         // atlas does not incur any additional overhead, and has the benefit of ensuring that we do not remove glyphs
  1041.         // used by another symbol.
  1042.  
  1043.         this.glyphAtlas = atlas;
  1044.     }
  1045.  
  1046.     public void pick(DrawContext dc, Point pickPoint, OrderedSymbol osym)
  1047.     {
  1048.         if (dc == null)
  1049.         {
  1050.             String msg = Logging.getMessage("nullValue.DrawContextIsNull");
  1051.             Logging.logger().severe(msg);
  1052.             throw new IllegalArgumentException(msg);
  1053.         }
  1054.  
  1055.         this.pickSupport.clearPickList();
  1056.         try
  1057.         {
  1058.             this.pickSupport.beginPicking(dc);
  1059.             this.drawOrderedRenderable(dc, osym);
  1060.         }
  1061.         finally
  1062.         {
  1063.             this.pickSupport.endPicking(dc);
  1064.             this.pickSupport.resolvePick(dc, pickPoint, this.pickLayer);
  1065.         }
  1066.     }
  1067.  
  1068.     /** {@inheritDoc} */
  1069.     public void render(DrawContext dc)
  1070.     {
  1071.         if (dc == null)
  1072.         {
  1073.             String msg = Logging.getMessage("nullValue.DrawContextIsNull");
  1074.             Logging.logger().severe(msg);
  1075.             throw new IllegalArgumentException(msg);
  1076.         }
  1077.  
  1078.         if (!this.isVisible())
  1079.             return;
  1080.  
  1081.         this.makeOrderedRenderable(dc);
  1082.     }
  1083.  
  1084.     protected void makeOrderedRenderable(DrawContext dc)
  1085.     {
  1086.         OrderedSymbol osym;
  1087.  
  1088.         // Calculate this symbol's per-frame values, re-using values already calculated this frame.
  1089.         if (dc.getFrameTimeStamp() != this.frameNumber || dc.isContinuous2DGlobe())
  1090.         {
  1091.             osym = new OrderedSymbol();
  1092.  
  1093.             // Compute the model and screen coordinate points corresponding to the position and altitude mode.
  1094.             this.computeSymbolPoints(dc, osym);
  1095.          
  1096.             if (osym.placePoint == null || osym.screenPoint == null)
  1097.                 return;
  1098.  
  1099.             // Don't draw if beyond the horizon.
  1100.             double horizon = dc.getView().getHorizonDistance();
  1101.             if (!dc.is2DGlobe() && osym.eyeDistance > horizon)
  1102.                 return;
  1103.  
  1104.             // If the symbol has never been laid out perform a frustum test using estimated screen bounds. If the symbol
  1105.             // is not visible, then don't compute layout. This avoids downloading icons and laying out symbols that are
  1106.             // not yet visible.
  1107.             if (osym.screenRect == null && !this.intersectsFrustum(dc, osym))
  1108.                 return;
  1109.  
  1110.             if (this.getLODSelector() != null)
  1111.                 this.getLODSelector().selectLOD(dc, this, osym.eyeDistance);
  1112.  
  1113.             // Compute the currently active attributes from either the normal or the highlight attributes.
  1114.             this.determineActiveAttributes();
  1115.             if (this.getActiveAttributes() == null)
  1116.                 return;
  1117.  
  1118.             // Compute the scale for this frame. This must happen before layout because the text layout may depend
  1119.             // on the scale.
  1120.             this.computeScale(osym);
  1121.  
  1122.             // Compute the icon and modifier layout.
  1123.             this.layout(dc, osym);
  1124.  
  1125.             // Compute the offset parameters that are applied during rendering. This must be done after
  1126.             // layout, because the transform depends on the frame rectangle computed during layout.
  1127.             this.computeTransform(dc, osym);
  1128.  
  1129.             this.frameNumber = dc.getFrameTimeStamp();
  1130.             this.thisFramesOrderedSymbol = osym;
  1131.         }
  1132.         else
  1133.         {
  1134.             osym = thisFramesOrderedSymbol;
  1135.         }
  1136.  
  1137.         // Determine if the symbol is visible, now that the layout is known.
  1138.         if (this.intersectsFrustum(dc, osym))
  1139.             dc.addOrderedRenderable(osym);
  1140.  
  1141.         if (dc.isPickingMode())
  1142.             this.pickLayer = dc.getCurrentLayer();
  1143.     }
  1144.  
  1145.     protected void computeSymbolPoints(DrawContext dc, OrderedSymbol osym)
  1146.     {
  1147.         osym.placePoint = null;
  1148.         osym.screenPoint = null;
  1149.         osym.terrainPoint = null;
  1150.         osym.eyeDistance = 0;
  1151.  
  1152.         Position pos = this.getPosition();
  1153.         if (pos == null)
  1154.             return;
  1155.  
  1156.         if (this.altitudeMode == WorldWind.CLAMP_TO_GROUND || dc.is2DGlobe())
  1157.         {
  1158.             osym.placePoint = dc.computeTerrainPoint(pos.getLatitude(), pos.getLongitude(), 0);
  1159.         }
  1160.         else if (this.altitudeMode == WorldWind.RELATIVE_TO_GROUND)
  1161.         {
  1162.             osym.placePoint = dc.computeTerrainPoint(pos.getLatitude(), pos.getLongitude(), pos.getAltitude());
  1163.         }
  1164.         else // Default to ABSOLUTE
  1165.         {
  1166.             double height = pos.getElevation() * dc.getVerticalExaggeration();
  1167.             osym.placePoint = dc.getGlobe().computePointFromPosition(pos.getLatitude(), pos.getLongitude(), height);
  1168.         }
  1169.  
  1170.         if (osym.placePoint == null)
  1171.             return;
  1172.  
  1173.         // Compute the symbol's screen location the distance between the eye point and the place point.
  1174.         osym.screenPoint = dc.getView().project(osym.placePoint);
  1175.         osym.eyeDistance = osym.placePoint.distanceTo3(dc.getView().getEyePoint());
  1176.        
  1177.         // Compute a terrain point if needed.
  1178.         if (this.isLineEnabled() && this.altitudeMode != WorldWind.CLAMP_TO_GROUND && !dc.is2DGlobe())
  1179.             osym.terrainPoint = dc.computeTerrainPoint(pos.getLatitude(), pos.getLongitude(), 0);
  1180.  
  1181.     }
  1182.  
  1183.     protected void determineActiveAttributes()
  1184.     {
  1185.         Font previousFont = this.activeAttrs.getTextModifierFont();
  1186.         Double previousScale = this.activeAttrs.getScale();
  1187.         Double previousOpacity = this.activeAttrs.getOpacity();
  1188.  
  1189.         if (this.isHighlighted())
  1190.         {
  1191.             if (this.getHighlightAttributes() != null)
  1192.                 this.activeAttrs.copy(this.getHighlightAttributes());
  1193.             else
  1194.             {
  1195.                 // If no highlight attributes have been specified we need to use either the normal or default attributes
  1196.                 // but adjust them to cause highlighting.
  1197.                 if (this.getAttributes() != null)
  1198.                     this.activeAttrs.copy(this.getAttributes());
  1199.                 else
  1200.                     this.activeAttrs.copy(defaultAttrs);
  1201.             }
  1202.         }
  1203.         else if (this.getAttributes() != null)
  1204.         {
  1205.             this.activeAttrs.copy(this.getAttributes());
  1206.         }
  1207.         else
  1208.         {
  1209.             this.activeAttrs.copy(defaultAttrs);
  1210.         }
  1211.  
  1212.         // If the font has changed since the last frame, then the layout needs to be recomputed since text may be a
  1213.         // different size.
  1214.         Font newFont = this.activeAttrs.getTextModifierFont();
  1215.         if (newFont != null && !newFont.equals(previousFont)
  1216.             || (newFont == null && previousFont != null))
  1217.         {
  1218.             this.reset();
  1219.         }
  1220.  
  1221.         // Opacity does not directly affect layout, but each label stores the opacity in its material. If the opacity
  1222.         // has changed, then recreate the labels.
  1223.         Double newOpacity = this.activeAttrs.getOpacity();
  1224.         if ((newOpacity != null && !newOpacity.equals(previousOpacity))
  1225.             || (newOpacity == null && previousOpacity != null))
  1226.         {
  1227.             this.reset();
  1228.         }
  1229.  
  1230.         // If the scale has changed then the layout needs to be recomputed.
  1231.         Double newScale = this.activeAttrs.getScale();
  1232.         if (newScale != null && !newScale.equals(previousScale)
  1233.             || (newScale == null && previousScale != null))
  1234.         {
  1235.             this.reset();
  1236.         }
  1237.     }
  1238.  
  1239.     protected TacticalSymbolAttributes getActiveAttributes()
  1240.     {
  1241.         return this.activeAttrs;
  1242.     }
  1243.  
  1244.     /** Invalidate the symbol layout, causing it to be recomputed on the next frame. */
  1245.     protected void reset()
  1246.     {
  1247.         this.staticScreenRect = null;
  1248.         this.staticLayoutRect = null;
  1249.     }
  1250.  
  1251.     protected void layout(DrawContext dc, OrderedSymbol osym)
  1252.     {
  1253.         AVList modifierParams = new AVListImpl();
  1254.         modifierParams.setValues(this.modifiers);
  1255.         this.applyImplicitModifiers(modifierParams);
  1256.  
  1257.         boolean mustDrawModifiers = this.mustDrawGraphicModifiers(dc) || this.mustDrawTextModifiers(dc);
  1258.  
  1259.         // If the icon retrieval parameters have changed then the icon needs to be updated, which may affect layout.
  1260.         AVList retrieverParams = this.assembleIconRetrieverParameters(null);
  1261.         IconSource iconSource = new IconSource(this.getIconRetriever(), this.getIdentifier(), retrieverParams);
  1262.  
  1263.         // Compute layout of icon and static modifiers only when necessary.
  1264.         if (this.mustLayout(iconSource, modifierParams) || dc.isContinuous2DGlobe())
  1265.         {
  1266.             osym.screenRect = null;
  1267.             osym.layoutRect = null;
  1268.  
  1269.             // Set the unresolved flag false. addGlyph will set it to true if there are still unresolved resources.
  1270.             this.unresolvedGlyph = false;
  1271.  
  1272.             if (this.mustDrawIcon(dc))
  1273.                 this.layoutIcon(dc, iconSource, osym);
  1274.  
  1275.             if (mustDrawModifiers)
  1276.                 this.layoutStaticModifiers(dc, modifierParams, osym);
  1277.  
  1278.             // Save the static layout to reuse on subsequent frames.
  1279.             this.staticScreenRect = new Rectangle(osym.screenRect);
  1280.             this.staticLayoutRect = new Rectangle(osym.layoutRect);
  1281.  
  1282.             // Save the active modifiers so that we can detect when they change.
  1283.             this.activeModifiers.setValues(modifierParams);
  1284.  
  1285.             this.removeDeadModifiers(System.currentTimeMillis());
  1286.         }
  1287.         else
  1288.         {
  1289.             // Reuse cached layout.
  1290.             osym.layoutRect = new Rectangle(this.staticLayoutRect);
  1291.             osym.screenRect = new Rectangle(this.staticScreenRect);
  1292.         }
  1293.  
  1294.         // Layout dynamic modifiers each frame because they are expected to change each frame.
  1295.         if (mustDrawModifiers)
  1296.             this.layoutDynamicModifiers(dc, modifierParams, osym);
  1297.     }
  1298.  
  1299.     /**
  1300.      * Determines if the icon layout or static modifier layout must be computed.
  1301.      *
  1302.      * @param iconSource Current icon source.
  1303.      * @param modifiers  Current modifiers.
  1304.      *
  1305.      * @return true if the layout must be recomputed.
  1306.      */
  1307.     protected boolean mustLayout(IconSource iconSource, AVList modifiers)
  1308.     {
  1309.         // If one or more glyphs need to be resolved, then layout is not complete.
  1310.         if (this.unresolvedGlyph)
  1311.             return true;
  1312.  
  1313.         // If there is no cached layout, then we need to layout.
  1314.         if (this.staticScreenRect == null || this.staticLayoutRect == null)
  1315.             return true;
  1316.  
  1317.         // If the modifiers have changed since layout was computed then it needs to be recomputed.
  1318.         if (!this.activeModifiers.getEntries().equals(modifiers.getEntries()))
  1319.             return true;
  1320.  
  1321.         // Layout may change if the icon is not update to date.
  1322.         if (this.iconTexture == null || this.iconTexture != this.activeIconTexture)
  1323.             return true;
  1324.  
  1325.         // If the icon retrieval parameters have changed then the icon needs to be updated, which may affect layout.
  1326.         return !this.iconTexture.getImageSource().equals(iconSource);
  1327.     }
  1328.  
  1329.     protected void layoutIcon(DrawContext dc, IconSource source, OrderedSymbol osym)
  1330.     {
  1331.         if (this.getIconRetriever() == null)
  1332.             return;
  1333.  
  1334.         // Lazily create the symbol icon texture when either the IconRetriever, the symbol ID, or the retriever
  1335.         // parameters change.
  1336.         if (this.iconTexture == null || !this.iconTexture.getImageSource().equals(source))
  1337.             this.iconTexture = new IconTexture(source);
  1338.  
  1339.         // Use the currently active icon texture until the new icon texture (if any) has successfully loaded. This
  1340.         // ensures that the old icon texture continues to display until the new icon texture is ready, and avoids
  1341.         // temporarily displaying nothing.
  1342.         if (this.activeIconTexture != this.iconTexture && this.iconTexture != null
  1343.             && this.iconTexture.bind(dc))
  1344.         {
  1345.             this.activeIconTexture = this.iconTexture;
  1346.             this.iconRect = null; // Recompute the icon rectangle when the active icon texture changes.
  1347.         }
  1348.  
  1349.         // If the icon is not available then draw a default icon. Only draw the default before any icon is loaded. If
  1350.         // the icon is changed after loading then we will continue to draw the old icon until the new one becomes
  1351.         // available rather than going back to the default icon.
  1352.         boolean textureLoaded = this.activeIconTexture != null;
  1353.         if (!textureLoaded)
  1354.         {
  1355.             this.activeIconTexture = new BasicWWTexture(LOADING_IMAGE_PATH);
  1356.             textureLoaded = this.activeIconTexture.bind(dc);
  1357.         }
  1358.  
  1359.         // Lazily compute the symbol icon rectangle only when necessary, and only after the symbol icon texture has
  1360.         // successfully loaded.
  1361.         if (this.iconRect == null && textureLoaded)
  1362.         {
  1363.             // Compute the symbol icon's frame rectangle in local coordinates. This is used by the modifier layout to
  1364.             // determine where to place modifier graphics and modifier text. Note that we bind the texture in order to
  1365.             // load the texture image, and make the width and height available.
  1366.             int w = this.activeIconTexture.getWidth(dc);
  1367.             int h = this.activeIconTexture.getHeight(dc);
  1368.             Point2D point = this.iconOffset != null ? this.iconOffset.computeOffset(w, h, null, null)
  1369.                 : new Point(0, 0);
  1370.             Dimension size = this.iconSize != null ? this.iconSize.compute(w, h, w, h) : new Dimension(w, h);
  1371.             this.iconRect = new Rectangle((int) point.getX(), (int) point.getY(), size.width, size.height);
  1372.         }
  1373.  
  1374.         // Add the symbol icon rectangle to the screen rectangle and layout rectangle every frame.
  1375.         if (this.iconRect != null)
  1376.         {
  1377.             if (osym.screenRect != null)
  1378.                 osym.screenRect.add(this.iconRect);
  1379.             else
  1380.                 osym.screenRect = new Rectangle(this.iconRect);
  1381.  
  1382.             if (osym.layoutRect != null)
  1383.                 osym.layoutRect.add(this.iconRect);
  1384.             else
  1385.                 osym.layoutRect = new Rectangle(this.iconRect);
  1386.         }
  1387.     }
  1388.  
  1389.     protected AVList assembleIconRetrieverParameters(AVList params)
  1390.     {
  1391.         if (params == null)
  1392.             params = new AVListImpl();
  1393.  
  1394.         Material interiorMaterial = this.getActiveAttributes().getInteriorMaterial();
  1395.         if (interiorMaterial != null)
  1396.             params.setValue(AVKey.COLOR, interiorMaterial.getDiffuse());
  1397.  
  1398.         return params;
  1399.     }
  1400.  
  1401.     /**
  1402.      * Layout static modifiers around the symbol. Static modifiers are not expected to change due to changes in view.
  1403.      * Subclasses should not override this method. Instead, subclasses may override {@link
  1404.      * #layoutGraphicModifiers(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.avlist.AVList,
  1405.      * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol) layoutGraphicModifiers} and {@link
  1406.      * #layoutTextModifiers(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.avlist.AVList,
  1407.      * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol) layoutTextModifiers}.
  1408.      *
  1409.      * @param dc        Current draw context.
  1410.      * @param modifiers Current modifiers.
  1411.      * @param osym      The OrderedSymbol to hold the per-frame data.
  1412.      *
  1413.      * @see #layoutDynamicModifiers(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.avlist.AVList,
  1414.      * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)
  1415.      * @see #layoutGraphicModifiers(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.avlist.AVList,
  1416.      * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)
  1417.      * @see #layoutTextModifiers(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.avlist.AVList,
  1418.      * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)
  1419.      */
  1420.     protected void layoutStaticModifiers(DrawContext dc, AVList modifiers, OrderedSymbol osym)
  1421.     {
  1422.         if (this.iconRect == null)
  1423.             return;
  1424.  
  1425.         if (this.mustDrawGraphicModifiers(dc))
  1426.             this.layoutGraphicModifiers(dc, modifiers, osym);
  1427.  
  1428.         // Compute the bounds of the symbol and graphic modifiers with scaling applied. The text will be laid out
  1429.         // based on this size (text is not scaled with the symbol).
  1430.         this.computeScaledBounds(dc, modifiers, osym);
  1431.  
  1432.         if (this.mustDrawTextModifiers(dc))
  1433.             this.layoutTextModifiers(dc, modifiers, osym);
  1434.     }
  1435.  
  1436.     /**
  1437.      * Layout static graphic modifiers around the symbol. Static modifiers are not expected to change due to changes in
  1438.      * view. The static layout is computed when a modifier is changed, but may not be computed each frame. For example,
  1439.      * a text modifier indicating a symbol identifier would only need to be laid out when the text is changed, so this
  1440.      * is best treated as a static modifier. However a direction of movement line that needs to be computed based on the
  1441.      * current eye position should be treated as a dynamic modifier.
  1442.      *
  1443.      * @param dc        Current draw context.
  1444.      * @param modifiers Current modifiers.
  1445.      * @param osym      The OrderedSymbol to hold the per-frame data.
  1446.      *
  1447.      * @see #layoutDynamicModifiers(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.avlist.AVList,
  1448.      * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)
  1449.      */
  1450.     protected void layoutGraphicModifiers(DrawContext dc, AVList modifiers, OrderedSymbol osym)
  1451.     {
  1452.         // Intentionally left blank. Subclasses can override this method in order to layout any modifiers associated
  1453.         // with this tactical symbol.
  1454.     }
  1455.  
  1456.     /**
  1457.      * Layout static text modifiers around the symbol. Static modifiers are not expected to change due to changes in
  1458.      * view. The static layout is computed when a modifier is changed, but may not be computed each frame. For example,
  1459.      * a text modifier indicating a symbol identifier would only need to be laid out when the text is changed, so this
  1460.      * is best treated as a static modifier. However a direction of movement line that needs to be computed based on the
  1461.      * current eye position should be treated as a dynamic modifier.
  1462.      *
  1463.      * @param dc        Current draw context.
  1464.      * @param modifiers Current modifiers.
  1465.      * @param osym      The OrderedSymbol to hold the per-frame data.
  1466.      *
  1467.      * @see #layoutDynamicModifiers(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.avlist.AVList,
  1468.      * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)
  1469.      */
  1470.     protected void layoutTextModifiers(DrawContext dc, AVList modifiers, OrderedSymbol osym)
  1471.     {
  1472.         // Intentionally left blank. Subclasses can override this method in order to layout any modifiers associated
  1473.         // with this tactical symbol.
  1474.     }
  1475.  
  1476.     /**
  1477.      * Layout dynamic modifiers around the symbol. Dynamic modifiers are expected to (potentially) change each frame,
  1478.      * and are laid out each frame. For example, a direction of movement line that is computed based on the current eye
  1479.      * position would be treated as a dynamic modifier. Dynamic modifiers are always laid out after static modifiers.
  1480.      *
  1481.      * @param dc        Current draw context.
  1482.      * @param modifiers Current modifiers.
  1483.      * @param osym      The OrderedSymbol to hold the per-frame data.
  1484.      *
  1485.      * @see #layoutStaticModifiers(gov.nasa.worldwind.render.DrawContext, gov.nasa.worldwind.avlist.AVList,
  1486.      * gov.nasa.worldwind.symbology.AbstractTacticalSymbol.OrderedSymbol)
  1487.      */
  1488.     protected void layoutDynamicModifiers(DrawContext dc, AVList modifiers, OrderedSymbol osym)
  1489.     {
  1490.         // Intentionally left blank. Subclasses can override this method in order to layout any modifiers associated
  1491.         // with this tactical symbol.
  1492.     }
  1493.  
  1494.     /**
  1495.      * Add implicit modifiers to the modifier list. This method is called each frame to add modifiers that are
  1496.      * determined implicitly by the symbol state, rather than set explicitly by the application. For example, the
  1497.      * location modifier can be determined by the symbol position without the application needing to specify it.
  1498.      *
  1499.      * @param modifiers List of modifiers. This method may modify this list by adding implicit modifiers.
  1500.      */
  1501.     protected void applyImplicitModifiers(AVList modifiers)
  1502.     {
  1503.         // Intentionally left blank. Subclasses can override this method in order to add modifiers that are implicitly
  1504.         // determined by the symbol state.
  1505.     }
  1506.  
  1507.     /**
  1508.      * Layout a rectangle relative to the current layout.
  1509.      *
  1510.      * @param offset     Offset into either the {@code iconRect} or {@code layoutRect} at which to align the hot spot.
  1511.      * @param hotspot    Offset into the rectangle of the hot spot.
  1512.      * @param size       Size of the rectangle.
  1513.      * @param layoutMode One of {@link #LAYOUT_ABSOLUTE}, {@link #LAYOUT_RELATIVE}, or {@link #LAYOUT_NONE}.
  1514.      * @param osym       The OrderedSymbol to hold the per-frame data.
  1515.      *
  1516.      * @return the laid out rectangle.
  1517.      */
  1518.     protected Rectangle layoutRect(Offset offset, Offset hotspot, Dimension size, Object layoutMode,
  1519.         OrderedSymbol osym)
  1520.     {
  1521.         int x = 0;
  1522.         int y = 0;
  1523.  
  1524.         if (offset != null)
  1525.         {
  1526.             Rectangle rect;
  1527.             if (LAYOUT_ABSOLUTE.equals(layoutMode))
  1528.                 rect = this.iconRect;
  1529.             else if (LAYOUT_RELATIVE.equals(layoutMode))
  1530.                 rect = osym.layoutRect;
  1531.             else // LAYOUT_NONE
  1532.                 rect = this.iconRect;
  1533.  
  1534.             Point2D p = offset.computeOffset(rect.getWidth(), rect.getHeight(), null, null);
  1535.             x += rect.getX() + p.getX();
  1536.             y += rect.getY() + p.getY();
  1537.         }
  1538.  
  1539.         if (hotspot != null)
  1540.         {
  1541.             Point2D p = hotspot.computeOffset(size.getWidth(), size.getHeight(), null, null);
  1542.             x -= p.getX();
  1543.             y -= p.getY();
  1544.         }
  1545.  
  1546.         Rectangle rect = new Rectangle(x, y, size.width, size.height);
  1547.  
  1548.         if (osym.screenRect != null)
  1549.             osym.screenRect.add(rect);
  1550.         else
  1551.             osym.screenRect = new Rectangle(rect);
  1552.  
  1553.         if (LAYOUT_ABSOLUTE.equals(layoutMode) || LAYOUT_RELATIVE.equals(layoutMode))
  1554.         {
  1555.             if (osym.layoutRect != null)
  1556.                 osym.layoutRect.add(rect);
  1557.             else
  1558.                 osym.layoutRect = new Rectangle(rect);
  1559.         }
  1560.  
  1561.         return rect;
  1562.     }
  1563.  
  1564.     /**
  1565.      * Layout a label rectangle relative to the current layout. This method lays out text around the icon and graphic
  1566.      * modifiers after scaling has been applied (text is not scaled with the icon).
  1567.      *
  1568.      * @param offset     Offset into either the {@code iconRect} or {@code layoutRect} at which to align the hot spot.
  1569.      * @param hotspot    Offset into the rectangle of the hot spot.
  1570.      * @param size       Size of the rectangle.
  1571.      * @param layoutMode One of {@link #LAYOUT_ABSOLUTE}, {@link #LAYOUT_RELATIVE}, or {@link #LAYOUT_NONE}.
  1572.      * @param osym       The OrderedSymbol to hold the per-frame data.
  1573.      *
  1574.      * @return the laid out rectangle.
  1575.      */
  1576.     protected Rectangle layoutLabelRect(Offset offset, Offset hotspot, Dimension size, Object layoutMode,
  1577.         OrderedSymbol osym)
  1578.     {
  1579.         int x = 0;
  1580.         int y = 0;
  1581.  
  1582.         if (offset != null)
  1583.         {
  1584.             Rectangle rect;
  1585.             if (LAYOUT_ABSOLUTE.equals(layoutMode))
  1586.                 rect = osym.iconRectScaled;
  1587.             else if (LAYOUT_RELATIVE.equals(layoutMode))
  1588.                 rect = osym.layoutRectScaled;
  1589.             else // LAYOUT_NONE
  1590.                 rect = osym.iconRectScaled;
  1591.  
  1592.             Point2D p = offset.computeOffset(rect.getWidth(), rect.getHeight(), null, null);
  1593.             x += rect.getX() + p.getX();
  1594.             y += rect.getY() + p.getY();
  1595.         }
  1596.  
  1597.         if (hotspot != null)
  1598.         {
  1599.             Point2D p = hotspot.computeOffset(size.getWidth(), size.getHeight(), null, null);
  1600.             x -= p.getX();
  1601.             y -= p.getY();
  1602.         }
  1603.  
  1604.         Rectangle rect = new Rectangle(x, y, size.width, size.height);
  1605.  
  1606.         if (LAYOUT_ABSOLUTE.equals(layoutMode) || LAYOUT_RELATIVE.equals(layoutMode))
  1607.         {
  1608.             if (osym.layoutRectScaled != null)
  1609.             {
  1610.                 osym.layoutRectScaled.add(rect);
  1611.             }
  1612.             else
  1613.                 osym.layoutRectScaled = new Rectangle(rect);
  1614.  
  1615.             // Compute where the label rectangle falls in the icon layout before scaling is applied. This is necessary
  1616.             // to layout graphic modifiers such as the ground direction of movement indicator that are scaled down with
  1617.             // the icon, but should not overlap text which is not scaled with the icon.
  1618.             Rectangle scaledRect = this.computeScaledRect(rect, rect.getSize(), 1 / osym.sx, 1 / osym.sy);
  1619.             if (osym.layoutRect != null)
  1620.                 osym.layoutRect.add(scaledRect);
  1621.             else
  1622.                 osym.layoutRect = new Rectangle(scaledRect);
  1623.         }
  1624.  
  1625.         return rect;
  1626.     }
  1627.  
  1628.     protected List<? extends Point2D> layoutPoints(Offset offset, List<? extends Point2D> points, Object layoutMode,
  1629.         int numPointsInLayout, OrderedSymbol osym)
  1630.     {
  1631.         int x = 0;
  1632.         int y = 0;
  1633.  
  1634.         if (offset != null)
  1635.         {
  1636.             Rectangle rect;
  1637.             if (LAYOUT_ABSOLUTE.equals(layoutMode))
  1638.                 rect = this.iconRect;
  1639.             else if (LAYOUT_RELATIVE.equals(layoutMode))
  1640.                 rect = osym.layoutRect;
  1641.             else // LAYOUT_NONE
  1642.                 rect = this.iconRect;
  1643.  
  1644.             Point2D p = offset.computeOffset(rect.getWidth(), rect.getHeight(), null, null);
  1645.             x += rect.getX() + p.getX();
  1646.             y += rect.getY() + p.getY();
  1647.         }
  1648.  
  1649.         for (int i = 0; i < points.size(); i++)
  1650.         {
  1651.             Point2D p = points.get(i);
  1652.             p.setLocation(x + p.getX(), y + p.getY());
  1653.  
  1654.             if (osym.screenRect != null)
  1655.                 osym.screenRect.add(p);
  1656.             else
  1657.                 osym.screenRect = new Rectangle((int) p.getX(), (int) p.getY(), 0, 0);
  1658.  
  1659.             if (i < numPointsInLayout && (LAYOUT_ABSOLUTE.equals(layoutMode) || LAYOUT_RELATIVE.equals(layoutMode)))
  1660.             {
  1661.                 if (osym.layoutRect != null)
  1662.                     osym.layoutRect.add(p);
  1663.                 else
  1664.                     osym.layoutRect = new Rectangle((int) p.getX(), (int) p.getY(), 0, 0);
  1665.             }
  1666.         }
  1667.  
  1668.         return points;
  1669.     }
  1670.  
  1671.     protected void addGlyph(DrawContext dc, Offset offset, Offset hotspot, String modifierCode, OrderedSymbol osym)
  1672.     {
  1673.         this.addGlyph(dc, offset, hotspot, modifierCode, null, null, osym);
  1674.     }
  1675.  
  1676.     protected void addGlyph(DrawContext dc, Offset offset, Offset hotspot, String modifierCode,
  1677.         AVList retrieverParams, Object layoutMode, OrderedSymbol osym)
  1678.     {
  1679.         IconAtlasElement elem = this.getGlyph(modifierCode, retrieverParams);
  1680.  
  1681.         if (elem.load(dc))
  1682.         {
  1683.             Rectangle rect = this.layoutRect(offset, hotspot, elem.getSize(), layoutMode, osym);
  1684.             elem.setPoint(rect.getLocation());
  1685.             this.currentGlyphs.add(elem);
  1686.         }
  1687.         else
  1688.         {
  1689.             this.unresolvedGlyph = true;
  1690.         }
  1691.     }
  1692.  
  1693.     protected void addLabel(DrawContext dc, Offset offset, Offset hotspot, String modifierText,
  1694.         OrderedSymbol osym)
  1695.     {
  1696.         this.addLabel(dc, offset, hotspot, modifierText, null, null, null, osym);
  1697.     }
  1698.  
  1699.     protected void addLabel(DrawContext dc, Offset offset, Offset hotspot, String modifierText, Font font,
  1700.         Color color, Object layoutMode, OrderedSymbol osym)
  1701.     {
  1702.         if (font == null)
  1703.         {
  1704.             // Use either the currently specified text modifier font or compute a default if no font is specified.
  1705.             font = this.getActiveAttributes().getTextModifierFont();
  1706.             if (font == null)
  1707.                 font = BasicTacticalSymbolAttributes.DEFAULT_TEXT_MODIFIER_FONT;
  1708.         }
  1709.  
  1710.         if (color == null)
  1711.         {
  1712.             // Use either the currently specified text modifier material or the default if no material is specified.
  1713.             Material material = this.getActiveAttributes().getTextModifierMaterial();
  1714.             if (material == null)
  1715.                 material = BasicTacticalSymbolAttributes.DEFAULT_TEXT_MODIFIER_MATERIAL;
  1716.  
  1717.             // Use either the currently specified opacity or the default if no opacity is specified.
  1718.             Double opacity = this.getActiveAttributes().getOpacity();
  1719.             if (opacity == null)
  1720.                 opacity = BasicTacticalSymbolAttributes.DEFAULT_OPACITY;
  1721.  
  1722.             int alpha = (int) (255 * opacity + 0.5);
  1723.             Color diffuse = material.getDiffuse();
  1724.             color = new Color(diffuse.getRed(), diffuse.getGreen(), diffuse.getBlue(), alpha);
  1725.         }
  1726.  
  1727.         // Compute the label's location using the TextRenderer that we expect to use when drawing the label. Don't
  1728.         // keep a reference to the TextRenderer, since it's a property of the draw context and may be disposed when the
  1729.         // GL context changes. See WWJ-426.
  1730.         TextRenderer tr = OGLTextRenderer.getOrCreateTextRenderer(dc.getTextRendererCache(), font);
  1731.         Rectangle bounds = tr.getBounds(modifierText).getBounds();
  1732.         Rectangle rect = this.layoutLabelRect(offset, hotspot, bounds.getSize(), layoutMode, osym);
  1733.         Point point = new Point(rect.getLocation().x, rect.getLocation().y + bounds.y + bounds.height);
  1734.  
  1735.         this.currentLabels.add(new Label(modifierText, point, font, color));
  1736.     }
  1737.  
  1738.     protected void addLine(DrawContext dc, Offset offset, List<? extends Point2D> points, OrderedSymbol osym)
  1739.     {
  1740.         this.addLine(dc, offset, points, null, 0, osym);
  1741.     }
  1742.  
  1743.     @SuppressWarnings({"UnusedParameters"})
  1744.     protected void addLine(DrawContext dc, Offset offset, List<? extends Point2D> points, Object layoutMode,
  1745.         int numPointsInLayout, OrderedSymbol osym)
  1746.     {
  1747.         points = this.layoutPoints(offset, points, layoutMode, numPointsInLayout, osym);
  1748.         this.currentLines.add(new Line(points));
  1749.     }
  1750.  
  1751.     protected IconAtlasElement getGlyph(String modifierCode, AVList retrieverParams)
  1752.     {
  1753.         if (this.getGlyphAtlas() == null || this.getModifierRetriever() == null)
  1754.             return null;
  1755.  
  1756.         IconAtlasElement elem = this.glyphMap.get(modifierCode);
  1757.  
  1758.         if (elem == null)
  1759.         {
  1760.             IconSource source = new IconSource(this.getModifierRetriever(), modifierCode, retrieverParams);
  1761.             elem = new IconAtlasElement(this.getGlyphAtlas(), source);
  1762.             this.glyphMap.put(modifierCode, elem);
  1763.         }
  1764.  
  1765.         elem.lastUsed = System.currentTimeMillis();
  1766.  
  1767.         return elem;
  1768.     }
  1769.  
  1770.     protected void removeDeadModifiers(long now)
  1771.     {
  1772.         if (this.glyphMap.isEmpty())
  1773.             return;
  1774.  
  1775.         List<String> deadKeys = null; // Lazily created below to avoid unnecessary allocation.
  1776.  
  1777.         for (Map.Entry<String, IconAtlasElement> entry : this.glyphMap.entrySet())
  1778.         {
  1779.             if (entry.getValue().lastUsed + this.maxTimeSinceLastUsed < now)
  1780.             {
  1781.                 if (deadKeys == null)
  1782.                     deadKeys = new ArrayList<String>();
  1783.                 deadKeys.add(entry.getKey());
  1784.             }
  1785.         }
  1786.  
  1787.         if (deadKeys == null)
  1788.             return;
  1789.  
  1790.         for (String key : deadKeys)
  1791.         {
  1792.             this.glyphMap.remove(key);
  1793.         }
  1794.     }
  1795.  
  1796.     protected void computeScale(OrderedSymbol osym)
  1797.     {
  1798.         if (this.getActiveAttributes().getScale() != null)
  1799.         {
  1800.             osym.sx = this.getActiveAttributes().getScale();
  1801.             osym.sy = this.getActiveAttributes().getScale();
  1802.         }
  1803.         else
  1804.         {
  1805.             osym.sx = BasicTacticalSymbolAttributes.DEFAULT_SCALE;
  1806.             osym.sy = BasicTacticalSymbolAttributes.DEFAULT_SCALE;
  1807.         }
  1808.     }
  1809.  
  1810.     protected void computeTransform(DrawContext dc, OrderedSymbol osym)
  1811.     {
  1812.         if (this.getOffset() != null && this.iconRect != null)
  1813.         {
  1814.             Point2D p = this.getOffset().computeOffset(this.iconRect.getWidth(), this.iconRect.getHeight(), null,
  1815.                 null);
  1816.             osym.dx = -this.iconRect.getX() - p.getX();
  1817.             osym.dy = -this.iconRect.getY() - p.getY();
  1818.         }
  1819.         else
  1820.         {
  1821.             osym.dx = 0;
  1822.             osym.dy = 0;
  1823.         }
  1824.     }
  1825.  
  1826.     /**
  1827.      * Compute the bounds of symbol after the scale has been applied.
  1828.      *
  1829.      * @param dc        Current draw context.
  1830.      * @param modifiers Current modifiers.
  1831.      * @param osym      The OrderedSymbol to hold the per-frame data.
  1832.      */
  1833.     protected void computeScaledBounds(DrawContext dc, AVList modifiers, OrderedSymbol osym)
  1834.     {
  1835.         Dimension maxDimension = this.computeMinTextLayout(dc, modifiers);
  1836.         osym.iconRectScaled = this.computeScaledRect(this.iconRect, maxDimension, osym.sx, osym.sy);
  1837.         osym.layoutRectScaled = this.computeScaledRect(osym.layoutRect, maxDimension, osym.sx, osym.sy);
  1838.     }
  1839.  
  1840.     /**
  1841.      * Compute the dimension of the minimum layout rectangle for the text modifiers. A minimum dimension is enforced to
  1842.      * prevent the text from overlapping if the symbol is scaled to a very small size.
  1843.      *
  1844.      * @param dc Current draw context.
  1845.      *
  1846.      * @return Minimum dimension for the label layout rectangle.
  1847.      */
  1848.     protected Dimension computeMinTextLayout(DrawContext dc, AVList modifiers)
  1849.     {
  1850.         // Use either the currently specified text modifier font or compute a default if no font is specified.
  1851.         Font font = this.getActiveAttributes().getTextModifierFont();
  1852.         if (font == null)
  1853.             font = BasicTacticalSymbolAttributes.DEFAULT_TEXT_MODIFIER_FONT;
  1854.  
  1855.         TextRenderer tr = OGLTextRenderer.getOrCreateTextRenderer(dc.getTextRendererCache(), font);
  1856.  
  1857.         // Get the bounds of "E" to estimate how tall a typical line of text will be.
  1858.         Rectangle2D bounds = tr.getBounds("E");
  1859.  
  1860.         // Determine how many lines of text to expect so that we can compute a reasonable minimum size.
  1861.         int maxLines = this.getMaxLabelLines(modifiers);
  1862.  
  1863.         int maxDim = (int) (bounds.getHeight() * maxLines * 1.5); // Add 50% for line spacing
  1864.         return new Dimension(maxDim, maxDim);
  1865.     }
  1866.  
  1867.     @SuppressWarnings({"UnusedParameters"})
  1868.     protected int getMaxLabelLines(AVList modifiers)
  1869.     {
  1870.         return DEFAULT_LABEL_LINES;
  1871.     }
  1872.  
  1873.     protected Rectangle computeScaledRect(Rectangle rect, Dimension maxDimension, double scaleX, double scaleY)
  1874.     {
  1875.         double x = rect.getX() * scaleX;
  1876.         double y = rect.getY() * scaleY;
  1877.         double width = rect.getWidth() * scaleX;
  1878.         double height = rect.getHeight() * scaleY;
  1879.  
  1880.         double maxWidth = maxDimension.getWidth();
  1881.         double maxHeight = maxDimension.getHeight();
  1882.  
  1883.         if (width < maxWidth)
  1884.         {
  1885.             x = x + (width - maxWidth) / 2.0;
  1886.             width = maxWidth;
  1887.         }
  1888.         if (height < maxHeight)
  1889.         {
  1890.             y = y + (height - maxHeight) / 2.0;
  1891.             height = maxHeight;
  1892.         }
  1893.  
  1894.         return new Rectangle((int) x, (int) y, (int) Math.ceil(width), (int) Math.ceil(height));
  1895.     }
  1896.  
  1897.     protected Rectangle computeScreenExtent(OrderedSymbol osym)
  1898.     {
  1899.         double width;
  1900.         double height;
  1901.         double x;
  1902.         double y;
  1903.  
  1904.         if (osym.screenRect != null)
  1905.         {
  1906.             x = osym.screenPoint.x + osym.sx * (osym.dx + osym.screenRect.getX());
  1907.             y = osym.screenPoint.y + osym.sy * (osym.dy + osym.screenRect.getY());
  1908.             width = osym.sx * osym.screenRect.getWidth();
  1909.             height = osym.sy * osym.screenRect.getHeight();
  1910.         }
  1911.         else
  1912.         {
  1913.             width = MAX_SYMBOL_DIMENSION;
  1914.             height = MAX_SYMBOL_DIMENSION;
  1915.             x = osym.screenPoint.x - width / 2.0;
  1916.             y = osym.screenPoint.y - height / 2.0;
  1917.         }
  1918.  
  1919.         return new Rectangle((int) x, (int) y, (int) Math.ceil(width), (int) Math.ceil(height));
  1920.     }
  1921.  
  1922.     /**
  1923.      * Indicates the maximum expected size of a rendered tactical symbol. This value is used to estimate the size of a
  1924.      * symbol and perform culling. If the symbol would not be visible (assuming it is the max size), then the icon does
  1925.      * not need to be retrieved.
  1926.      *
  1927.      * @return Maximum size of a symbol, in pixels.
  1928.      */
  1929.     protected int getMaxSymbolDimension()
  1930.     {
  1931.         return MAX_SYMBOL_DIMENSION;
  1932.     }
  1933.  
  1934.     protected boolean intersectsFrustum(DrawContext dc, OrderedSymbol osym)
  1935.     {
  1936.         View view = dc.getView();
  1937.  
  1938.         // Test the symbol's model coordinate point against the near and far clipping planes.
  1939.         if (osym.placePoint != null
  1940.             && (view.getFrustumInModelCoordinates().getNear().distanceTo(osym.placePoint) < 0
  1941.             || view.getFrustumInModelCoordinates().getFar().distanceTo(osym.placePoint) < 0))
  1942.         {
  1943.             return false;
  1944.         }
  1945.  
  1946.         Rectangle screenExtent = this.computeScreenExtent(osym);
  1947.         if (screenExtent != null)
  1948.         {
  1949.             if (dc.isPickingMode())
  1950.                 return dc.getPickFrustums().intersectsAny(screenExtent);
  1951.             else
  1952.                 return view.getViewport().intersects(screenExtent);
  1953.         }
  1954.  
  1955.         return true;
  1956.     }
  1957.  
  1958.     protected void drawOrderedRenderable(DrawContext dc, OrderedSymbol osym)
  1959.     {
  1960.         this.beginDrawing(dc, 0);
  1961.         try
  1962.         {
  1963.             this.doDrawOrderedRenderable(dc, this.pickSupport, osym);
  1964.  
  1965.             if (this.isEnableBatchRendering())
  1966.                 this.drawBatched(dc, osym);
  1967.         }
  1968.         finally
  1969.         {
  1970.             this.endDrawing(dc);
  1971.         }
  1972.     }
  1973.  
  1974.     protected void drawBatched(DrawContext dc, OrderedSymbol firstSymbol)
  1975.     {
  1976.         // Draw as many as we can in a batch to save ogl state switching.
  1977.         Object nextItem = dc.peekOrderedRenderables();
  1978.  
  1979.         if (!dc.isPickingMode())
  1980.         {
  1981.             while (nextItem != null && nextItem instanceof OrderedSymbol)
  1982.             {
  1983.                 OrderedSymbol ts = (OrderedSymbol) nextItem;
  1984.                 if (!ts.isEnableBatchRendering())
  1985.                     break;
  1986.  
  1987.                 dc.pollOrderedRenderables(); // take it off the queue
  1988.                 ts.doDrawOrderedRenderable(dc, this.pickSupport);
  1989.  
  1990.                 nextItem = dc.peekOrderedRenderables();
  1991.             }
  1992.         }
  1993.         else if (this.isEnableBatchPicking())
  1994.         {
  1995.             while (nextItem != null && nextItem instanceof OrderedSymbol)
  1996.             {
  1997.                 OrderedSymbol ts = (OrderedSymbol) nextItem;
  1998.                 if (!ts.isEnableBatchRendering() || !ts.isEnableBatchPicking())
  1999.                     break;
  2000.  
  2001.                 if (ts.getPickLayer() != firstSymbol.getPickLayer()) // batch pick only within a single layer
  2002.                     break;
  2003.  
  2004.                 dc.pollOrderedRenderables(); // take it off the queue
  2005.                 ts.doDrawOrderedRenderable(dc, this.pickSupport);
  2006.  
  2007.                 nextItem = dc.peekOrderedRenderables();
  2008.             }
  2009.         }
  2010.     }
  2011.  
  2012.     protected void beginDrawing(DrawContext dc, int attrMask)
  2013.     {
  2014.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2015.  
  2016.         attrMask |= GL2.GL_DEPTH_BUFFER_BIT // for depth test enable, depth func, depth mask
  2017.             | GL2.GL_COLOR_BUFFER_BIT // for alpha test enable, alpha func, blend enable, blend func
  2018.             | GL2.GL_CURRENT_BIT // for current color
  2019.             | GL2.GL_LINE_BIT; // for line smooth enable and line width
  2020.  
  2021.         Rectangle viewport = dc.getView().getViewport();
  2022.  
  2023.         this.BEogsh.clear(); // Reset the stack handler's internal state.
  2024.         this.BEogsh.pushAttrib(gl, attrMask);
  2025.         this.BEogsh.pushProjectionIdentity(gl);
  2026.         gl.glOrtho(0d, viewport.getWidth(), 0d, viewport.getHeight(), 0d, -1d);
  2027.         this.BEogsh.pushModelviewIdentity(gl);
  2028.  
  2029.         // Enable OpenGL vertex arrays for all symbols by default. All tactical symbol drawing code specifies its data
  2030.         // to OpenGL using vertex arrays.
  2031.         gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
  2032.         gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
  2033.  
  2034.         // Enable the alpha test to suppress any fully transparent image pixels. We do this for both normal rendering
  2035.         // and picking because it eliminates fully transparent texture data from contributing to the pick frame.
  2036.         gl.glEnable(GL2.GL_ALPHA_TEST);
  2037.         gl.glAlphaFunc(GL2.GL_GREATER, 0f);
  2038.  
  2039.         // Apply the depth buffer but don't change it (for screen-space symbols).
  2040.         if (!dc.isDeepPickingEnabled())
  2041.             gl.glEnable(GL.GL_DEPTH_TEST);
  2042.         gl.glDepthFunc(GL.GL_LESS);
  2043.         gl.glDepthMask(false);
  2044.  
  2045.         // Enable OpenGL texturing for all symbols by default. The most common case is to render a series of textured
  2046.         // quads representing symbol icons and modifiers.
  2047.         gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
  2048.         gl.glPolygonOffset(0, (float) DEFAULT_DEPTH_OFFSET);
  2049.  
  2050.         // Enable OpenGL texturing for all symbols by default. The most common case is to render a series of textured
  2051.         // quads representing symbol icons and modifiers.
  2052.         gl.glEnable(GL.GL_TEXTURE_2D);
  2053.  
  2054.         if (dc.isPickingMode())
  2055.         {
  2056.             // Set up to replace the non-transparent texture colors with the single pick color.
  2057.             gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_COMBINE);
  2058.             gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_SRC0_RGB, GL2.GL_PREVIOUS);
  2059.             gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_RGB, GL2.GL_REPLACE);
  2060.  
  2061.             // Give symbol modifier lines a thicker width during picking in order to make them easier to select.
  2062.             gl.glLineWidth(9f);
  2063.         }
  2064.         else
  2065.         {
  2066.             // Enable blending for RGB colors which have been premultiplied by their alpha component. We use this mode
  2067.             // because the icon texture and modifier textures RGB color components have been premultiplied by their color
  2068.             // component.
  2069.             gl.glEnable(GL.GL_BLEND);
  2070.             OGLUtil.applyBlending(gl, true);
  2071.  
  2072.             // Give symbol modifier lines a 3 pixel wide anti-aliased appearance. This GL state does not affect the
  2073.             // symbol icon and symbol modifiers drawn with textures.
  2074.             gl.glEnable(GL.GL_LINE_SMOOTH);
  2075.             gl.glLineWidth(3f);
  2076.         }
  2077.     }
  2078.  
  2079.     protected void endDrawing(DrawContext dc)
  2080.     {
  2081.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2082.  
  2083.         // Restore the default OpenGL vertex array state.
  2084.         gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
  2085.         gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
  2086.  
  2087.         // Restore the default OpenGL polygon offset state.
  2088.         gl.glDisable(GL.GL_POLYGON_OFFSET_FILL);
  2089.         gl.glPolygonOffset(0f, 0f);
  2090.  
  2091.         // Restore the default OpenGL texture state.
  2092.         gl.glDisable(GL.GL_TEXTURE_2D);
  2093.         gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
  2094.  
  2095.         if (dc.isPickingMode())
  2096.         {
  2097.             gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, OGLUtil.DEFAULT_TEX_ENV_MODE);
  2098.             gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_SRC0_RGB, OGLUtil.DEFAULT_SRC0_RGB);
  2099.             gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_RGB, OGLUtil.DEFAULT_COMBINE_RGB);
  2100.         }
  2101.  
  2102.         this.BEogsh.pop(gl);
  2103.     }
  2104.    
  2105.    
  2106.     boolean lineEnabled = true;
  2107.  
  2108.     //these control how the line looks
  2109.     double lineWidth = 1;
  2110.     protected int linePickWidth = 10;
  2111.     Color lineColor = Color.white;
  2112.    
  2113.     /**
  2114.      * Indicates whether a line from the placemark point to the corresponding position on the terrain is drawn.
  2115.      *
  2116.      * @return true if the line is drawn, otherwise false.
  2117.      */
  2118.     public boolean isLineEnabled()
  2119.     {
  2120.         return lineEnabled;
  2121.     }
  2122.  
  2123.     /**
  2124.      * Specifies whether a line from the placemark point to the corresponding position on the terrain is drawn.
  2125.      *
  2126.      * @param lineEnabled true if the line is drawn, otherwise false.
  2127.      */
  2128.     public void setLineEnabled(boolean lineEnabled)
  2129.     {
  2130.         this.lineEnabled = lineEnabled;
  2131.     }
  2132.    
  2133.     /**
  2134.      * Determines whether the placemark's optional line should be drawn and whether it intersects the view frustum.
  2135.      *
  2136.      * @param dc the current draw context.
  2137.      *
  2138.      * @return true if the line should be drawn and it intersects the view frustum, otherwise false.
  2139.      */
  2140.     protected boolean isDrawLine(DrawContext dc, OrderedSymbol opm)
  2141.     {
  2142.         if (!this.isLineEnabled() || dc.is2DGlobe() || this.getAltitudeMode() == WorldWind.CLAMP_TO_GROUND
  2143.             || opm.terrainPoint == null)
  2144.             return false;
  2145.  
  2146.         if (dc.isPickingMode())
  2147.             return dc.getPickFrustums().intersectsAny(opm.placePoint, opm.terrainPoint);
  2148.         else
  2149.             return dc.getView().getFrustumInModelCoordinates().intersectsSegment(opm.placePoint, opm.terrainPoint);
  2150.     }
  2151.  
  2152.  
  2153.    
  2154.  
  2155.     /**
  2156.      * Draws the placemark's line.
  2157.      *
  2158.      * @param dc             the current draw context.
  2159.      * @param pickCandidates the pick support object to use when adding this as a pick candidate.
  2160.      */
  2161.     protected void drawLine(DrawContext dc, PickSupport pickCandidates, OrderedSymbol opm)
  2162.     {
  2163.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2164.  
  2165.         if ((!dc.isDeepPickingEnabled()))
  2166.             gl.glEnable(GL.GL_DEPTH_TEST);
  2167.         gl.glDepthFunc(GL.GL_LEQUAL);
  2168.         gl.glDepthMask(true);
  2169.  
  2170.         try
  2171.         {
  2172.             dc.getView().pushReferenceCenter(dc, opm.placePoint); // draw relative to the place point
  2173.  
  2174.             this.setLineWidth(dc);
  2175.             this.setLineColor(dc, pickCandidates);
  2176.  
  2177.             gl.glBegin(GL2.GL_LINE_STRIP);
  2178.             gl.glVertex3d(Vec4.ZERO.x, Vec4.ZERO.y, Vec4.ZERO.z);
  2179.             gl.glVertex3d(opm.terrainPoint.x - opm.placePoint.x, opm.terrainPoint.y - opm.placePoint.y,
  2180.                 opm.terrainPoint.z - opm.placePoint.z);
  2181.             gl.glEnd();
  2182.         }
  2183.         finally
  2184.         {
  2185.             dc.getView().popReferenceCenter(dc);
  2186.         }
  2187.     }
  2188.  
  2189.    
  2190.     /**
  2191.      * Sets the width of the placemark's line during rendering.
  2192.      *
  2193.      * @param dc the current draw context.
  2194.      */
  2195.     protected void setLineWidth(DrawContext dc)
  2196.     {
  2197.         Double lineWidth = this.lineWidth;
  2198.         if (lineWidth != null)
  2199.         {
  2200.             GL gl = dc.getGL();
  2201.  
  2202.             if (dc.isPickingMode())
  2203.             {
  2204.                 gl.glLineWidth(lineWidth.floatValue() + linePickWidth);
  2205.             }
  2206.             else
  2207.                 gl.glLineWidth(lineWidth.floatValue());
  2208.  
  2209.             if (!dc.isPickingMode())
  2210.             {
  2211.                 gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_FASTEST);
  2212.                 gl.glEnable(GL.GL_LINE_SMOOTH);
  2213.             }
  2214.         }
  2215.     }
  2216.  
  2217.  
  2218.     /**
  2219.      * Sets the color of the placemark's line during rendering.
  2220.      *
  2221.      * @param dc             the current draw context.
  2222.      * @param pickCandidates the pick support object to use when adding this as a pick candidate.
  2223.      */
  2224.     protected void setLineColor(DrawContext dc, PickSupport pickCandidates)
  2225.     {
  2226.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2227.  
  2228.         if (!dc.isPickingMode())
  2229.         {
  2230.             Color color = this.lineColor;
  2231.             gl.glColor4ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue(),
  2232.                 (byte) color.getAlpha());
  2233.         }
  2234.         else
  2235.         {
  2236.             Color pickColor = dc.getUniquePickColor();
  2237.             Object delegateOwner = this.getDelegateOwner();
  2238.             pickCandidates.addPickableObject(pickColor.getRGB(), delegateOwner != null ? delegateOwner : this,
  2239.                 this.getPosition());
  2240.             gl.glColor3ub((byte) pickColor.getRed(), (byte) pickColor.getGreen(), (byte) pickColor.getBlue());
  2241.         }
  2242.     }
  2243.  
  2244.  
  2245.     protected void doDrawOrderedRenderable(DrawContext dc, PickSupport pickCandidates, OrderedSymbol osym)
  2246.     {
  2247.        
  2248.         boolean drawLine = this.isDrawLine(dc, osym);
  2249.         if (drawLine)
  2250.             this.drawLine(dc, pickCandidates, osym);
  2251.        
  2252.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2253.  
  2254.         if (dc.isPickingMode())
  2255.         {
  2256.             Color pickColor = dc.getUniquePickColor();
  2257.             pickCandidates.addPickableObject(this.createPickedObject(pickColor.getRGB()));
  2258.             gl.glColor3ub((byte) pickColor.getRed(), (byte) pickColor.getGreen(), (byte) pickColor.getBlue());
  2259.         }
  2260.         else
  2261.         {
  2262.             // Set the current color to white with the symbol's current opacity. This applies the symbol's opacity to
  2263.             // its icon texture and graphic modifier textures by multiplying texture fragment colors by the opacity. We
  2264.             // pre-multiply the white RGB color components by the alpha since the texture's RGB color components have
  2265.             // also been pre-multiplied by their color component.
  2266.             float a = this.getActiveAttributes().getOpacity() != null
  2267.                 ? this.getActiveAttributes().getOpacity().floatValue()
  2268.                 : (float) BasicTacticalSymbolAttributes.DEFAULT_OPACITY;
  2269.             gl.glColor4f(a, a, a, a);
  2270.         }
  2271.  
  2272.         Double depthOffsetUnits = this.getDepthOffset();
  2273.         try
  2274.         {
  2275.             // Apply any custom depth offset specified by the caller. This overrides the default depth offset specified
  2276.             // in beginRendering, and is therefore restored in the finally block below.
  2277.             if (depthOffsetUnits != null)
  2278.                 gl.glPolygonOffset(0f, depthOffsetUnits.floatValue());
  2279.  
  2280.             this.prepareToDraw(dc, osym);
  2281.             this.draw(dc, osym);
  2282.         }
  2283.         finally
  2284.         {
  2285.             // If the caller specified a custom depth offset, we restore the default depth offset to the value specified
  2286.             // in beginRendering.
  2287.             if (depthOffsetUnits != null)
  2288.                 gl.glPolygonOffset(0f, (float) DEFAULT_DEPTH_OFFSET);
  2289.         }
  2290.     }
  2291.  
  2292.     protected void prepareToDraw(DrawContext dc, OrderedSymbol osym)
  2293.     {
  2294.         // Apply the symbol's offset in screen coordinates. We translate the X and Y coordinates so that the
  2295.         // symbol's hot spot (identified by its offset) is aligned with its screen point. We translate the Z
  2296.         // coordinate so that the symbol's depth values are appropriately computed by OpenGL according to its
  2297.         // distance from the eye. The orthographic projection matrix configured in beginRendering correctly maps
  2298.         // the screen point's Z coordinate to its corresponding depth value.
  2299.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2300.         gl.glLoadIdentity(); // Assumes that the current matrix mode is GL_MODELVIEW.
  2301.         gl.glTranslated(osym.screenPoint.x, osym.screenPoint.y, osym.screenPoint.z);
  2302.     }
  2303.  
  2304.     protected void draw(DrawContext dc, OrderedSymbol osym)
  2305.     {
  2306.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2307.         try
  2308.         {
  2309.             gl.glPushMatrix();
  2310.             gl.glScaled(osym.sx, osym.sy, 1d);
  2311.             gl.glTranslated(osym.dx, osym.dy, 0d);
  2312.  
  2313.             if (this.mustDrawIcon(dc))
  2314.                 this.drawIcon(dc);
  2315.  
  2316.             if (this.mustDrawGraphicModifiers(dc))
  2317.                 this.drawGraphicModifiers(dc, osym);
  2318.         }
  2319.         finally
  2320.         {
  2321.             gl.glPopMatrix();
  2322.         }
  2323.  
  2324.         if (this.mustDrawTextModifiers(dc) && !dc.isPickingMode())
  2325.         {
  2326.             try
  2327.             {
  2328.                 // Do not apply scale to text modifiers. The size of the text is determined by the font. Do apply scale
  2329.                 // to dx and dy to put the text in the right place.
  2330.                 gl.glPushMatrix();
  2331.                 gl.glTranslated(osym.dx * osym.sx, osym.dy * osym.sy, 0d);
  2332.  
  2333.                 this.drawTextModifiers(dc);
  2334.             }
  2335.             finally
  2336.             {
  2337.                 gl.glPopMatrix();
  2338.             }
  2339.         }
  2340.     }
  2341.  
  2342.     @SuppressWarnings({"UnusedParameters"})
  2343.     protected boolean mustDrawIcon(DrawContext dc)
  2344.     {
  2345.         return true;
  2346.     }
  2347.  
  2348.     @SuppressWarnings({"UnusedParameters"})
  2349.     protected boolean mustDrawGraphicModifiers(DrawContext dc)
  2350.     {
  2351.         return this.isShowGraphicModifiers();
  2352.     }
  2353.  
  2354.     @SuppressWarnings({"UnusedParameters"})
  2355.     protected boolean mustDrawTextModifiers(DrawContext dc)
  2356.     {
  2357.         return this.isShowTextModifiers();
  2358.     }
  2359.  
  2360.     protected void drawIcon(DrawContext dc)
  2361.     {
  2362.         if (this.activeIconTexture == null || this.iconRect == null)
  2363.             return;
  2364.  
  2365.         if (!this.activeIconTexture.bind(dc))
  2366.             return;
  2367.  
  2368.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2369.         try
  2370.         {
  2371.             gl.glPushMatrix();
  2372.             gl.glScaled(this.activeIconTexture.getWidth(dc), this.activeIconTexture.getHeight(dc), 1d);
  2373.             dc.drawUnitQuad(this.activeIconTexture.getTexCoords());
  2374.         }
  2375.         finally
  2376.         {
  2377.             gl.glPopMatrix();
  2378.         }
  2379.     }
  2380.  
  2381.     protected void drawGraphicModifiers(DrawContext dc, OrderedSymbol osym)
  2382.     {
  2383.         this.drawGlyphs(dc);
  2384.         this.drawLines(dc, osym);
  2385.     }
  2386.  
  2387.     protected void drawTextModifiers(DrawContext dc)
  2388.     {
  2389.         this.drawLabels(dc);
  2390.     }
  2391.  
  2392.     protected void drawGlyphs(DrawContext dc)
  2393.     {
  2394.         if (this.glyphAtlas == null || this.currentGlyphs.isEmpty())
  2395.             return;
  2396.  
  2397.         if (!this.glyphAtlas.bind(dc))
  2398.             return;
  2399.  
  2400.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2401.  
  2402.         for (IconAtlasElement atlasElem : this.currentGlyphs)
  2403.         {
  2404.             Point point = atlasElem.getPoint();
  2405.             Dimension size = atlasElem.getSize();
  2406.             TextureCoords texCoords = atlasElem.getTexCoords();
  2407.  
  2408.             if (point == null || size == null || texCoords == null)
  2409.                 continue;
  2410.  
  2411.             try
  2412.             {
  2413.                 gl.glPushMatrix();
  2414.                 gl.glTranslated(point.getX(), point.getY(), 0d);
  2415.                 gl.glScaled(size.getWidth(), size.getHeight(), 1d);
  2416.                 dc.drawUnitQuad(texCoords);
  2417.             }
  2418.             finally
  2419.             {
  2420.                 gl.glPopMatrix();
  2421.             }
  2422.         }
  2423.     }
  2424.  
  2425.     protected void drawLabels(DrawContext dc)
  2426.     {
  2427.         if (this.currentLabels.isEmpty())
  2428.             return;
  2429.  
  2430.         GL gl = dc.getGL();
  2431.         TextRenderer tr = null;
  2432.         TextRendererCache trCache = dc.getTextRendererCache();
  2433.         try
  2434.         {
  2435.             // Don't depth buffer labels. Depth buffering would cause the labels to intersect terrain, which is
  2436.             // usually a bigger usability problem for text than a label showing through a hill.
  2437.             gl.glDisable(GL.GL_DEPTH_TEST);
  2438.  
  2439.             for (Label modifier : this.currentLabels)
  2440.             {
  2441.                 TextRenderer modifierRenderer = OGLTextRenderer.getOrCreateTextRenderer(trCache, modifier.getFont());
  2442.                 if (tr == null || tr != modifierRenderer)
  2443.                 {
  2444.                     if (tr != null)
  2445.                         tr.end3DRendering();
  2446.                     tr = modifierRenderer;
  2447.                     tr.begin3DRendering();
  2448.                 }
  2449.  
  2450.                 Point p = modifier.getPoint();
  2451.                 tr.setColor(modifier.getColor());
  2452.                 tr.draw(modifier.getText(), p.x, p.y);
  2453.             }
  2454.         }
  2455.         finally
  2456.         {
  2457.             if (tr != null)
  2458.                 tr.end3DRendering();
  2459.  
  2460.             gl.glEnable(GL.GL_DEPTH_TEST);
  2461.         }
  2462.     }
  2463.  
  2464.     protected void drawLines(DrawContext dc, OrderedSymbol osym)
  2465.     {
  2466.         // Use either the currently specified opacity or the default if no opacity is specified.
  2467.         Double opacity = this.getActiveAttributes().getOpacity() != null ? this.getActiveAttributes().getOpacity()
  2468.             : BasicTacticalSymbolAttributes.DEFAULT_OPACITY;
  2469.  
  2470.         GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
  2471.  
  2472.         try
  2473.         {
  2474.             gl.glDisable(GL.GL_TEXTURE_2D);
  2475.  
  2476.             // Apply an offset to move the line away from terrain.
  2477.             double depth = osym.screenPoint.z - (8d * 0.00048875809d);
  2478.             depth = depth < 0d ? 0d : (depth > 1d ? 1d : depth);
  2479.             gl.glDepthRange(depth, depth);
  2480.  
  2481.             // Set the current color to black with the current opacity value as the alpha component. Blending is set to
  2482.             // pre-multiplied alpha mode, but we can just specify 0 for the RGB components because multiplying them by
  2483.             // the alpha component has no effect.
  2484.             if (!dc.isPickingMode())
  2485.                 gl.glColor4f(0f, 0f, 0f, opacity.floatValue());
  2486.  
  2487.             for (Line lm : this.currentLines)
  2488.             {
  2489.                 try
  2490.                 {
  2491.                     gl.glBegin(GL2.GL_LINE_STRIP);
  2492.  
  2493.                     for (Point2D p : lm.getPoints())
  2494.                     {
  2495.                         gl.glVertex2d(p.getX(), p.getY());
  2496.                     }
  2497.                 }
  2498.                 finally
  2499.                 {
  2500.                     gl.glEnd();
  2501.                 }
  2502.             }
  2503.         }
  2504.         finally
  2505.         {
  2506.             // Restore the depth range and texture 2D enable state to the values specified in beginDrawing.
  2507.             gl.glEnable(GL.GL_TEXTURE_2D);
  2508.             gl.glDepthRange(0.0, 1.0);
  2509.  
  2510.             // Restore the current color to that specified in doDrawOrderedRenderable.
  2511.             if (!dc.isPickingMode())
  2512.                 gl.glColor4f(opacity.floatValue(), opacity.floatValue(), opacity.floatValue(),
  2513.                     opacity.floatValue());
  2514.         }
  2515.     }
  2516.  
  2517.     protected PickedObject createPickedObject(int colorCode)
  2518.     {
  2519.         Object owner = this.getDelegateOwner();
  2520.         return new PickedObject(colorCode, owner != null ? owner : this);
  2521.     }
  2522. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement