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); }
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); }
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; }
/* * (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); } }
/** * 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; }
/** * 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)); } }
/** * 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; } }
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(); } }
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"); }
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); }
/** * 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); } }*/ }
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); } }
/** * 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; }
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); }
/** * 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); }
/** * 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"); } }
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"); }
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); } }
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; }
/** * 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); } }
/** 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"); }