Esempio n. 1
0
  public void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;

    String message = "Hello, World!";

    Font f = new Font("Serif", Font.BOLD, 36);
    g2.setFont(f);

    FontRenderContext context = g2.getFontRenderContext();
    Rectangle2D bounds = f.getStringBounds(message, context);

    double x = (getWidth() - bounds.getWidth()) / 2;
    double y = (getHeight() - bounds.getHeight()) / 2;

    double ascent = -bounds.getY();
    double baseY = y + ascent;

    g2.drawString(message, (int) x, (int) baseY);

    g2.setPaint(Color.LIGHT_GRAY);

    g2.draw(new Line2D.Double(x, baseY, x + bounds.getWidth(), baseY));
    Rectangle2D rect = new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight());
    g2.draw(rect);
  }
Esempio n. 2
0
  public Rectangle2D getBounds2D() {
    StringTokenizer tokens = new StringTokenizer(getRenderString(), "\n");

    int noLines = tokens.countTokens();

    double height = (theFont.getSize2D() * noLines) + 5;
    double width = 0;

    while (tokens.hasMoreTokens()) {
      double l = theFont.getSize2D() * tokens.nextToken().length() * (5.0 / 8.0);
      if (l > width) width = l;
    }

    double parX;
    double parY;
    if (parent instanceof State) {
      parX = ((State) parent).getX();
      parY = ((State) parent).getY();
    } else if (parent instanceof Transition) {
      parX = ((Transition) parent).getMiddle().getX(); // dummy
      parY = ((Transition) parent).getMiddle().getY(); // dummy
    } else {
      parX = 0;
      parY = 0;
    }

    double mx = parX + offsetX;
    double my = parY + offsetY - 10;

    double tx = parX + width + offsetX;
    double ty = parY + height + offsetY - 10;

    return new Rectangle2D.Double(mx, my, tx - mx, ty - my);
  }
Esempio n. 3
0
  public void workOutMinsAndMaxs() {
    StringTokenizer tokens = new StringTokenizer(getRenderString(), "\n");

    int noLines = tokens.countTokens();

    double height = (theFont.getSize2D() * noLines) + 5;
    double width = 0;

    while (tokens.hasMoreTokens()) {
      double l = theFont.getSize2D() * tokens.nextToken().length() * (5.0 / 8.0);
      if (l > width) width = l;
    }

    double parX;
    double parY;
    if (parent instanceof State) {
      parX = ((State) parent).getX();
      parY = ((State) parent).getY();
    } else if (parent instanceof Transition) {
      parX = ((Transition) parent).getMiddle().getX(); // dummy
      parY = ((Transition) parent).getMiddle().getY(); // dummy
    } else {
      parX = 0;
      parY = 0;
    }

    minX = parX + offsetX - 5;
    minY = parY + offsetY - 25;

    maxX = parX + width + offsetX + 5;
    maxY = parY + height + offsetY - 5;
  }
Esempio n. 4
0
  /*
   * (non-Javadoc)
   *
   * @see org.math.plot.render.AbstractDrawer#drawStringRatio(java.lang.String,
   *      double[], double, double, double)
   */
  public void drawTextBase(String label, double... rC) {
    int[] sC = projection.screenProjectionBase(rC);

    // Corner offset adjustment : Text Offset is used Here
    FontRenderContext frc = comp2D.getFontRenderContext();
    Font font1 = comp2D.getFont();
    int x = sC[0];
    int y = sC[1];
    double w = font1.getStringBounds(label, frc).getWidth();
    double h = font1.getSize2D();
    x -= (int) (w * text_Eastoffset);
    y += (int) (h * text_Northoffset);

    int wc = (int) (w * FastMath.cos(text_angle) + h * FastMath.sin(text_angle));
    int hc = (int) (h * FastMath.cos(text_angle) + w * FastMath.sin(text_angle));
    if (!comp2D.hitClip(x, y, wc, hc)) {
      return;
    }

    if (text_angle != 0) {
      comp2D.rotate(text_angle, x + w / 2, y - h / 2);
    }

    String[] lines = label.split("\n");
    for (int i = 0; i < lines.length; i++) {
      comp2D.drawString(lines[i], x, y);
      y += h;
    }
    // comp2D.drawString(label, x, y);

    if (text_angle != 0) {
      comp2D.rotate(-text_angle, x + w / 2, y - h / 2);
    }
  }
Esempio n. 5
0
 /**
  * Create the name label if needed.
  *
  * @return The component that holds the name label.
  */
 protected JComponent getLabelComponent() {
   if (nameLabel == null) {
     nameLabel = new JLabel();
     Font font = nameLabel.getFont();
     nameLabel.setFont(font.deriveFont(Font.ITALIC | Font.BOLD));
     labelComponent = GuiUtils.hflow(Misc.newList(new JLabel("Layout Model: "), nameLabel));
   }
   return labelComponent;
 }
Esempio n. 6
0
 /**
  * Gets the page count of this section.
  *
  * @param g2 the graphics context
  * @param pf the page format
  * @return the number of pages needed
  */
 public int getPageCount(Graphics2D g2, PageFormat pf) {
   if (message.equals("")) return 0;
   FontRenderContext context = g2.getFontRenderContext();
   Font f = new Font("Serif", Font.PLAIN, 72);
   Rectangle2D bounds = f.getStringBounds(message, context);
   scale = pf.getImageableHeight() / bounds.getHeight();
   double width = scale * bounds.getWidth();
   int pages = (int) Math.ceil(width / pf.getImageableWidth());
   return pages;
 }
 protected void paintComponent(Graphics g) {
   super.paintComponent(g);
   Graphics2D g2 = (Graphics2D) g;
   g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
   int w = getWidth();
   int h = getHeight();
   // Draw ordinate.
   g2.draw(new Line2D.Double(Points, Points, Points, h - Points));
   // Draw abcissa.
   g2.draw(new Line2D.Double(Points, h - Points, w - Points, h - Points));
   // Draw labels.
   Font font = g2.getFont();
   FontRenderContext frc = g2.getFontRenderContext();
   LineMetrics lm = font.getLineMetrics("0", frc);
   float sh = lm.getAscent() + lm.getDescent();
   // Ordinate label.
   String s = "Strecke";
   float sy = Points + ((h - 2 * Points) - s.length() * sh) / 2 + lm.getAscent();
   for (int i = 0; i < s.length(); i++) {
     String letter = String.valueOf(s.charAt(i));
     float sw = (float) font.getStringBounds(letter, frc).getWidth();
     float sx = (Points - sw) / 2;
     g2.drawString(letter, sx, sy);
     sy += sh;
   }
   // Abcissa label.
   s = "Datum";
   sy = h - Points + (Points - sh) / 2 + lm.getAscent();
   float sw = (float) font.getStringBounds(s, frc).getWidth();
   float sx = (w - sw) / 2;
   g2.drawString(s, sx, sy);
   // Draw lines.
   double xInc = (double) (w - 2 * Points) / (data.length - 1);
   double scale = (double) (h - 2 * Points) / getMax();
   g2.setPaint(Color.green.darker());
   for (int i = 0; i < data.length - 1; i++) {
     double x1 = Points + i * xInc;
     double y1 = h - Points - scale * data[i];
     double x2 = Points + (i + 1) * xInc;
     double y2 = h - Points - scale * data[i + 1];
     g2.draw(new Line2D.Double(x1, y1, x2, y2));
   }
   // Mark data points.
   g2.setPaint(Color.red);
   for (int i = 0; i < data.length; i++) {
     double x = Points + i * xInc;
     double y = h - Points - scale * data[i];
     g2.fill(new Ellipse2D.Double(x - 2, y - 2, 4, 4));
   }
 }
Esempio n. 8
0
  /**
   * Loads a font based on file path
   *
   * @param String filePath
   * @param float fontSize
   * @param FontStyle fontStyle
   * @throws FontFormatException
   * @throws IOException
   */
  public INFont(String filePath, float fontSize, FontStyle fontStyle)
      throws FontFormatException, IOException {
    File fontFile = new File(filePath);

    this.loadedFont = Font.createFont(Font.TRUETYPE_FONT, fontFile);
    this.loadedFont = this.loadedFont.deriveFont(fontSize);

    this.style = fontStyle;
    this.size = fontSize;

    switch (fontStyle) {
      case BOLD:
        this.loadedFont = this.loadedFont.deriveFont(Font.BOLD);
        break;
      case ITALIC:
        this.loadedFont = this.loadedFont.deriveFont(Font.ITALIC);
        break;
      case PLAIN:
        this.loadedFont = this.loadedFont.deriveFont(Font.PLAIN);
        break;
      case BOLDANDITALIC:
        this.loadedFont = this.loadedFont.deriveFont(Font.BOLD + Font.ITALIC);
        break;
    }
  }
Esempio n. 9
0
  private Shape generateShapeFromText() {
    Font font = new Font(fontFamily, Font.PLAIN, fontSize);
    BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2 = img.createGraphics();

    try {
      GlyphVector vect = font.createGlyphVector(g2.getFontRenderContext(), str);
      float dispX = x;
      float dispY = (float) (y - vect.getVisualBounds().getY());
      Shape shape = vect.getOutline(dispX, dispY);

      return shape;
    } finally {
      g2.dispose();
    }
  }
Esempio n. 10
0
 void drawRoiLabel(Graphics g, int index, Roi roi) {
   Rectangle r = roi.getBounds();
   int x = screenX(r.x);
   int y = screenY(r.y);
   double mag = getMagnification();
   int width = (int) (r.width * mag);
   int height = (int) (r.height * mag);
   int size = width > 40 && height > 40 ? 12 : 9;
   if (font != null) {
     g.setFont(font);
     size = font.getSize();
   } else if (size == 12) g.setFont(largeFont);
   else g.setFont(smallFont);
   boolean drawingList = index >= LIST_OFFSET;
   if (drawingList) index -= LIST_OFFSET;
   String label = "" + (index + 1);
   if (drawNames && roi.getName() != null) label = roi.getName();
   FontMetrics metrics = g.getFontMetrics();
   int w = metrics.stringWidth(label);
   x = x + width / 2 - w / 2;
   y = y + height / 2 + Math.max(size / 2, 6);
   int h = metrics.getAscent() + metrics.getDescent();
   if (bgColor != null) {
     g.setColor(bgColor);
     g.fillRoundRect(x - 1, y - h + 2, w + 1, h - 3, 5, 5);
   }
   if (!drawingList && labelRects != null && index < labelRects.length)
     labelRects[index] = new Rectangle(x - 1, y - h + 2, w + 1, h);
   g.setColor(labelColor);
   g.drawString(label, x, y - 2);
   g.setColor(defaultColor);
 }
  /**
   * Set current font. Default to Plain Courier 11 if null.
   *
   * @param font new font.
   */
  public void setFont(Font font) {

    if (font != null) {
      m_localGraphicsState.setFont(font);
      if (font.getName().equals(m_psGraphicsState.getFont().getName())
          && (m_psGraphicsState.getFont().getStyle() == font.getStyle())
          && (m_psGraphicsState.getFont().getSize() == yScale(font.getSize()))) return;
      m_psGraphicsState.setFont(
          new Font(font.getName(), font.getStyle(), yScale(getFont().getSize())));
    } else {
      m_localGraphicsState.setFont(new Font("Courier", Font.PLAIN, 11));
      m_psGraphicsState.setFont(getFont());
    }

    m_printstream.println("/(" + replacePSFont(getFont().getPSName()) + ")" + " findfont");
    m_printstream.println(yScale(getFont().getSize()) + " scalefont setfont");
  }
Esempio n. 12
0
  public void paintComponent(Graphics g) {
    // необходиом чтобы текст коректно отрисовывался в окне
    super.paintComponent(g);
    // рисуем текст в окне
    Graphics2D g2 = (Graphics2D) g;
    AffineTransform t = g2.getTransform();
    g.drawString("It is text", 5, 5);
    // создание шрифта
    Font f = new Font("SanasSerif", Font.ITALIC, 20);
    g2.setFont(f);
    g2.drawString("It is new text", 5, 33);
    String[] fontNames =
        GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
    for (int i = 5; i < 20; i++) {
      g2.rotate(-0.05);
      g2.setColor(
          new Color(
              (int) (Math.random() * 255),
              (int) (Math.random() * 255),
              (int) (Math.random() * 255)));
      Font f1 = new Font(fontNames[i], Font.BOLD, 20);
      g2.setFont(f1);
      g2.drawString(fontNames[i], 5, 20 * i);
    }
    // текст в центре

    g2.setTransform(t); // возращение к кординатам, которые запонилив начале
    Font f2 = new Font("SanasSerif", Font.ITALIC, 20);
    g2.setFont(f2);
    String s = "It is center!";
    FontRenderContext context = g2.getFontRenderContext();
    Rectangle2D r = f2.getStringBounds(s, context);
    double x1 = (getWidth() - r.getWidth()) / 2;
    double y1 = (getHeight() - r.getHeight()) / 2;
    double ascent = -r.getY(); // узнаем высоту текста
    double y2 = y1 + ascent;
    Rectangle2D rect = new Rectangle2D.Double(x1, y1, r.getWidth(), r.getHeight());
    g2.setColor(Color.YELLOW);
    g2.fill(rect);
    g2.setColor(Color.red);
    g2.drawString(s, (int) x1, (int) y2);
    g2.setColor(Color.blue);
    g2.draw(new Line2D.Double(x1, y2, x1 + r.getWidth(), y2));

    g2.draw(rect);
  }
Esempio n. 13
0
  /**
   * A rendering method to draw this label to the given Graphics2D object, with the additional
   * option of allowing the "long" lines to be drawn. Due to the fact that the relative drawing
   * point of a state is its x and y co-ordinates and for a Transition there is a workOutMiddle()
   * method, the relative x and y values must be supplied to this method. Usually this method will
   * be called from inside a Transition render method or a State render method.
   *
   * @param g2 the Graphics2D component upon which to draw this label.
   * @param x the x position upon which to make relative co-ordinates exact.
   * @param y the y position upon which to make relative co-ordinates exact.
   * @param longLines flag to determine whether the long version of this label should be drawn.
   */
  public void render(Graphics2D g2, double x, double y, boolean longLines) {
    intersects(new Rectangle2D.Double(0, 0, 1, 1));
    StringTokenizer tokens = new StringTokenizer(getRenderString(), "\n");
    if (selected) {
      g2.setColor(Color.green);
    } else {
      g2.setColor(theColour);
    }
    g2.setFont(theFont);
    int i = 0;
    boolean doneLong = false;
    while (tokens.hasMoreTokens()) {
      if (doneLong)
        g2.drawString(
            tokens.nextToken(),
            (float) (x + offsetX),
            (float) (y + offsetY + ((i * (theFont.getSize() + 2)))));
      else {
        if (!longLines)
          g2.drawString(
              tokens.nextToken().trim(),
              (float) (x + offsetX),
              (float) (y + offsetY + ((i * (theFont.getSize()))) + 2));
        else
          g2.drawString(
              getName() + ": " + tokens.nextToken().trim(),
              (float) (x + offsetX),
              (float) (y + offsetY + ((i * (theFont.getSize()))) + 2));
      }
      i++;
      doneLong = true;
    }

    /*if(intersects != null)
    {
        g2.setColor(Color.magenta);
        for(int j = 0; j < intersects.size(); j++)
        {
            Rectangle2D rect = (Rectangle2D)intersects.get(j);
            g2.draw(rect);
        }
    }*/

  }
Esempio n. 14
0
  public void drawShadowedText(String label, float alpha, double... pC) {
    int[] sC = projection.screenProjection(pC);

    // Corner offset adjustment : Text Offset is used Here
    FontRenderContext frc = comp2D.getFontRenderContext();
    Font font1 = comp2D.getFont();
    int x = sC[0];
    int y = sC[1];
    double w = font1.getStringBounds(label, frc).getWidth();
    double h = font1.getSize2D();
    x -= (int) (w * text_Eastoffset);
    y += (int) (h * text_Northoffset);

    int wc = (int) (w * FastMath.cos(text_angle) + h * FastMath.sin(text_angle));
    int hc = (int) (h * FastMath.cos(text_angle) + w * FastMath.sin(text_angle));
    if (!comp2D.hitClip(x, y, wc, hc)) {
      return;
    }

    if (text_angle != 0) {
      comp2D.rotate(text_angle, x + w / 2, y - h / 2);
    }

    Composite cs = comp2D.getComposite();
    Color c = comp2D.getColor();

    String[] lines = label.split("\n");
    for (int i = 0; i < lines.length; i++) {

      comp2D.setColor(Color.white);
      comp2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
      comp2D.fillRect(x, y - (int) h, (int) w, (int) h);
      comp2D.setComposite(cs);
      comp2D.setColor(c);

      comp2D.drawString(lines[i], x, y);
      y += h;
    }

    if (text_angle != 0) {
      comp2D.rotate(-text_angle, x + w / 2, y - h / 2);
    }
  }
Esempio n. 15
0
  /**
   * This method calculates whether a given "Hot area" rectangle intersects with this label and
   * returns the result. The aim of this method is that it can be used to detect whether the
   * position of the mouse is colliding with the label.
   *
   * @param rect A "Hot area" which we are looking for the collision to lie in.
   * @return the result of the collision.
   */
  public boolean intersects(Rectangle2D rect) {
    intersects = new ArrayList();
    if (!getString().equals("")) {
      double parX;
      double parY;
      if (parent instanceof State) {
        parX = ((State) parent).getX();
        parY = ((State) parent).getY();
      } else if (parent instanceof Transition) {
        parX = ((Transition) parent).getMiddle().getX(); // dummy
        parY = ((Transition) parent).getMiddle().getY(); // dummy
      } else {
        parX = 0;
        parY = 0;
      }

      double x = parX + offsetX;
      double y = parY + offsetY - 5;
      StringTokenizer tokens;
      String pre = "";
      if (lineLabels) {
        pre = getName() + ": ";
      }

      tokens = new StringTokenizer(pre + getRenderString(), "\n");

      int i = 0;
      boolean collides = false;
      while (tokens.hasMoreTokens() && !collides) {
        String str = tokens.nextToken().trim();
        // System.out.println("str = \""+str+"\"");
        double height = theFont.getSize2D();
        double width = theFont.getSize2D() * str.length() * (5.0 / 8.0);
        intersects.add(new Rectangle2D.Double(x, y + (i * (height + 1.75) - 2), width, height));
        collides =
            (new Rectangle2D.Double(x, y + (i * (height + 1.75) - 2), width, height))
                .intersects(rect);
        i++;
      }
      return collides;
    } else return false;
  }
Esempio n. 16
0
  public void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;

    String message = "Hello, World!";

    Font f = new Font("Serif", Font.BOLD, 36);
    g2.setFont(f);

    // measure the size of the message

    FontRenderContext context = g2.getFontRenderContext();
    Rectangle2D bounds = f.getStringBounds(message, context);

    // set (x,y) = top left corner of text

    double x = (getWidth() - bounds.getWidth()) / 2;
    double y = (getHeight() - bounds.getHeight()) / 2;

    // add ascent to y to reach the baseline

    double ascent = -bounds.getY();
    double baseY = y + ascent;

    // draw the message

    g2.drawString(message, (int) x, (int) baseY);

    g2.setPaint(Color.LIGHT_GRAY);

    // draw the baseline

    g2.draw(new Line2D.Double(x, baseY, x + bounds.getWidth(), baseY));

    // draw the enclosing rectangle

    Rectangle2D rect = new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight());
    g2.draw(rect);
  }
Esempio n. 17
0
  /**
   * Same as parent, but override for native version of the font.
   *
   * <p>Also gets called by textFont, so the metrics will get recorded properly.
   */
  public void textSize(float size) {
    if (textFont == null) {
      defaultFontOrDeath("textAscent", size);
    }

    // if a native version available, derive this font
    //    if (textFontNative != null) {
    //      textFontNative = textFontNative.deriveFont(size);
    //      g2.setFont(textFontNative);
    //      textFontNativeMetrics = g2.getFontMetrics(textFontNative);
    //    }
    Font font = textFont.getFont();
    if (font != null && (textFont.isStream() || hints[ENABLE_NATIVE_FONTS])) {
      Font dfont = font.deriveFont(size);
      g2.setFont(dfont);
      textFont.setFont(dfont);
    }

    // take care of setting the textSize and textLeading vars
    // this has to happen second, because it calls textAscent()
    // (which requires the native font metrics to be set)
    super.textSize(size);
  }
Esempio n. 18
0
/**
 * Renders a scalebar graphic in a screen corner.
 *
 * @author Patrick Murris
 * @version $Id: ScalebarLayer.java 12872 2009-12-09 15:26:15Z patrickmurris $
 */
public class ScalebarLayer extends AbstractLayer {
  // Units constants
  public static final String UNIT_METRIC = "gov.nasa.worldwind.ScalebarLayer.Metric";
  public static final String UNIT_IMPERIAL = "gov.nasa.worldwind.ScalebarLayer.Imperial";

  // Display parameters - TODO: make configurable
  private Dimension size = new Dimension(150, 10);
  private Color color = Color.white;
  private int borderWidth = 20;
  private String position = AVKey.SOUTHEAST;
  private String resizeBehavior = AVKey.RESIZE_SHRINK_ONLY;
  private String unit = UNIT_METRIC;
  private Font defaultFont = Font.decode("Arial-PLAIN-12");
  private double toViewportScale = 0.2;

  private PickSupport pickSupport = new PickSupport();
  private Vec4 locationCenter = null;
  private Vec4 locationOffset = null;
  private double pixelSize;

  // Draw it as ordered with an eye distance of 0 so that it shows up in front of most other things.
  // TODO: Add general support for this common pattern.
  private OrderedIcon orderedImage = new OrderedIcon();

  private class OrderedIcon implements OrderedRenderable {
    public double getDistanceFromEye() {
      return 0;
    }

    public void pick(DrawContext dc, Point pickPoint) {
      ScalebarLayer.this.draw(dc);
    }

    public void render(DrawContext dc) {
      ScalebarLayer.this.draw(dc);
    }
  }

  /** Renders a scalebar graphic in a screen corner */
  public ScalebarLayer() {
    setPickEnabled(false);
  }

  // Public properties

  /**
   * Get the apparent pixel size in meter at the reference position.
   *
   * @return the apparent pixel size in meter at the reference position.
   */
  public double getPixelSize() {
    return this.pixelSize;
  }

  /**
   * Get the scalebar graphic Dimension (in pixels)
   *
   * @return the scalebar graphic Dimension
   */
  public Dimension getSize() {
    return this.size;
  }

  /**
   * Set the scalebar graphic Dimenion (in pixels)
   *
   * @param size the scalebar graphic Dimension
   */
  public void setSize(Dimension size) {
    if (size == null) {
      String message = Logging.getMessage("nullValue.DimensionIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }
    this.size = size;
  }

  /**
   * Get the scalebar color
   *
   * @return the scalebar Color
   */
  public Color getColor() {
    return this.color;
  }

  /**
   * Set the scalbar Color
   *
   * @param color the scalebar Color
   */
  public void setColor(Color color) {
    if (color == null) {
      String msg = Logging.getMessage("nullValue.ColorIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }
    this.color = color;
  }

  /**
   * Returns the scalebar-to-viewport scale factor.
   *
   * @return the scalebar-to-viewport scale factor
   */
  public double getToViewportScale() {
    return toViewportScale;
  }

  /**
   * Sets the scale factor applied to the viewport size to determine the displayed size of the
   * scalebar. This scale factor is used only when the layer's resize behavior is
   * AVKey.RESIZE_STRETCH or AVKey.RESIZE_SHRINK_ONLY. The scalebar's width is adjusted to occupy
   * the proportion of the viewport's width indicated by this factor. The scalebar's height is
   * adjusted to maintain the scalebar's Dimension aspect ratio.
   *
   * @param toViewportScale the scalebar to viewport scale factor
   */
  public void setToViewportScale(double toViewportScale) {
    this.toViewportScale = toViewportScale;
  }

  public String getPosition() {
    return this.position;
  }

  /**
   * Sets the relative viewport location to display the scalebar. Can be one of AVKey.NORTHEAST,
   * AVKey.NORTHWEST, AVKey.SOUTHEAST (the default), or AVKey.SOUTHWEST. These indicate the corner
   * of the viewport.
   *
   * @param position the desired scalebar position
   */
  public void setPosition(String position) {
    if (position == null) {
      String msg = Logging.getMessage("nullValue.PositionIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }
    this.position = position;
  }

  /**
   * Returns the current scalebar center location.
   *
   * @return the current location center. May be null.
   */
  public Vec4 getLocationCenter() {
    return locationCenter;
  }

  /**
   * Specifies the screen location of the scalebar center. May be null. If this value is non-null,
   * it overrides the position specified by #setPosition. The location is specified in pixels. The
   * origin is the window's lower left corner. Positive X values are to the right of the origin,
   * positive Y values are upwards from the origin. The final scalebar location will be affected by
   * the currently specified location offset if a non-null location offset has been specified (see
   * #setLocationOffset).
   *
   * @param locationCenter the scalebar center. May be null.
   * @see #setPosition, #setLocationOffset
   */
  public void setLocationCenter(Vec4 locationCenter) {
    this.locationCenter = locationCenter;
  }

  /**
   * Returns the current location offset. See #setLocationOffset for a description of the offset and
   * its values.
   *
   * @return the location offset. Will be null if no offset has been specified.
   */
  public Vec4 getLocationOffset() {
    return locationOffset;
  }

  /**
   * Specifies a placement offset from the scalebar's position on the screen.
   *
   * @param locationOffset the number of pixels to shift the scalebar from its specified screen
   *     position. A positive X value shifts the image to the right. A positive Y value shifts the
   *     image up. If null, no offset is applied. The default offset is null.
   * @see #setLocationCenter, #setPosition
   */
  public void setLocationOffset(Vec4 locationOffset) {
    this.locationOffset = locationOffset;
  }

  /**
   * Returns the layer's resize behavior.
   *
   * @return the layer's resize behavior
   */
  public String getResizeBehavior() {
    return resizeBehavior;
  }

  /**
   * Sets the behavior the layer uses to size the scalebar when the viewport size changes, typically
   * when the World Wind window is resized. If the value is AVKey.RESIZE_KEEP_FIXED_SIZE, the
   * scalebar size is kept to the size specified in its Dimension scaled by the layer's current icon
   * scale. If the value is AVKey.RESIZE_STRETCH, the scalebar is resized to have a constant size
   * relative to the current viewport size. If the viewport shrinks the scalebar size decreases; if
   * it expands then the scalebar enlarges. If the value is AVKey.RESIZE_SHRINK_ONLY (the default),
   * scalebar sizing behaves as for AVKey.RESIZE_STRETCH but it will not grow larger than the size
   * specified in its Dimension.
   *
   * @param resizeBehavior the desired resize behavior
   */
  public void setResizeBehavior(String resizeBehavior) {
    this.resizeBehavior = resizeBehavior;
  }

  public int getBorderWidth() {
    return borderWidth;
  }

  /**
   * Sets the scalebar offset from the viewport border.
   *
   * @param borderWidth the number of pixels to offset the scalebar from the borders indicated by
   *     {@link #setPosition(String)}.
   */
  public void setBorderWidth(int borderWidth) {
    this.borderWidth = borderWidth;
  }

  public String getUnit() {
    return this.unit;
  }

  /**
   * Sets the unit the scalebar uses to display distances. Can be one of {@link #UNIT_METRIC} (the
   * default), or {@link #UNIT_IMPERIAL}.
   *
   * @param unit the desired unit
   */
  public void setUnit(String unit) {
    this.unit = unit;
  }

  /**
   * Get the scalebar legend Fon
   *
   * @return the scalebar legend Font
   */
  public Font getFont() {
    return this.defaultFont;
  }

  /**
   * Set the scalebar legend Fon
   *
   * @param font the scalebar legend Font
   */
  public void setFont(Font font) {
    if (font == null) {
      String msg = Logging.getMessage("nullValue.FontIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }
    this.defaultFont = font;
  }

  // Rendering
  @Override
  public void doRender(DrawContext dc) {
    dc.addOrderedRenderable(this.orderedImage);
  }

  @Override
  public void doPick(DrawContext dc, Point pickPoint) {
    // Delegate drawing to the ordered renderable list
    dc.addOrderedRenderable(this.orderedImage);
  }

  // Rendering
  public void draw(DrawContext dc) {
    GL gl = dc.getGL();

    boolean attribsPushed = false;
    boolean modelviewPushed = false;
    boolean projectionPushed = false;

    try {
      gl.glPushAttrib(
          GL.GL_DEPTH_BUFFER_BIT
              | GL.GL_COLOR_BUFFER_BIT
              | GL.GL_ENABLE_BIT
              | GL.GL_TEXTURE_BIT
              | GL.GL_TRANSFORM_BIT
              | GL.GL_VIEWPORT_BIT
              | GL.GL_CURRENT_BIT);
      attribsPushed = true;

      gl.glDisable(GL.GL_TEXTURE_2D); // no textures

      gl.glEnable(GL.GL_BLEND);
      gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
      gl.glDisable(GL.GL_DEPTH_TEST);

      double width = this.size.width;
      double height = this.size.height;

      // Load a parallel projection with xy dimensions (viewportWidth, viewportHeight)
      // into the GL projection matrix.
      java.awt.Rectangle viewport = dc.getView().getViewport();
      gl.glMatrixMode(javax.media.opengl.GL.GL_PROJECTION);
      gl.glPushMatrix();
      projectionPushed = true;
      gl.glLoadIdentity();
      double maxwh = width > height ? width : height;
      gl.glOrtho(0d, viewport.width, 0d, viewport.height, -0.6 * maxwh, 0.6 * maxwh);

      gl.glMatrixMode(GL.GL_MODELVIEW);
      gl.glPushMatrix();
      modelviewPushed = true;
      gl.glLoadIdentity();

      // Scale to a width x height space
      // located at the proper position on screen
      double scale = this.computeScale(viewport);
      Vec4 locationSW = this.computeLocation(viewport, scale);
      gl.glTranslated(locationSW.x(), locationSW.y(), locationSW.z());
      gl.glScaled(scale, scale, 1);

      // Compute scale size in real world
      Position referencePosition = dc.getViewportCenterPosition();
      if (referencePosition != null) {
        Vec4 groundTarget = dc.getGlobe().computePointFromPosition(referencePosition);
        Double distance = dc.getView().getEyePoint().distanceTo3(groundTarget);
        this.pixelSize = dc.getView().computePixelSizeAtDistance(distance);
        Double scaleSize = this.pixelSize * width * scale; // meter
        String unitLabel = "m";
        if (this.unit.equals(UNIT_METRIC)) {
          if (scaleSize > 10000) {
            scaleSize /= 1000;
            unitLabel = "Km";
          }
        } else if (this.unit.equals(UNIT_IMPERIAL)) {
          scaleSize *= 3.280839895; // feet
          unitLabel = "ft";
          if (scaleSize > 5280) {
            scaleSize /= 5280;
            unitLabel = "mile(s)";
          }
        }

        // Rounded division size
        int pot = (int) Math.floor(Math.log10(scaleSize));
        if (!Double.isNaN(pot)) {
          int digit = Integer.parseInt(String.format("%.0f", scaleSize).substring(0, 1));
          double divSize = digit * Math.pow(10, pot);
          if (digit >= 5) divSize = 5 * Math.pow(10, pot);
          else if (digit >= 2) divSize = 2 * Math.pow(10, pot);
          double divWidth = width * divSize / scaleSize;

          // Draw scale
          if (!dc.isPickingMode()) {
            // Set color using current layer opacity
            Color backColor = this.getBackgroundColor(this.color);
            float[] colorRGB = backColor.getRGBColorComponents(null);
            gl.glColor4d(
                colorRGB[0],
                colorRGB[1],
                colorRGB[2],
                (double) backColor.getAlpha() / 255d * this.getOpacity());
            gl.glTranslated((width - divWidth) / 2, 0d, 0d);
            this.drawScale(dc, divWidth, height);

            colorRGB = this.color.getRGBColorComponents(null);
            gl.glColor4d(colorRGB[0], colorRGB[1], colorRGB[2], this.getOpacity());
            gl.glTranslated(-1d / scale, 1d / scale, 0d);
            this.drawScale(dc, divWidth, height);

            // Draw label
            String label = String.format("%.0f ", divSize) + unitLabel;
            gl.glLoadIdentity();
            gl.glDisable(GL.GL_CULL_FACE);
            drawLabel(
                dc,
                label,
                locationSW.add3(
                    new Vec4(divWidth * scale / 2 + (width - divWidth) / 2, height * scale, 0)));
          } else {
            // Picking
            this.pickSupport.clearPickList();
            this.pickSupport.beginPicking(dc);
            // Draw unique color across the map
            Color color = dc.getUniquePickColor();
            int colorCode = color.getRGB();
            // Add our object(s) to the pickable list
            this.pickSupport.addPickableObject(colorCode, this, referencePosition, false);
            gl.glColor3ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue());
            gl.glTranslated((width - divWidth) / 2, 0d, 0d);
            this.drawRectangle(dc, divWidth, height);
            // Done picking
            this.pickSupport.endPicking(dc);
            this.pickSupport.resolvePick(dc, dc.getPickPoint(), this);
          }
        }
      }
    } finally {
      if (projectionPushed) {
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glPopMatrix();
      }
      if (modelviewPushed) {
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glPopMatrix();
      }
      if (attribsPushed) gl.glPopAttrib();
    }
  }

  // Draw scale rectangle
  private void drawRectangle(DrawContext dc, double width, double height) {
    GL gl = dc.getGL();
    gl.glBegin(GL.GL_POLYGON);
    gl.glVertex3d(0, height, 0);
    gl.glVertex3d(0, 0, 0);
    gl.glVertex3d(width, 0, 0);
    gl.glVertex3d(width, height, 0);
    gl.glVertex3d(0, height, 0);
    gl.glEnd();
  }

  // Draw scale graphic
  private void drawScale(DrawContext dc, double width, double height) {
    GL gl = dc.getGL();
    gl.glBegin(GL.GL_LINE_STRIP);
    gl.glVertex3d(0, height, 0);
    gl.glVertex3d(0, 0, 0);
    gl.glVertex3d(width, 0, 0);
    gl.glVertex3d(width, height, 0);
    gl.glEnd();
    gl.glBegin(GL.GL_LINE_STRIP);
    gl.glVertex3d(width / 2, 0, 0);
    gl.glVertex3d(width / 2, height / 2, 0);
    gl.glEnd();
  }

  // Draw the scale label
  private void drawLabel(DrawContext dc, String text, Vec4 screenPoint) {
    TextRenderer textRenderer =
        OGLTextRenderer.getOrCreateTextRenderer(dc.getTextRendererCache(), this.defaultFont);

    Rectangle2D nameBound = textRenderer.getBounds(text);
    int x = (int) (screenPoint.x() - nameBound.getWidth() / 2d);
    int y = (int) screenPoint.y();

    textRenderer.begin3DRendering();

    textRenderer.setColor(this.getBackgroundColor(this.color));
    textRenderer.draw(text, x + 1, y - 1);
    textRenderer.setColor(this.color);
    textRenderer.draw(text, x, y);

    textRenderer.end3DRendering();
  }

  private final float[] compArray = new float[4];
  // Compute background color for best contrast
  private Color getBackgroundColor(Color color) {
    Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), compArray);
    if (compArray[2] > 0.5) return new Color(0, 0, 0, 0.7f);
    else return new Color(1, 1, 1, 0.7f);
  }

  private double computeScale(java.awt.Rectangle viewport) {
    if (this.resizeBehavior.equals(AVKey.RESIZE_SHRINK_ONLY)) {
      return Math.min(1d, (this.toViewportScale) * viewport.width / this.size.width);
    } else if (this.resizeBehavior.equals(AVKey.RESIZE_STRETCH)) {
      return (this.toViewportScale) * viewport.width / this.size.width;
    } else if (this.resizeBehavior.equals(AVKey.RESIZE_KEEP_FIXED_SIZE)) {
      return 1d;
    } else {
      return 1d;
    }
  }

  private Vec4 computeLocation(java.awt.Rectangle viewport, double scale) {
    double scaledWidth = scale * this.size.width;
    double scaledHeight = scale * this.size.height;

    double x;
    double y;

    if (this.locationCenter != null) {
      x = this.locationCenter.x - scaledWidth / 2;
      y = this.locationCenter.y - scaledHeight / 2;
    } else if (this.position.equals(AVKey.NORTHEAST)) {
      x = viewport.getWidth() - scaledWidth - this.borderWidth;
      y = viewport.getHeight() - scaledHeight - this.borderWidth;
    } else if (this.position.equals(AVKey.SOUTHEAST)) {
      x = viewport.getWidth() - scaledWidth - this.borderWidth;
      y = 0d + this.borderWidth;
    } else if (this.position.equals(AVKey.NORTHWEST)) {
      x = 0d + this.borderWidth;
      y = viewport.getHeight() - scaledHeight - this.borderWidth;
    } else if (this.position.equals(AVKey.SOUTHWEST)) {
      x = 0d + this.borderWidth;
      y = 0d + this.borderWidth;
    } else // use North East
    {
      x = viewport.getWidth() - scaledWidth / 2 - this.borderWidth;
      y = viewport.getHeight() - scaledHeight / 2 - this.borderWidth;
    }

    if (this.locationOffset != null) {
      x += this.locationOffset.x;
      y += this.locationOffset.y;
    }

    return new Vec4(x, y, 0);
  }

  @Override
  public String toString() {
    return Logging.getMessage("layers.Earth.ScalebarLayer.Name");
  }
}
Esempio n. 19
0
  public GamePanel(String name, int l) {
    setFocusable(true);
    grabFocus();
    addMouseListener(this);
    addMouseMotionListener(this);
    addKeyListener(this);
    keys = new boolean[10000];

    try {
      scoreFont =
          Font.createFont(Font.TRUETYPE_FONT, new FileInputStream(new File("BRLNSB.ttf")))
              .deriveFont(0, 32);
    } catch (IOException ioe) {
      System.out.println("error loading BRLNSB.tff");
    } catch (FontFormatException ffe) {
      System.out.println("Something went wrong with the font.");
    }

    gameBckgrnd = new ImageIcon("Backgroundimage.png").getImage();
    for (int i = 1; i < 7; i++) {
      magnetList.add(new ImageIcon("gamelayerstuff/powerups/magnet" + i + ".png").getImage());
    }

    coinPic = new ImageIcon("gamelayerstuff/coins/byellowcoin1.png").getImage();
    player1 = new Player(200, 300, 100, "sheldon");

    click = false;
    ground = true;
    pause = false;
    lvlClear = false;
    die = false;
    musicOn = true;

    player1.setVelo(150);
    player1.setInvi(true);
    // ------------------------------------------------------------------------------------------------------------------------------------
    // Sound
    coinSound = Applet.newAudioClip(getClass().getResource("coin_pickup_2.wav"));
    clicked = Applet.newAudioClip(getClass().getResource("menu_deselect.wav"));
    starSound = Applet.newAudioClip(getClass().getResource("star.wav"));
    bounce = Applet.newAudioClip(getClass().getResource("grav_step_4.wav"));
    bckGrndMusic = Applet.newAudioClip(getClass().getResource("bgmusic00.wav"));
    bckGrndMusic.loop();
    // ------------------------------------------------------------------------------------------------------------------------------------

    // distance=0;
    score = 0;
    coins = 0;
    level = 1;
    prevLvl = level;
    backy = 0;
    height = backy;
    dieHeight = 0;
    dieMenuHeight = 0;

    pauseB = new SButton(400, 670, "pause", "");
    resumeB = new SButton(100, 500, "resume", "");
    menuB = new SButton(100, 620, "back", "");
    muteB = new SButton(400, 600, "mute", "");
    unmuteB = new SButton(400, 600, "unmute", "");
    // System.out.println("characters/"+name+"/"+name+"35.png");
  }
Esempio n. 20
0
  static void paintShadowTitle(
      Graphics g,
      String title,
      int x,
      int y,
      Color frente,
      Color shadow,
      int desp,
      int tipo,
      int orientation) {

    // Si hay que rotar la fuente, se rota
    Font f = g.getFont();
    if (orientation == SwingConstants.VERTICAL) {
      AffineTransform rotate = AffineTransform.getRotateInstance(Math.PI / 2);
      f = f.deriveFont(rotate);
    }

    // Si hay que pintar sombra, se hacen un monton de cosas
    if (shadow != null) {
      int matrix = (tipo == THIN ? MATRIX_THIN : MATRIX_FAT);

      Rectangle2D rect = g.getFontMetrics().getStringBounds(title, g);

      int w, h;
      if (orientation == SwingConstants.HORIZONTAL) {
        w = (int) rect.getWidth() + 6 * matrix; // Hay que dejar espacio para las sombras y el borde
        h = (int) rect.getHeight() + 6 * matrix; // que ConvolveOp ignora por el EDGE_NO_OP
      } else {
        h = (int) rect.getWidth() + 6 * matrix; // Hay que dejar espacio para las sombras y el borde
        w = (int) rect.getHeight() + 6 * matrix; // que ConvolveOp ignora por el EDGE_NO_OP
      }

      // La sombra del titulo
      BufferedImage iTitulo = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
      BufferedImage iSombra = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

      Graphics2D g2 = iTitulo.createGraphics();
      g2.setRenderingHint(
          RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

      g2.setFont(f);
      g2.setColor(shadow);
      g2.drawString(title, 3 * matrix, 3 * matrix); // La pintamos en el centro

      ConvolveOp cop =
          new ConvolveOp((tipo == THIN ? kernelThin : kernelFat), ConvolveOp.EDGE_NO_OP, null);
      cop.filter(iTitulo, iSombra); // A ditorsionar

      // Por fin, pintamos el jodio titulo
      g.drawImage(
          iSombra,
          x - 3 * matrix + desp, // Lo llevamos a la posicion original y le sumamos 1
          y - 3 * matrix + desp, // para que la sombra quede pelin desplazada
          null);
    }

    // Si hay que pintar el frente, se pinta
    if (frente != null) {
      g.setFont(f);
      g.setColor(frente);
      g.drawString(title, x, y);
    }
  }
Esempio n. 21
0
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    int w = getWidth();
    int h = getHeight();
    double xScale = (w - 2 * PAD) / (xMax - xMin);
    double yScale = (h - 2 * PAD) / (yMax - yMin);
    if (firstTime) System.out.printf("xScale = %.1f  yScale = %.1f%n", xScale, yScale);
    Point2D.Double origin = new Point2D.Double(); // Axes origin.
    Point2D.Double offset = new Point2D.Double(); // Locate data.
    if (xMax < 0) {
      origin.x = w - PAD;
      offset.x = origin.x - xScale * xMax;
    } else if (xMin < 0) {
      origin.x = PAD - xScale * xMin;
      offset.x = origin.x;
    } else {
      origin.x = PAD;
      offset.x = PAD - xScale * xMin;
    }
    if (yMax < 0) {
      origin.y = h - PAD;
      offset.y = origin.y - yScale * yMax;
    } else if (yMin < 0) {
      origin.y = PAD - yScale * yMin;
      offset.y = origin.y;
    } else {
      origin.y = PAD;
      offset.y = PAD - yScale * yMin;
    }
    if (firstTime) {
      System.out.printf("origin = [%6.1f, %6.1f]%n", origin.x, origin.y);
      System.out.printf("offset = [%6.1f, %6.1f]%n", offset.x, offset.y);
    }

    // Draw abcissa.
    g2.draw(new Line2D.Double(PAD, origin.y, w - PAD, origin.y));
    // Draw ordinate.
    g2.draw(new Line2D.Double(origin.x, PAD, origin.x, h - PAD));
    g2.setPaint(Color.red);
    // Mark origin.
    g2.fill(new Ellipse2D.Double(origin.x - 2, origin.y - 2, 4, 4));

    // Plot data.
    g2.setPaint(Color.blue);
    for (int i = 0; i < x.length; i++) {
      double x1 = offset.x + xScale * x[i];
      double y1 = offset.y + yScale * y[i];
      if (firstTime) System.out.printf("i = %d  x1 = %6.1f  y1 = %.1f%n", i, x1, y1);
      g2.fill(new Ellipse2D.Double(x1 - 2, y1 - 2, 4, 4));
      g2.drawString(String.valueOf(i), (float) x1 + 3, (float) y1 - 3);
    }

    // Draw extreme data values.
    g2.setPaint(Color.black);
    Font font = g2.getFont();
    FontRenderContext frc = g2.getFontRenderContext();
    LineMetrics lm = font.getLineMetrics("0", frc);
    String s = String.format("%.1f", xMin);
    float width = (float) font.getStringBounds(s, frc).getWidth();
    double x = offset.x + xScale * xMin;
    g2.drawString(s, (float) x, (float) origin.y + lm.getAscent());
    s = String.format("%.1f", xMax);
    width = (float) font.getStringBounds(s, frc).getWidth();
    x = offset.x + xScale * xMax;
    g2.drawString(s, (float) x - width, (float) origin.y + lm.getAscent());
    s = String.format("%.1f", yMin);
    width = (float) font.getStringBounds(s, frc).getWidth();
    double y = offset.y + yScale * yMin;
    g2.drawString(s, (float) origin.x + 1, (float) y + lm.getAscent());
    s = String.format("%.1f", yMax);
    width = (float) font.getStringBounds(s, frc).getWidth();
    y = offset.y + yScale * yMax;
    g2.drawString(s, (float) origin.x + 1, (float) y);
    if (firstTime) System.out.println("------------------------------");
    firstTime = false;
  }
Esempio n. 22
0
/**
 * A label drawn as part of a tactical graphic. The label is drawn at constant screen size. The
 * label can include multiple lines of text, and can optionally be kept aligned with features on the
 * globe. To align a label with the globe specify an {@link
 * #setOrientationPosition(gov.nasa.worldwind.geom.Position) orientationPosition} for the label. The
 * label will be drawn along a line connecting the label's position to the orientation position.
 *
 * @author pabercrombie
 * @version $Id$
 */
public class TacticalGraphicLabel implements OrderedRenderable {
  /** Default font. */
  public static final Font DEFAULT_FONT = Font.decode("Arial-BOLD-16");
  /**
   * Default offset. The default offset aligns the label horizontal with the text alignment
   * position, and centers the label vertically. For example, if the text alignment is <code>
   * AVKey.LEFT</code>, then the left edge of the text will be aligned with the geographic position,
   * and the label will be centered vertically.
   */
  public static final Offset DEFAULT_OFFSET = new Offset(0d, -0.5d, AVKey.FRACTION, AVKey.FRACTION);
  /** Default insets around the label. */
  public static final Insets DEFAULT_INSETS = new Insets(5, 5, 5, 5);
  /** Default interior opacity. */
  public static final double DEFAULT_INTERIOR_OPACITY = 0.7;
  /** Default text effect (shadow). */
  public static final String DEFAULT_TEXT_EFFECT = AVKey.TEXT_EFFECT_SHADOW;

  /** Text split into separate lines. */
  protected String[] lines;
  /** The label's geographic position. */
  protected Position position;
  /** Offset from the geographic position at which to draw the label. */
  protected Offset offset = DEFAULT_OFFSET;
  /** Text alignment for multi-line labels. */
  protected String textAlign = AVKey.LEFT;
  /** The label is drawn along a line from the label position to the orientation position. */
  protected Position orientationPosition;

  /** Material used to draw the label. */
  protected Material material = Material.BLACK;
  /** Opacity of the text, as a value between 0 and 1. */
  protected double opacity = 1.0;

  protected double interiorOpacity = DEFAULT_INTERIOR_OPACITY;
  /** Font used to draw the label. */
  protected Font font = DEFAULT_FONT;
  /** Space (in pixels) between lines in a multi-line label. */
  protected int lineSpacing = 5; // TODO compute default based on font size

  /**
   * Effect applied to the text. May be {@link AVKey#TEXT_EFFECT_SHADOW} or {@link
   * AVKey#TEXT_EFFECT_NONE}.
   */
  protected String effect = DEFAULT_TEXT_EFFECT;
  /**
   * Insets that separate the text from its frame. Only applies when the text interior is rendered.
   */
  protected Insets insets = DEFAULT_INSETS;
  /** Indicates whether or not to draw the label interior. */
  protected boolean drawInterior;

  /** Indicates whether or not batch rendering is enabled. */
  protected boolean enableBatchRendering = false;
  /** Indicates whether or not batch picking is enabled. */
  protected boolean enableBatchPicking = true;

  /** Indicates an object that represents the label during picking. */
  protected Object delegateOwner;

  // Computed each frame
  protected long frameTimeStamp = -1L;
  /** Geographic position in cartesian coordinates. */
  protected Vec4 placePoint;
  /** Location of the place point projected onto the screen. */
  protected Vec4 screenPlacePoint;
  /**
   * Location of the upper left corner of the text measured from the lower left corner of the
   * viewport. This point in OGL coordinates.
   */
  protected Point screenPoint;
  /**
   * Rotation applied to the label. This is computed each frame based on the orientation position.
   */
  protected Angle rotation;
  /**
   * Height of a line of text, computed in {@link
   * #computeBoundsIfNeeded(gov.nasa.worldwind.render.DrawContext)}.
   */
  protected int lineHeight;
  /** Size of the label. */
  protected Rectangle2D bounds;
  /** Cached bounds for each line of text. */
  protected Rectangle2D[] lineBounds;
  /** Extent of the label on the screen. */
  protected Rectangle screenExtent;
  /** Distance from the eye point to the label's geographic location. */
  protected double eyeDistance;

  /** Stack handler used for beginDrawing/endDrawing state. */
  protected OGLStackHandler BEogsh = new OGLStackHandler();
  /** Support object used during picking. */
  protected PickSupport pickSupport = new PickSupport();
  /** Active layer. */
  protected Layer pickLayer;

  /** Create a new empty label. */
  public TacticalGraphicLabel() {}

  /**
   * Create a new label.
   *
   * @param text Label text.
   */
  public TacticalGraphicLabel(String text) {
    this.setText(text);
  }

  /**
   * Indicates the text of this label.
   *
   * @return The label's text.
   */
  public String getText() {
    if (this.lines != null) {
      StringBuilder sb = new StringBuilder();

      for (int i = 0; i < this.lines.length - 1; i++) {
        sb.append(this.lines[i]).append("\n");
      }
      sb.append(this.lines[this.lines.length - 1]);

      return sb.toString();
    }

    return null;
  }

  /**
   * Specifies the text of this label. The text may include multiple lines, separated by newline
   * characters.
   *
   * @param text New text.
   */
  public void setText(String text) {
    if (text != null) this.lines = text.split("\n");
    else this.lines = null;

    this.bounds = null; // Need to recompute
  }

  /**
   * Indicates the label's position. The label is drawn at an offset from this position.
   *
   * @return The label's geographic position.
   * @see #getOffset()
   */
  public Position getPosition() {
    return this.position;
  }

  /**
   * Indicates the label's geographic position. The label is drawn at an offset from this position.
   *
   * @param position New position.
   * @see #getOffset()
   */
  public void setPosition(Position position) {
    this.position = position;

    // Label has moved, need to recompute screen extent. Explicitly set the extent to null so that
    // it will be
    // recomputed even if the application calls setPosition multiple times per frame.
    this.screenExtent = null;
  }

  /**
   * Indicates the current text alignment. Can be one of {@link AVKey#LEFT} (default), {@link
   * AVKey#CENTER} or {@link AVKey#RIGHT}.
   *
   * @return the current text alignment.
   */
  public String getTextAlign() {
    return this.textAlign;
  }

  /**
   * Specifies the text alignment. Can be one of {@link AVKey#LEFT} (default), {@link AVKey#CENTER},
   * or {@link AVKey#RIGHT}.
   *
   * @param textAlign New text alignment.
   */
  public void setTextAlign(String textAlign) {
    if (textAlign == null) {
      String message = Logging.getMessage("nullValue.StringIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.textAlign = textAlign;
  }

  /**
   * Indicates the offset from the geographic position at which to draw the label. See {@link
   * #setOffset(gov.nasa.worldwind.render.Offset) setOffset} for more information on how the offset
   * is interpreted.
   *
   * @return The offset at which to draw the label.
   */
  public Offset getOffset() {
    return this.offset;
  }

  /**
   * Specifies the offset from the geographic position at which to draw the label. The default
   * offset aligns the label horizontal with the text alignment position, and centers the label
   * vertically. For example, if the text alignment is <code>AVKey.LEFT</code>., then the left edge
   * of the text will be aligned with the geographic position, and the label will be centered
   * vertically.
   *
   * <p>When the text is rotated a horizontal offset moves the text along the orientation line, and
   * a vertical offset moves the text perpendicular to the orientation line.
   *
   * @param offset The offset at which to draw the label.
   */
  public void setOffset(Offset offset) {
    if (offset == null) {
      String message = Logging.getMessage("nullValue.OffsetIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.offset = offset;
  }

  /**
   * Indicates the font used to draw the label.
   *
   * @return The label's font.
   */
  public Font getFont() {
    return this.font;
  }

  /**
   * Specifies the font used to draw the label.
   *
   * @param font New font.
   */
  public void setFont(Font font) {
    if (font == null) {
      String message = Logging.getMessage("nullValue.FontIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    if (font != this.font) {
      this.font = font;
      this.bounds = null; // Need to recompute
    }
  }

  /**
   * Indicates the line spacing applied to multi-line labels.
   *
   * @return The space (in pixels) between lines of a multi-line label.
   */
  public int getLineSpacing() {
    return lineSpacing;
  }

  /**
   * Specifies the line spacing applied to multi-line labels.
   *
   * @param lineSpacing New line spacing.
   */
  public void setLineSpacing(int lineSpacing) {
    if (lineSpacing < 0) {
      String message = Logging.getMessage("generic.ArgumentOutOfRange");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.lineSpacing = lineSpacing;
  }

  /**
   * Indicates the material used to draw the label.
   *
   * @return The label's material.
   */
  public Material getMaterial() {
    return this.material;
  }

  /**
   * Specifies the material used to draw the label.
   *
   * @param material New material.
   */
  public void setMaterial(Material material) {
    if (material == null) {
      String message = Logging.getMessage("nullValue.MaterialIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.material = material;
  }

  /**
   * Indicates whether or not to draw a colored frame behind the label.
   *
   * @return <code>true</code> if the label's interior is drawn, otherwise <code>false</code>.
   * @see #setDrawInterior(boolean)
   */
  public boolean isDrawInterior() {
    return this.drawInterior;
  }

  /**
   * Specifies whether or not to draw a colored frame behind the label.
   *
   * @param drawInterior <code>true</code> if the label's interior is drawn, otherwise <code>false
   *     </code>.
   * @see #isDrawInterior()
   */
  public void setDrawInterior(boolean drawInterior) {
    this.drawInterior = drawInterior;
  }

  /**
   * Indicates the opacity of the text as a floating-point value in the range 0.0 to 1.0. A value of
   * 1.0 specifies a completely opaque text, and 0.0 specifies a completely transparent text. Values
   * in between specify a partially transparent text.
   *
   * @return the opacity of the text as a floating-point value from 0.0 to 1.0.
   */
  public double getOpacity() {
    return this.opacity;
  }

  /**
   * Specifies the opacity of the text as a floating-point value in the range 0.0 to 1.0. A value of
   * 1.0 specifies a completely opaque text, and 0.0 specifies a completely transparent text. Values
   * in between specify a partially transparent text.
   *
   * @param opacity the opacity of text as a floating-point value from 0.0 to 1.0.
   * @throws IllegalArgumentException if <code>opacity</code> is less than 0.0 or greater than 1.0.
   */
  public void setOpacity(double opacity) {
    if (opacity < 0 || opacity > 1) {
      String message = Logging.getMessage("generic.OpacityOutOfRange", opacity);
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.opacity = opacity;
  }

  /**
   * Indicates the opacity of label's interior as a floating-point value in the range 0.0 to 1.0. A
   * value of 1.0 specifies a completely opaque interior, and 0.0 specifies a completely transparent
   * interior. Values in between specify a partially transparent interior.
   *
   * @return the opacity of the interior as a floating-point value from 0.0 to 1.0.
   */
  public double getInteriorOpacity() {
    return this.interiorOpacity;
  }

  /**
   * Specifies the opacity of the label's interior as a floating-point value in the range 0.0 to
   * 1.0. A value of 1.0 specifies a completely opaque interior, and 0.0 specifies a completely
   * transparent interior. Values in between specify a partially transparent interior.
   *
   * @param interiorOpacity the opacity of label's interior as a floating-point value from 0.0 to
   *     1.0.
   * @throws IllegalArgumentException if <code>opacity</code> is less than 0.0 or greater than 1.0.
   */
  public void setInteriorOpacity(double interiorOpacity) {
    if (opacity < 0 || opacity > 1) {
      String message = Logging.getMessage("generic.OpacityOutOfRange", opacity);
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.interiorOpacity = interiorOpacity;
  }

  /**
   * Indicates the orientation position. The label oriented on a line drawn from the label's
   * position to the orientation position.
   *
   * @return Position used to orient the label. May be null.
   */
  public Position getOrientationPosition() {
    return this.orientationPosition;
  }

  /**
   * Specifies the orientation position. The label is oriented on a line drawn from the label's
   * position to the orientation position. If the orientation position is null then the label is
   * drawn with no rotation.
   *
   * @param orientationPosition Draw label oriented toward this position.
   */
  public void setOrientationPosition(Position orientationPosition) {
    this.orientationPosition = orientationPosition;
  }

  /**
   * Indicates the amount of space between the label's content and its frame, in pixels.
   *
   * @return the padding between the label's content and its frame, in pixels.
   * @see #setInsets(java.awt.Insets)
   */
  public Insets getInsets() {
    return this.insets;
  }

  /**
   * Specifies the amount of space (in pixels) between the label's content and the edges of the
   * label's frame.
   *
   * @param insets the desired padding between the label's content and its frame, in pixels.
   * @throws IllegalArgumentException if <code>insets</code> is <code>null</code>.
   * @see #getInsets()
   */
  public void setInsets(Insets insets) {
    if (insets == null) {
      String message = Logging.getMessage("nullValue.InsetsIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.insets = insets;
  }

  /**
   * Indicates an effect used to decorate the text. Can be one of {@link AVKey#TEXT_EFFECT_SHADOW}
   * (default), or {@link AVKey#TEXT_EFFECT_NONE}.
   *
   * @return the effect used for text rendering
   */
  public String getEffect() {
    return this.effect;
  }

  /**
   * Specifies an effect used to decorate the text. Can be one of {@link AVKey#TEXT_EFFECT_SHADOW}
   * (default), or {@link AVKey#TEXT_EFFECT_NONE}.
   *
   * @param effect the effect to use for text rendering
   */
  public void setEffect(String effect) {
    if (effect == null) {
      String message = Logging.getMessage("nullValue.StringIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.effect = effect;
  }

  /**
   * Returns the delegate owner of this label. If non-null, the returned object replaces the label
   * as the pickable object returned during picking. If null, the label itself is the pickable
   * object returned during picking.
   *
   * @return the object used as the pickable object returned during picking, or null to indicate the
   *     the label is returned during picking.
   */
  public Object getDelegateOwner() {
    return this.delegateOwner;
  }

  /**
   * Specifies the delegate owner of this label. If non-null, the delegate owner replaces the label
   * as the pickable object returned during picking. If null, the label itself is the pickable
   * object returned during picking.
   *
   * @param owner the object to use as the pickable object returned during picking, or null to
   *     return the label.
   */
  public void setDelegateOwner(Object owner) {
    this.delegateOwner = owner;
  }

  /**
   * Indicates whether batch picking is enabled.
   *
   * @return true if batch rendering is enabled, otherwise false.
   * @see #setEnableBatchPicking(boolean).
   */
  public boolean isEnableBatchPicking() {
    return this.enableBatchPicking;
  }

  /**
   * Specifies whether adjacent Labels in the ordered renderable list may be pick-tested together if
   * they are contained in the same layer. This increases performance but allows only the top-most
   * of the label to be reported in a {@link gov.nasa.worldwind.event.SelectEvent} even if several
   * of the labels are at the pick position.
   *
   * <p>Batch rendering ({@link #setEnableBatchRendering(boolean)}) must be enabled in order for
   * batch picking to occur.
   *
   * @param enableBatchPicking true to enable batch rendering, otherwise false.
   */
  public void setEnableBatchPicking(boolean enableBatchPicking) {
    this.enableBatchPicking = enableBatchPicking;
  }

  /**
   * Indicates whether batch rendering is enabled.
   *
   * @return true if batch rendering is enabled, otherwise false.
   * @see #setEnableBatchRendering(boolean).
   */
  public boolean isEnableBatchRendering() {
    return this.enableBatchRendering;
  }

  /**
   * Specifies whether adjacent Labels in the ordered renderable list may be rendered together if
   * they are contained in the same layer. This increases performance and there is seldom a reason
   * to disable it.
   *
   * @param enableBatchRendering true to enable batch rendering, otherwise false.
   */
  public void setEnableBatchRendering(boolean enableBatchRendering) {
    this.enableBatchRendering = enableBatchRendering;
  }

  /**
   * Get the label bounding {@link java.awt.Rectangle} using OGL coordinates - bottom-left corner x
   * and y relative to the {@link gov.nasa.worldwind.WorldWindow} bottom-left corner. If the label
   * is rotated then the returned rectangle is the bounding rectangle of the rotated label.
   *
   * @param dc the current DrawContext.
   * @return the label bounding {@link java.awt.Rectangle} using OGL viewport coordinates.
   * @throws IllegalArgumentException if <code>dc</code> is null.
   */
  public Rectangle getBounds(DrawContext dc) {
    if (dc == null) {
      String message = Logging.getMessage("nullValue.DrawContextIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.computeGeometryIfNeeded(dc);
    return this.screenExtent;
  }

  /**
   * Compute label geometry, if it has not already been computed this frame, or if the label
   * position has changed since the extent was last computed.
   *
   * @param dc Current geometry.
   */
  protected void computeGeometryIfNeeded(DrawContext dc) {
    // Re-use rendering state values already calculated this frame. If the screenExtent is null,
    // recompute even if
    // the timestamp is the same. This prevents using a stale position if the application calls
    // setPosition and
    // getBounds multiple times before the label is rendered.
    long timeStamp = dc.getFrameTimeStamp();
    if (timeStamp != this.frameTimeStamp || this.screenExtent == null) {
      this.computeGeometry(dc);
      this.frameTimeStamp = timeStamp;
    }
  }

  /**
   * Compute the bounds of the text, if necessary.
   *
   * @param dc the current DrawContext.
   */
  protected void computeBoundsIfNeeded(DrawContext dc) {
    // Do not compute bounds if they are available. Computing text bounds is expensive, so only do
    // this
    // calculation if necessary.
    if (this.bounds != null) return;

    TextRenderer textRenderer =
        OGLTextRenderer.getOrCreateTextRenderer(dc.getTextRendererCache(), this.getFont());

    int width = 0;
    int maxLineHeight = 0;
    this.lineBounds = new Rectangle2D[this.lines.length];

    for (int i = 0; i < this.lines.length; i++) {
      Rectangle2D lineBounds = textRenderer.getBounds(lines[i]);
      width = (int) Math.max(lineBounds.getWidth(), width);

      double thisLineHeight = Math.abs(lineBounds.getY());
      maxLineHeight = (int) Math.max(thisLineHeight, maxLineHeight);

      this.lineBounds[i] = lineBounds;
    }
    this.lineHeight = maxLineHeight;

    // Compute final height using maxLineHeight and number of lines
    this.bounds =
        new Rectangle(
            this.lines.length,
            maxLineHeight,
            width,
            this.lines.length * maxLineHeight + this.lines.length * this.lineSpacing);
  }

  /**
   * Compute the label's screen position from its geographic position.
   *
   * @param dc Current draw context.
   */
  protected void computeGeometry(DrawContext dc) {
    // Project the label position onto the viewport
    Position pos = this.getPosition();
    if (pos == null) return;

    this.placePoint = dc.computeTerrainPoint(pos.getLatitude(), pos.getLongitude(), 0);
    this.screenPlacePoint = dc.getView().project(this.placePoint);

    this.eyeDistance = this.placePoint.distanceTo3(dc.getView().getEyePoint());

    boolean orientationReversed = false;
    if (this.orientationPosition != null) {
      // Project the orientation point onto the screen
      Vec4 orientationPlacePoint =
          dc.computeTerrainPoint(
              this.orientationPosition.getLatitude(), this.orientationPosition.getLongitude(), 0);
      Vec4 orientationScreenPoint = dc.getView().project(orientationPlacePoint);

      this.rotation = this.computeRotation(this.screenPlacePoint, orientationScreenPoint);

      // The orientation is reversed if the orientation point falls to the right of the screen
      // point. Text is
      // never drawn upside down, so when the orientation is reversed the text flips vertically to
      // keep the text
      // right side up.
      orientationReversed = (orientationScreenPoint.x <= this.screenPlacePoint.x);
    }

    this.computeBoundsIfNeeded(dc);

    Offset offset = this.getOffset();
    Point2D offsetPoint =
        offset.computeOffset(this.bounds.getWidth(), this.bounds.getHeight(), null, null);

    // If a rotation is applied to the text, then rotate the offset as well. An offset in the x
    // direction
    // will move the text along the orientation line, and a offset in the y direction will move the
    // text
    // perpendicular to the orientation line.
    if (this.rotation != null) {
      double dy = offsetPoint.getY();

      // If the orientation is reversed we need to adjust the vertical offset to compensate for the
      // flipped
      // text. For example, if the offset normally aligns the top of the text with the place point
      // then without
      // this adjustment the bottom of the text would align with the place point when the
      // orientation is
      // reversed.
      if (orientationReversed) {
        dy = -(dy + this.bounds.getHeight());
      }

      Vec4 pOffset = new Vec4(offsetPoint.getX(), dy);
      Matrix rot = Matrix.fromRotationZ(this.rotation.multiply(-1));

      pOffset = pOffset.transformBy3(rot);

      offsetPoint = new Point((int) pOffset.getX(), (int) pOffset.getY());
    }

    int x = (int) (this.screenPlacePoint.x + offsetPoint.getX());
    int y = (int) (this.screenPlacePoint.y - offsetPoint.getY());

    this.screenPoint = new Point(x, y);
    this.screenExtent = this.computeTextExtent(x, y, this.rotation);
  }

  /**
   * Determine if this label intersects the view or pick frustum.
   *
   * @param dc Current draw context.
   * @return True if this label intersects the active frustum (view or pick). Otherwise false.
   */
  protected boolean intersectsFrustum(DrawContext dc) {
    View view = dc.getView();
    Frustum frustum = view.getFrustumInModelCoordinates();

    // Test the label's model coordinate point against the near and far clipping planes.
    if (this.placePoint != null
        && (frustum.getNear().distanceTo(this.placePoint) < 0
            || frustum.getFar().distanceTo(this.placePoint) < 0)) {
      return false;
    }

    if (dc.isPickingMode()) return dc.getPickFrustums().intersectsAny(this.screenExtent);
    else return view.getViewport().intersects(this.screenExtent);
  }

  /**
   * Compute the amount of rotation to apply to a label in order to keep it oriented toward its
   * orientation position.
   *
   * @param screenPoint Geographic position of the text, projected onto the screen.
   * @param orientationScreenPoint Orientation position, projected onto the screen.
   * @return The rotation angle to apply when drawing the label.
   */
  protected Angle computeRotation(Vec4 screenPoint, Vec4 orientationScreenPoint) {
    // Determine delta between the orientation position and the label position
    double deltaX = screenPoint.x - orientationScreenPoint.x;
    double deltaY = screenPoint.y - orientationScreenPoint.y;

    if (deltaX != 0) {
      double angle = Math.atan(deltaY / deltaX);
      return Angle.fromRadians(angle);
    } else {
      return Angle.POS90; // Vertical label
    }
  }

  /** {@inheritDoc} */
  public double getDistanceFromEye() {
    return this.eyeDistance;
  }

  /** {@inheritDoc} */
  public void render(DrawContext dc) {
    // This render method is called three times during frame generation. It's first called as a
    // Renderable
    // during Renderable picking. It's called again during normal rendering. And it's called a third
    // time as an OrderedRenderable. The first two calls determine whether to add the label the
    // ordered renderable
    // list during pick and render. The third call just draws the ordered renderable.

    if (dc == null) {
      String msg = Logging.getMessage("nullValue.DrawContextIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    if (dc.isOrderedRenderingMode()) this.drawOrderedRenderable(dc);
    else this.makeOrderedRenderable(dc);
  }

  /** {@inheritDoc} */
  public void pick(DrawContext dc, Point pickPoint) {
    // This method is called only when ordered renderables are being drawn.
    // Arg checked within call to render.

    if (dc == null) {
      String msg = Logging.getMessage("nullValue.DrawContextIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    this.pickSupport.clearPickList();
    try {
      this.pickSupport.beginPicking(dc);
      this.render(dc);
    } finally {
      this.pickSupport.endPicking(dc);
      this.pickSupport.resolvePick(dc, pickPoint, this.pickLayer);
    }
  }

  /**
   * Draws the graphic as an ordered renderable.
   *
   * @param dc the current draw context.
   */
  protected void makeOrderedRenderable(DrawContext dc) {
    if (this.lines == null || this.position == null) return;

    this.computeGeometryIfNeeded(dc);

    // Don't draw if beyond the horizon.
    double horizon = dc.getView().getHorizonDistance();
    if (this.eyeDistance > horizon) return;

    if (this.intersectsFrustum(dc)) dc.addOrderedRenderable(this);

    if (dc.isPickingMode()) this.pickLayer = dc.getCurrentLayer();
  }

  /**
   * Draws the graphic as an ordered renderable.
   *
   * @param dc the current draw context.
   */
  protected void drawOrderedRenderable(DrawContext dc) {
    this.beginDrawing(dc);
    try {
      this.doDrawOrderedRenderable(dc, this.pickSupport);

      if (this.isEnableBatchRendering()) this.drawBatched(dc);
    } finally {
      this.endDrawing(dc);
    }
  }

  /**
   * Draw this label during ordered rendering.
   *
   * @param dc Current draw context.
   * @param pickSupport Support object used during picking.
   */
  protected void doDrawOrderedRenderable(DrawContext dc, PickSupport pickSupport) {
    TextRenderer textRenderer =
        OGLTextRenderer.getOrCreateTextRenderer(dc.getTextRendererCache(), font);
    if (dc.isPickingMode()) {
      this.doPick(dc, pickSupport);
    } else {
      this.drawText(dc, textRenderer);
    }
  }

  /**
   * Establish the OpenGL state needed to draw text.
   *
   * @param dc the current draw context.
   */
  protected void beginDrawing(DrawContext dc) {
    GL gl = dc.getGL();

    int attrMask =
        GL.GL_DEPTH_BUFFER_BIT // for depth test, depth mask and depth func
            | GL.GL_TRANSFORM_BIT // for modelview and perspective
            | GL.GL_VIEWPORT_BIT // for depth range
            | GL.GL_CURRENT_BIT // for current color
            | GL.GL_COLOR_BUFFER_BIT // for alpha test func and ref, and blend
            | GL.GL_DEPTH_BUFFER_BIT // for depth func
            | GL.GL_ENABLE_BIT; // for enable/disable changes

    this.BEogsh.pushAttrib(gl, attrMask);

    if (!dc.isPickingMode()) {
      gl.glEnable(GL.GL_BLEND);
      OGLUtil.applyBlending(gl, false);
    }

    // Do not depth buffer the label. (Labels beyond the horizon are culled above.)
    gl.glDisable(GL.GL_DEPTH_TEST);
    gl.glDepthMask(false);

    // The image is drawn using a parallel projection.
    this.BEogsh.pushProjectionIdentity(gl);
    gl.glOrtho(
        0d, dc.getView().getViewport().width, 0d, dc.getView().getViewport().height, -1d, 1d);

    this.BEogsh.pushModelviewIdentity(gl);
  }

  /**
   * Pop the state set in beginDrawing.
   *
   * @param dc the current draw context.
   */
  protected void endDrawing(DrawContext dc) {
    this.BEogsh.pop(dc.getGL());
  }

  /**
   * Draw labels for picking.
   *
   * @param dc Current draw context.
   * @param pickSupport the PickSupport instance to be used.
   */
  protected void doPick(DrawContext dc, PickSupport pickSupport) {
    GL gl = dc.getGL();

    Angle heading = this.rotation;

    double headingDegrees;
    if (heading != null) headingDegrees = heading.degrees;
    else headingDegrees = 0;

    int x = this.screenPoint.x;
    int y = this.screenPoint.y;

    boolean matrixPushed = false;
    try {
      if (headingDegrees != 0) {
        gl.glPushMatrix();
        matrixPushed = true;

        gl.glTranslated(x, y, 0);
        gl.glRotated(headingDegrees, 0, 0, 1);
        gl.glTranslated(-x, -y, 0);
      }

      for (int i = 0; i < this.lines.length; i++) {
        Rectangle2D bounds = this.lineBounds[i];
        double width = bounds.getWidth();
        double height = bounds.getHeight();

        x = this.screenPoint.x;
        if (this.textAlign.equals(AVKey.CENTER)) x = x - (int) (width / 2.0);
        else if (this.textAlign.equals(AVKey.RIGHT)) x = x - (int) width;
        y -= this.lineHeight;

        Color color = dc.getUniquePickColor();
        int colorCode = color.getRGB();
        PickedObject po = new PickedObject(colorCode, this.getPickedObject(), this.position, false);
        pickSupport.addPickableObject(po);

        // Draw line rectangle
        gl.glColor3ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue());

        try {
          gl.glBegin(GL.GL_POLYGON);
          gl.glVertex3d(x, y, 0);
          gl.glVertex3d(x + width - 1, y, 0);
          gl.glVertex3d(x + width - 1, y + height - 1, 0);
          gl.glVertex3d(x, y + height - 1, 0);
          gl.glVertex3d(x, y, 0);
        } finally {
          gl.glEnd();
        }

        y -= this.lineSpacing;
      }
    } finally {
      if (matrixPushed) {
        gl.glPopMatrix();
      }
    }
  }

  /**
   * Draw the label's text. This method sets up the text renderer, and then calls {@link
   * #doDrawText(TextRenderer) doDrawText} to actually draw the text.
   *
   * @param dc Current draw context.
   * @param textRenderer Text renderer.
   */
  protected void drawText(DrawContext dc, TextRenderer textRenderer) {
    GL gl = dc.getGL();

    Angle heading = this.rotation;

    double headingDegrees;
    if (heading != null) headingDegrees = heading.degrees;
    else headingDegrees = 0;

    boolean matrixPushed = false;
    try {
      int x = this.screenPoint.x;
      int y = this.screenPoint.y;

      if (headingDegrees != 0) {
        gl.glPushMatrix();
        matrixPushed = true;

        gl.glTranslated(x, y, 0);
        gl.glRotated(headingDegrees, 0, 0, 1);
        gl.glTranslated(-x, -y, 0);
      }

      if (this.isDrawInterior()) this.drawInterior(dc);

      textRenderer.begin3DRendering();
      try {
        this.doDrawText(textRenderer);

        // Draw other labels that share the same text renderer configuration, if possible.
        if (this.isEnableBatchRendering()) this.drawBatchedText(dc, textRenderer);
      } finally {
        textRenderer.end3DRendering();
      }
    } finally {
      if (matrixPushed) {
        gl.glPopMatrix();
      }
    }
  }

  /**
   * Render the label interior as a filled rectangle.
   *
   * @param dc Current draw context.
   */
  protected void drawInterior(DrawContext dc) {
    GL gl = dc.getGL();

    double width = this.bounds.getWidth();
    double height = this.bounds.getHeight();

    int x = this.screenPoint.x;
    int y = this.screenPoint.y;

    // Adjust x to account for text alignment
    int xAligned = x;
    if (AVKey.CENTER.equals(textAlign)) xAligned = x - (int) (width / 2);
    else if (AVKey.RIGHT.equals(textAlign)) xAligned = x - (int) width;

    // We draw text top-down, so adjust y to compensate.
    int yAligned = (int) (y - height);

    // Apply insets
    Insets insets = this.getInsets();
    xAligned -= insets.left;
    width = width + insets.left + insets.right;
    yAligned -= insets.bottom;
    height = height + insets.bottom + insets.top;

    if (!dc.isPickingMode()) {
      // Apply the frame background color and opacity if we're in normal rendering mode.
      Color color = this.computeBackgroundColor(this.getMaterial().getDiffuse());
      gl.glColor4ub(
          (byte) color.getRed(),
          (byte) color.getGreen(),
          (byte) color.getBlue(),
          (byte) (this.interiorOpacity < 1 ? (int) (this.interiorOpacity * 255 + 0.5) : 255));
    }

    try {
      // Draw a quad
      gl.glPushMatrix();
      gl.glTranslated(xAligned, yAligned, 0);
      gl.glScaled(width, height, 1.0);
      dc.drawUnitQuad();
    } finally {
      gl.glPopMatrix();
    }
  }

  /**
   * Draw the label's text. This method assumes that the text renderer context has already been set
   * up.
   *
   * @param textRenderer renderer to use.
   */
  protected void doDrawText(TextRenderer textRenderer) {
    Color color = this.material.getDiffuse();
    Color backgroundColor = this.computeBackgroundColor(color);
    float opacity = (float) this.getOpacity();

    int x = this.screenPoint.x;
    int y = this.screenPoint.y;

    float[] compArray = new float[3];
    if (AVKey.TEXT_EFFECT_SHADOW.equals(this.effect) && backgroundColor != null) {
      backgroundColor.getRGBColorComponents(compArray);

      textRenderer.setColor(compArray[0], compArray[1], compArray[2], opacity);
      this.drawMultiLineText(textRenderer, x + 1, y - 1);
    }

    color.getRGBColorComponents(compArray);
    textRenderer.setColor(compArray[0], compArray[1], compArray[2], opacity);
    this.drawMultiLineText(textRenderer, x, y);
  }

  protected void drawMultiLineText(TextRenderer textRenderer, int x, int y) {
    if (this.lines == null) {
      String msg = Logging.getMessage("nullValue.StringIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    for (int i = 0; i < this.lines.length; i++) {
      String line = this.lines[i];
      Rectangle2D bounds = this.lineBounds[i];

      int xAligned = x;
      if (this.textAlign.equals(AVKey.CENTER)) xAligned = x - (int) (bounds.getWidth() / 2);
      else if (this.textAlign.equals(AVKey.RIGHT)) xAligned = x - (int) (bounds.getWidth());

      y -= this.lineHeight;
      textRenderer.draw3D(line, xAligned, y, 0, 1);
      y -= this.lineSpacing;
    }
  }

  /**
   * Draws this ordered renderable and all subsequent Label ordered renderables in the ordered
   * renderable list. This method differs from {@link
   * #drawBatchedText(gov.nasa.worldwind.render.DrawContext, TextRenderer) drawBatchedText} in that
   * this method re-initializes the text renderer to draw the next label, while {@code
   * drawBatchedText} re-uses the active text renderer context. That is, {@code drawBatchedText}
   * attempts to draw as many labels as possible that share same text renderer configuration as this
   * label, and this method attempts to draw as many labels as possible regardless of the text
   * renderer configuration of the subsequent labels.
   *
   * @param dc the current draw context.
   */
  protected void drawBatched(DrawContext dc) {
    // Draw as many as we can in a batch to save ogl state switching.
    Object nextItem = dc.peekOrderedRenderables();

    if (!dc.isPickingMode()) {
      while (nextItem != null && nextItem instanceof TacticalGraphicLabel) {
        TacticalGraphicLabel nextLabel = (TacticalGraphicLabel) nextItem;
        if (!nextLabel.isEnableBatchRendering()) break;

        dc.pollOrderedRenderables(); // take it off the queue
        nextLabel.doDrawOrderedRenderable(dc, this.pickSupport);

        nextItem = dc.peekOrderedRenderables();
      }
    } else if (this.isEnableBatchPicking()) {
      while (nextItem != null && nextItem instanceof TacticalGraphicLabel) {
        TacticalGraphicLabel nextLabel = (TacticalGraphicLabel) nextItem;
        if (!nextLabel.isEnableBatchRendering() || !nextLabel.isEnableBatchPicking()) break;

        if (nextLabel.pickLayer != this.pickLayer) // batch pick only within a single layer
        break;

        dc.pollOrderedRenderables(); // take it off the queue
        nextLabel.doDrawOrderedRenderable(dc, this.pickSupport);

        nextItem = dc.peekOrderedRenderables();
      }
    }
  }

  /**
   * Draws text for subsequent Label ordered renderables in the ordered renderable list. This method
   * is called after the text renderer has been set up (after beginRendering has been called), so
   * this method can only draw text for subsequent labels that use the same font and rotation as
   * this label. This method differs from {@link #drawBatched(gov.nasa.worldwind.render.DrawContext)
   * drawBatched} in that this method reuses the active text renderer context to draw as many labels
   * as possible without switching text renderer state.
   *
   * @param dc the current draw context.
   * @param textRenderer Text renderer used to draw the label.
   */
  protected void drawBatchedText(DrawContext dc, TextRenderer textRenderer) {
    // Draw as many as we can in a batch to save ogl state switching.
    Object nextItem = dc.peekOrderedRenderables();

    if (!dc.isPickingMode()) {
      while (nextItem != null && nextItem instanceof TacticalGraphicLabel) {
        TacticalGraphicLabel nextLabel = (TacticalGraphicLabel) nextItem;
        if (!nextLabel.isEnableBatchRendering()) break;

        boolean sameFont = this.font.equals(nextLabel.getFont());
        boolean sameRotation =
            (this.rotation == null && nextLabel.rotation == null)
                || (this.rotation != null && this.rotation.equals(nextLabel.rotation));
        boolean drawInterior = nextLabel.isDrawInterior();

        // We've already set up the text renderer state, so we can can't change the font or text
        // rotation.
        // Also can't batch render if the next label needs an interior since that will require
        // tearing down the
        // text renderer context.
        if (!sameFont || !sameRotation || drawInterior) break;

        dc.pollOrderedRenderables(); // take it off the queue
        nextLabel.doDrawText(textRenderer);

        nextItem = dc.peekOrderedRenderables();
      }
    }
  }

  /**
   * Indicates the object that represents this label during picking.
   *
   * @return If a delegate owner is set, returns the delegate owner. Otherwise returns this label.
   */
  protected Object getPickedObject() {
    Object owner = this.getDelegateOwner();
    return (owner != null) ? owner : this;
  }

  /**
   * Determine the screen rectangle covered by a label. The input coordinate identifies either the
   * top left, top center, or top right corner of the label, depending on the text alignment. If the
   * label is rotated to align with features on the surface then the extent will be the smallest
   * screen rectangle that completely encloses the rotated label.
   *
   * @param x X coordinate at which to draw the label.
   * @param y Y coordinate at which to draw the label.
   * @param rotation Label rotation.
   * @return The rectangle, in OGL screen coordinates (origin at bottom left corner), that is
   *     covered by the label.
   */
  protected Rectangle computeTextExtent(int x, int y, Angle rotation) {
    double width = this.bounds.getWidth();
    double height = this.bounds.getHeight();

    String textAlign = this.getTextAlign();

    int xAligned = x;
    if (AVKey.CENTER.equals(textAlign)) xAligned = x - (int) (width / 2);
    else if (AVKey.RIGHT.equals(textAlign)) xAligned = x - (int) width;

    int yAligned = (int) (y - height);

    Rectangle screenRect = new Rectangle(xAligned, yAligned, (int) width, (int) height);

    // Compute bounds of the rotated rectangle, if there is a rotation angle.
    if (rotation != null && rotation.degrees != 0) {
      screenRect = this.computeRotatedScreenExtent(screenRect, x, y, rotation);
    }

    return screenRect;
  }

  /**
   * Compute the bounding screen extent of a rotated rectangle.
   *
   * @param rect Rectangle to rotate.
   * @param x X coordinate of the rotation point.
   * @param y Y coordinate of the rotation point.
   * @param rotation Rotation angle.
   * @return The smallest rectangle that completely contains {@code rect} when rotated by the
   *     specified angle.
   */
  protected Rectangle computeRotatedScreenExtent(Rectangle rect, int x, int y, Angle rotation) {
    Rectangle r = new Rectangle(rect);

    // Translate the rectangle to the rotation point.
    r.translate(-x, -y);

    // Compute corner points
    Vec4[] corners = {
      new Vec4(r.getMaxX(), r.getMaxY()),
      new Vec4(r.getMaxX(), r.getMinY()),
      new Vec4(r.getMinX(), r.getMaxY()),
      new Vec4(r.getMinX(), r.getMinY())
    };

    // Rotate the rectangle
    Matrix rotationMatrix = Matrix.fromRotationZ(rotation);
    for (int i = 0; i < corners.length; i++) {
      corners[i] = corners[i].transformBy3(rotationMatrix);
    }

    // Find the bounding rectangle of rotated points.
    int minX = Integer.MAX_VALUE;
    int minY = Integer.MAX_VALUE;
    int maxX = -Integer.MAX_VALUE;
    int maxY = -Integer.MAX_VALUE;

    for (Vec4 v : corners) {
      if (v.x > maxX) maxX = (int) v.x;

      if (v.x < minX) minX = (int) v.x;

      if (v.y > maxY) maxY = (int) v.y;

      if (v.y < minY) minY = (int) v.y;
    }

    // Set bounds and translate the rectangle back to where it started.
    r.setBounds(minX, minY, maxX - minX, maxY - minY);
    r.translate(x, y);

    return r;
  }

  /**
   * Compute a contrasting background color to draw the label's outline.
   *
   * @param color Label color.
   * @return A color that contrasts with {@code color}.
   */
  protected Color computeBackgroundColor(Color color) {
    float[] colorArray = new float[4];
    Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), colorArray);

    if (colorArray[2] > 0.5) return new Color(0, 0, 0, 0.7f);
    else return new Color(1, 1, 1, 0.7f);
  }
}
Esempio n. 23
0
 /** Default constructor Makes an INFont with size of 12 Plain style and with the Arial Font */
 public INFont() {
   this.size = 12;
   this.style = FontStyle.PLAIN;
   this.loadedFont = Font.decode("Arial-12");
 }