// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/omGraphics/OMGraphic.java,v $
// $RCSfile: OMGraphic.java,v $
// $Revision: 1.16 $
// $Date: 2009/01/21 01:24:41 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.omGraphics;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.TexturePaint;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import com.bbn.openmap.omGraphics.geom.BasicGeometry;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;
/**
* Used to be the base class of OpenMap graphics, but now inherits from
* BasicGeometry, which now contains all the information about the geometry of
* the OMGraphic. The OMGraphic also contains information about how the geometry
* should be drawn.
* <P>
* The OMGraphics are raster and vector graphic objects that know how to
* position and render themselves on a given x-y window or lat-lon map
* projection. All you have to do is supply the location data (x/y, lat/lon) and
* drawing information (color, line width) and the graphic handles the rest.
* <p>
*
* This class contains parameters that are common to most types of graphics. If
* a parameter doesn't make sense for a particular graphic type, it is ignored.
* <p>
*
* The OMGraphics are being updated to be able to provide java.awt.Shape
* representations of themselves after they have been generated(). The
* getShape() method returns a java.awt.Shape object. With the Shape object, you
* can do some spatial analysis (object operations) on the projected OMGraphics.
*
* NOTES:
* <ul>
* <li>Color values cannot be set to null, but can be set to OMGraphic.clear.
* Actually, if you set them to null, they will set themselves to be clear.
* <li>XY Rendering: Java specifies that the origin is the top left of the
* window, x increases to the right, y increases down.
* <li>LatLon Rendering: Defined by the Projection object. The center of the
* window usually corresponds to the center of the projection. OMGraphics should
* project themselves using the appropriate forward() method listed in the
* Projection interface
* <li>Offset Rendering: same as XY, but with origin set to a projected LatLon
* point.
* </ul>
*
* @see OMBitmap
* @see OMCircle
* @see OMLine
* @see OMPoly
* @see OMRect
* @see OMRaster
* @see OMText
* @see OMGraphicList
* @see Projection
*/
public abstract class OMGraphicAdapter extends BasicGeometry implements OMGraphic,
OMGraphicConstants, Cloneable, Serializable {
/**
* The Java2D Stroke. This is used for lineWidth, and dashing of the lines
* and polygon edges.
*/
protected transient Stroke stroke = BASIC_STROKE;
/**
* This color is the real foreground color of the object. It is kept so that
* the object knows how to de-highlight itself. It defaults to black.
*/
protected Paint linePaint = Color.black;
/**
* This paint is used for the matting area around the edge of an OMGraphic
* painted when the matted variable is set to true.
*/
protected Paint mattingPaint = Color.black;
/**
* The color that the object is displayed with. This color changes back and
* forth between the selectColor and the lineColor, depending on the if the
* object is selected or not.
*/
protected Paint displayPaint = linePaint;
/**
* This color is the fill color of the object. It defaults to a black color
* that is transparent.
*/
protected Paint fillPaint = clear;
/**
* This Paint object is the fill texture mask of the object. It defaults to
* null. If this texture mask is set, the fill paint will still be used to
* fill the OMGraphic shape, but then this paint will be rendered on top. If
* the textureMask has transparency, the fill paint still influences
* appearance.
*/
protected transient TexturePaint textureMask = null;
/**
* This color is the highlight color that can be used when the object is
* selected. The default color is black, just like the line color.
*/
protected Paint selectPaint = Color.black;
/**
* Flag to indicate that the object has/hasnot been put in a special mode as
* a result of some event. Set through the select()/deselect methods().
*/
protected boolean selected = false;
/**
* A flag for whether an EditableOMGraphic should show it's palette if the
* OMGraphic is modified.
*/
protected boolean showEditablePalette = true;
/**
* Flag to note if the current edge color matches the fill color. Can be
* used to save from rendering the edge if rendering the filled area already
* takes care of it.
*/
protected boolean edgeMatchesFill = false;
/**
* The renderType describes the relation of the object to the window.
* RENDERTYPE_LATLON means the object is positioned relative to lat/lon
* points. RENDERTYPE_XY means the object is positioned relative to window
* pixel coordinates. RENDERTYPE_OFFSET means the object is drawn at a pixel
* offset to a lat/lon point.
*/
protected int renderType = RENDERTYPE_UNKNOWN;
/**
* Decluttering is not supported by OpenMap yet. But, when it is, these
* parameters will describe the way the object is manipulated on the window
* relative to its neighbors. DECLUTTERTYPE_NONE means the object will be
* drawn where its attributes say it should be. DECLUTTERTYPE_SPACE
* indicates that the window space of the object should be marked as taken,
* but the object should not be moved. DECLUTTERTYPE_MOVE means the object
* should be moved to the nearest open location closest to the position
* indicated by its attributes. DECLUTTERTYPE_LINE is the same as MOVE, but
* in addition, a line is drawn from the current position to its original
* position.
*/
protected int declutterType = DECLUTTERTYPE_NONE;
/**
* Flag for determining when the matting around the edge of an OMGraphic.
* Matting is a line, two pixels wider than the edge, painted under the
* edge. It makes the OMGraphic stand out on busy backgrounds.
*/
protected boolean matted = false;
/**
* The flag set in generate that causes the OMGraphic to look for an
* OMLabeler attribute in render. This flag prevents an unnecessary
* hashtable lookup every render call.
*/
protected transient boolean hasLabel = false;
/**
* Checks if the Paint is clear.
*
* @param paint Paint or null.
* @return true if Paint is null or is a Color with a 0 alpha value.
*/
public boolean isClear(Paint paint) {
return DrawingAttributes.isClear(paint);
// if (paint instanceof Color) {
// return ((((Color) paint).getRGB() & 0xff000000) == 0);
// } else {
// return false;
// }
}
// ////////////////////////////////////////////////////////
/**
* Construct a default OMGraphic.
*/
protected OMGraphicAdapter() {
}
/**
* Construct an OMGraphic. Standard simple constructor that the child
* OMGraphics usually call. All of the other parameters get set to their
* default values.
*
* @param rType render type
* @param lType line type
* @param dcType declutter type
*/
protected OMGraphicAdapter(int rType, int lType, int dcType) {
setRenderType(rType);
setLineType(lType);
setDeclutterType(dcType);
}
/**
* Construct an OMGraphic. More complex constructor that lets you set the
* rest of the parameters.
*
* @param rType render type
* @param lType line type
* @param dcType declutter type
* @param lc line color
* @param fc fill color
* @param sc select color
*/
public OMGraphicAdapter(int rType, int lType, int dcType, Color lc, Color fc, Color sc) {
this(rType, lType, dcType);
setLinePaint(lc);
setSelectPaint(sc);
setFillPaint(fc);
}
/**
* Set the render type of the graphic. Accepts RENDERTYPE_LATLON,
* RENDERTYPE_XY and RENDERTYPE_OFFSET. All weird values get set to
* RENDERTYPE_XY. See the definition on the renderType parameter.
*
* @param value the rendertype for the object.
*/
public void setRenderType(int value) {
if (renderType == value)
return;
setNeedToRegenerate(true); // flag dirty
renderType = value;
}
/**
* Return the render type.
*
* @return the rendertype of the object - RENDERTYPE_LATLON, RENDERTYPE_XY,
* RENDERTYPE_OFFSET and RENDERTYPE_UNKNOWN.
*/
public int getRenderType() {
return renderType;
}
/**
* Set the declutter setting for the graphic. Accepts DECLUTTERTYPE_SPACE,
* DECLUTTERTYPE_MOVE, DECLUTTERTYPE_LINE, and DECLUTTERTYPE_NONE. All weird
* values are set to DECLUTTERTYPE_NONE.
* <p>
* Right now, this is unimplemented in OpenMap. But for information,
* DECLUTTERTYPE_NONE means the object has no impact on the placement of
* objects. DECLUTTERTYPE_SPACE means the object shouldn't have things
* placed on it, but to draw it where the coordinates dictate.
* DECLUTTERTYPE_MOVE means to put the object in an open space, and
* DELCUTTERTYPE_LINE adds the feature that if the object is not drawn where
* it's coordinates say it should be, then a line should be drawn showing
* where the original position is.
* <P>
* Decluttering of geometries is not supported. This flag is not used.
*
* @param value the declutter type value.
*/
public void setDeclutterType(int value) {
if (declutterType == value)
return;
setNeedToRegenerate(true); // flag dirty
declutterType = value;
}
/**
* Return the declutter type.
*
* @return declutter type, see above.
*/
public int getDeclutterType() {
return declutterType;
}
/**
* Given a java.awt.Graphics object, set the Stroke and Paint parameters of
* it to match the OMGraphic's edge settings.
*
* @param g java.awt.Graphics
* @see #setGraphicsColor
*/
public void setGraphicsForEdge(Graphics g) {
if (g instanceof Graphics2D) {
((Graphics2D) g).setStroke(getStroke());
}
setGraphicsColor(g, getDisplayPaint());
}
/**
* Given a java.awt.Graphics object, set the Paint to be the OMGraphic's
* fillPaint setting.
*
* @param g java.awt.Graphics
* @see #setGraphicsColor
*/
public void setGraphicsForFill(Graphics g) {
if (g instanceof Graphics2D) {
((Graphics2D) g).setStroke(BASIC_STROKE);
}
setGraphicsColor(g, getFillPaint());
}
/**
* Set the Paint in the given Graphics. If the Graphics is not an instance
* of Graphics2D, then the Color of the graphics is set if the Paint is an
* instance of Color.
*
* @param g java.awt.Graphics
* @param paint java.awt.Paint
*/
public void setGraphicsColor(Graphics g, Paint paint) {
if (g instanceof Graphics2D) {
((Graphics2D) g).setPaint(paint);
} else if (paint instanceof Color) {
g.setColor((Color) paint);
}
}
/**
* Set the line color of the graphic object. The line color is the normal
* display edge color of the object. This color is used as the display color
* when the object is NOT selected (highlighted). The display color is set
* to the select color in this method if <code>selected</code> boolean
* attribute is false.
*
* @param value the real line color
* @deprecated Use setLinePaint instead. Now taking advantage of the Java2D
* API.
*/
public void setLineColor(Color value) {
setLinePaint(value);
}
/**
* Return the normal foreground color of the object.
*
* @return the line color. Returns null if the Paint is not a Color.
*/
public Color getLineColor() {
if (linePaint instanceof Color) {
return (Color) linePaint;
} else {
return null;
}
}
/**
* Set the line Paint. The line Paint is the normal display edge paint of
* the graphic. This Paint is used as the display Paint when the object is
* NOT selected (highlighted). The display Paint is set to the select Paint
* in this method if <code>selected</code> boolean attribute is false.
*
* @param paint the real line Paint
*/
public void setLinePaint(Paint paint) {
if (paint != null) {
linePaint = paint;
} else {
linePaint = Color.black;
}
if (!selected) {
displayPaint = linePaint;
}
setEdgeMatchesFill();
}
/**
* Get the normal line Paint used for the graphic.
*
* @return Line Paint.
*/
public Paint getLinePaint() {
return linePaint;
}
/**
* Set the select color of the graphic object. The selected color is used as
* the display color when the object is selected (highlighted). The display
* color is set to the select color in this method if <code>selected</code>
* boolean attribute is true.
*
* @param value the selected color.
* @deprecated Use setSelectPaint instead. Now taking advantage of the
* Java2D API.
*/
public void setSelectColor(Color value) {
setSelectPaint(value);
}
/**
* Return the selected color, which is the line or foreground color used
* when the graphic is "selected".
*
* @return the selected mode line color. Returns null if the select Paint is
* not a Color.
*/
public Color getSelectColor() {
if (selectPaint instanceof Color) {
return (Color) selectPaint;
} else {
return null;
}
}
/**
* Set the select Paint. The select Paint is the display edge paint of the
* graphic. This Paint is used as the display Paint when the object IS
* selected (highlighted). The display Paint is set to the select Paint in
* this method if <code>selected</code> boolean attribute is true.
*
* @param paint the real select Paint
*/
public void setSelectPaint(Paint paint) {
if (paint != null) {
selectPaint = paint;
} else {
selectPaint = Color.black;
}
if (selected) {
displayPaint = selectPaint;
}
setEdgeMatchesFill();
}
/**
* Get the normal select Paint used for the graphic.
*
* @return Select Paint.
*/
public Paint getSelectPaint() {
return selectPaint;
}
/**
* Return the color that should be used for display. This color changes,
* depending on whether the object is selected or not. The display color is
* also set when the line color or the select color is set, depending on the
* statue of the <code>selected</code> boolean attribute.
*
* @return the color used as the edge color or foreground color, in the
* present selected state. If the displayPaint is not a Color, this
* method returns null.
*/
public Color getDisplayColor() {
if (displayPaint instanceof Color) {
return (Color) displayPaint;
} else {
return null;
}
}
/**
* Return the Paint that should be used for display. This Paint changes,
* depending on whether the object is selected or not. The display Paint is
* also set when the line Paint or the select Paint is set, depending on the
* statue of the <code>selected</code> boolean attribute.
*
* @return the Paint used as the edge Paint or foreground Paint, in the
* present selected state.
*/
public Paint getDisplayPaint() {
return displayPaint;
}
/**
* Set the selected attribute to true, and sets the color to the select
* color.
*/
public void select() {
selected = true;
displayPaint = getSelectPaint();
setEdgeMatchesFill();
}
/**
* Set the selected attribute to false, sets the color to the line color.
*/
public void deselect() {
selected = false;
displayPaint = getLinePaint();
setEdgeMatchesFill();
}
/**
* Return whether the OMGraphic is selected.
*/
public boolean isSelected() {
return selected;
}
/**
* Calls select() or deselect() depending on the boolean (select is true).
*/
public void setSelected(boolean set) {
if (set) {
select();
} else {
deselect();
}
}
/**
* Return whether the OMGraphic has matting around the edge.
*/
public boolean isMatted() {
return matted;
}
/**
* Set whether the OMGraphic should have matting around the edge.
*/
public void setMatted(boolean set) {
matted = set;
}
/**
* Set the background color of the graphic object.
*
* @param value java.awt.Color.
* @deprecated Use setFillPaint instead. Now taking advantage of the Java2D
* API.
*/
public void setFillColor(Color value) {
setFillPaint(value);
}
/**
* Return the background color of the graphic object. If the fill Paint is
* not a color, this method will return null.
*
* @return the color used for the background.
*/
public Color getFillColor() {
if (fillPaint instanceof Color) {
return (Color) fillPaint;
} else {
return null;
}
}
/**
* Set the fill Paint for this graphic. If the paint value is null, it will
* be set to OMGraphicConstants.clear.
*
* @param paint the Paint object.
*/
public void setFillPaint(Paint paint) {
if (paint != null) {
fillPaint = paint;
if (Debug.debugging("omGraphics")) {
Debug.output("OMGraphic.setFillPaint(): fillPaint= " + fillPaint);
}
} else {
fillPaint = clear;
if (Debug.debugging("omGraphics")) {
Debug.output("OMGraphic.setFillPaint(): fillPaint is clear");
}
}
setEdgeMatchesFill();
}
/**
* Set the texture mask for the OMGraphic. If not null, then it will be
* rendered on top of the fill paint. If the fill paint is clear, the
* texture mask will not be used. If you just want to render the texture
* mask as is, set the fill paint of the graphic instead. This is really to
* be used to have a texture added to the graphic, with the fill paint still
* influencing appearance.
*/
public void setTextureMask(TexturePaint texture) {
textureMask = texture;
}
/**
* Return the fill Paint for this graphic.
*/
public Paint getFillPaint() {
return fillPaint;
}
/**
* Return the texture mask Paint for this graphic.
*/
public TexturePaint getTextureMask() {
return textureMask;
}
protected void setEdgeMatchesFill() {
Paint paint = getDisplayPaint();
if (fillPaint instanceof Color && paint instanceof Color && !isClear(fillPaint)) {
edgeMatchesFill = ((Color) fillPaint).equals((Color) paint);
} else {
edgeMatchesFill = false;
}
}
public boolean getEdgeMatchesFill() {
return edgeMatchesFill;
}
/**
* Set the Paint used for matting.
*/
public void setMattingPaint(Paint mPaint) {
mattingPaint = mPaint;
}
/**
* Get the Paint used for matting.
*/
public Paint getMattingPaint() {
return mattingPaint;
}
/**
* Set the Stroke that should be used for the graphic edges. Using a
* BasicStroke, you can set a stroke that defines the line width, the dash
* interval and phase. If a null value is passed in, a default BasicStroke
* will be used.
*
* @param s the stroke to use for the graphic edge.
* @see java.awt.Stroke
* @see java.awt.BasicStroke
*/
public void setStroke(Stroke s) {
if (s != null) {
stroke = s;
} else {
stroke = BASIC_STROKE;
}
}
/**
* Get the Stroke used for the graphic edge.
*/
public Stroke getStroke() {
if (stroke == null) {
stroke = BASIC_STROKE;
}
return stroke;
}
/**
* Set whether an EditableOMGraphic modifying this graphic should show it's
* palette.
*/
public void setShowEditablePalette(boolean set) {
showEditablePalette = set;
}
/**
* Get whether an EditableOMGraphic modifying this graphic should show it's
* palette.
*/
public boolean getShowEditablePalette() {
return showEditablePalette;
}
/**
* A function that takes a float distance, which presumably represents the
* pixel distance from a point to a graphic, and subtracts half of the line
* width of the graphic from the distance if the graphic line width is
* greater than one. This should give a true pixel distance from the
* graphic, taking into account an embellished line.
*
* @param distance pixel distance to the graphic edge with a line width of
* one.
* @return the pixel distance to the true display edge of the graphic.
*/
public float normalizeDistanceForLineWidth(float distance) {
float lineWidth = 1;
if (stroke instanceof BasicStroke) {
lineWidth = ((BasicStroke) stroke).getLineWidth();
}
if (lineWidth > 1) {
// extra calculation for lineWidth
distance -= lineWidth / 2;
if (distance < 0f) {
distance = 0f;
}
}
return distance;
}
// ////////////////////////////////////////////////////////////////////////
/**
* Prepare the graphic for rendering. This must be done before calling
* <code>render()</code>! If a vector graphic has lat-lon components, then
* we project these vertices into x-y space. For raster graphics we prepare
* in a different fashion.
* <p>
* If the generate is unsuccessful, it's usually because of some oversight,
* (for instance if <code>proj</code> is null), and if debugging is enabled,
* a message may be output to the controlling terminal.
* <p>
*
* @param proj Projection
* @return boolean true if successful, false if not.
* @see #regenerate
*/
public abstract boolean generate(Projection proj);
/**
* Paint the graphic. This paints the graphic into the Graphics context.
* This is similar to <code>paint()</code> function of java.awt.Components.
* Note that if the graphic has not been generated, it will not be rendered.
* <P>
*
* This method used to be abstract, but with the conversion of OMGraphics to
* internally represent themselves as java.awt.Shape objects, it's a more
* generic method. If the OMGraphic hasn't been updated to use Shape
* objects, it should have its own render method.
*
* @param g Graphics2D context to render into.
*/
public void render(Graphics g) {
renderShape(g);
renderLabel(g);
}
/**
* Called from render, just handles rendering of the java.awt.Shape of the
* OMGraphic, as it is currently stored in this OMGraphic.
*
* @param g java.awt.Graphics to render into.
*/
protected void renderShape(Graphics g) {
Shape s = getShape();
if (!isRenderable(s)) {
return;
}
if (matted) {
if (g instanceof Graphics2D && stroke instanceof BasicStroke) {
BasicStroke bs = (BasicStroke) stroke;
((Graphics2D) g).setStroke(new BasicStroke(bs.getLineWidth() + 2f, bs.getEndCap(), bs.getLineJoin()));
setGraphicsColor(g, mattingPaint);
draw(g, s);
}
}
if (shouldRenderFill()) {
setGraphicsForFill(g);
fill(g, s);
if (textureMask != null && textureMask != fillPaint) {
setGraphicsColor(g, textureMask);
fill(g, s);
}
}
if (shouldRenderEdge()) {
setGraphicsForEdge(g);
draw(g, s);
}
}
protected void setHasLabel(boolean val) {
hasLabel = val;
}
/**
* Quick check of the flag to see if a label attribute has been set. Labels
* are stored in the attribute table, and that table should only be checked
* in a generate() method call, and not in the render(). The setShape() and
* initLabelingDuringGenerate() method calls set this flag which is used to
* opt-out of labeling methods for better performance.
*
* @return true if OMGraphic has label set.
*/
public boolean getHasLabel() {
return hasLabel;
}
/**
* The method only needs to be called in an OMGraphic's generate method if
* the setShape() method isn't called there. The appropriate
* setLabelLocation method for where the label should be set should be
* called if this method is going to be used.
*/
protected void initLabelingDuringGenerate() {
setHasLabel(getAttribute(LABEL) != null);
}
/**
* Sets the label location at the center of the polygon points. If the
* hasLabel variable hasn't been set, it no-ops.
*
* @param xpoints
* @param ypoints
* @deprecated use the version with the projection.
*/
public void setLabelLocation(int[] xpoints, int[] ypoints) {
setLabelLocation(xpoints, ypoints, null);
}
/**
* @see #setLabelLocation(int[], int[])
* @deprecated use the version with the projection.
*/
public void setLabelLocation(float[] xpoints, float[] ypoints) {
setLabelLocation(xpoints, ypoints, null);
}
/**
* Checks the attributes for a label and moves the label accordingly. The
* label will be placed in the center of the bounding box around the path.
*
* @param xpoints int[] describing the projected location of the OMGraphic.
* @param ypoints int[] describing the projected location of the OMGraphic.
* @param proj the current projection to do further evaluating of placement.
*/
protected void setLabelLocation(int[] xpoints, int[] ypoints, Projection proj) {
hasLabel = false;
Object obj = getAttribute(LABEL);
if (obj instanceof OMLabeler) {
OMLabeler labeler = (OMLabeler) obj;
// Go ahead and set the label location if the shape exists.
if (xpoints != null && ypoints != null) {
labeler.setLocation(xpoints, ypoints);
hasLabel = true;
}
if (proj != null) {
labeler.evaluateRotationAngle(proj);
}
}
}
/**
* @see #setLabelLocation(int[], int[])
*/
public void setLabelLocation(float[] xpoints, float[] ypoints, Projection proj) {
int[] xs = new int[xpoints.length];
int[] ys = new int[ypoints.length];
for (int i = 0; i < xpoints.length; i++) {
xs[i] = (int) xpoints[i];
ys[i] = (int) ypoints[i];
}
setLabelLocation(xs, ys, proj);
}
/**
* Sets the label location at the given point. If the hasLabel variable
* hasn't been set, it no-ops.
*
* @param p
* @deprecated use the version with Projection.
*/
public void setLabelLocation(Point2D p) {
setLabelLocation(p, null);
}
/**
* Checks the attributes for a label and moves the label accordingly. The
* label will be placed in the center of the bounding box around the path.
*
* @param p Point2D describing the projected location of the label.
* @param proj the current projection to do further evaluating of placement.
*/
protected void setLabelLocation(Point2D p, Projection proj) {
hasLabel = false;
Object obj = getAttribute(LABEL);
if (obj instanceof OMLabeler) {
OMLabeler labeler = (OMLabeler) obj;
// Go ahead and set the label location if the shape exists.
if (p != null) {
labeler.setLocation(p);
hasLabel = true;
}
if (proj != null) {
labeler.evaluateRotationAngle(proj);
}
}
}
/**
* Sets the label location at the center of the bounding box of the path. If
* the hasLabel variable hasn't been set, it no-ops.
*
* @param gp GeneralPath
* @deprecated use the version with the Projection
*/
public void setLabelLocation(GeneralPath gp) {
setLabelLocation(gp, null);
}
/**
* Checks the attributes for a label and moves the label accordingly. The
* label will be placed in the center of the bounding box around the path.
*
* @param gp GeneralPath describing the projected location of the OMGraphic.
* @param proj the current projection to do further evaluating of placement.
*/
protected void setLabelLocation(GeneralPath gp, Projection proj) {
hasLabel = false;
Object obj = getAttribute(LABEL);
if (obj instanceof OMLabeler) {
OMLabeler labeler = (OMLabeler) obj;
// Go ahead and set the label location if the shape exists.
if (gp != null) {
labeler.setLocation(gp);
hasLabel = true;
}
if (proj != null) {
labeler.evaluateRotationAngle(proj);
}
}
}
/**
* Checks to see if a label should be painted based on what methods were
* called in generate(), and renders the label if necessary. If the label
* wasn't set up, a quick no-op occurs.
*
* @param g
*/
public void renderLabel(Graphics g) {
if (hasLabel) {
OMLabeler labeler = (OMLabeler) getAttribute(LABEL);
if (labeler != null) {
labeler.render(g);
}
}
}
/**
* Return true of the fill color/paint should be rendered (not clear).
*/
public boolean shouldRenderFill() {
return !isClear(getFillPaint());
}
/**
* Return true if the edge color/paint should be rendered (not clear, or
* doesn't match the fill color).
*/
public boolean shouldRenderEdge() {
// OK, so isClear on the displayPaitn could be inaccurate if
// another thread changes the display paint on the graphic
// before it actually gets rendered.
return !isClear(getDisplayPaint()) || !edgeMatchesFill;
}
/**
* Return the shortest distance from the graphic to an XY-point.
* <p>
*
* This method used to be abstract, but with the conversion of OMGraphics to
* internally represent themselves as java.awt.Shape objects, it's a more
* generic method. If the OMGraphic hasn't been updated to use Shape
* objects, it should have its own distance method.
*
* @param x X coordinate of the point.
* @param y Y coordinate of the point.
* @return float distance, in pixels, from graphic to the point. Returns
* Float.POSITIVE_INFINITY if the graphic isn't ready (ungenerated).
*/
public float distance(double x, double y) {
float distance = Float.POSITIVE_INFINITY;
if (shouldRenderFill()) {
distance = super.distance(x, y);
} else {
distance = super.distanceToEdge(x, y);
}
if (distance != Float.POSITIVE_INFINITY) {
distance = normalizeDistanceForLineWidth(distance);
}
if (hasLabel) {
OMLabeler labeler = (OMLabeler) getAttribute(LABEL);
if (labeler != null) {
float lDistance = labeler.distance(x, y);
if (lDistance < distance) {
distance = lDistance;
}
}
}
return distance;
}
/**
* Invoke this to regenerate a "dirty" graphic. This method is a wrapper
* around the <code>generate()</code> method. It invokes
* <code>generate()</code> only if<code> needToRegenerate() </code> on the
* graphic returns true. To force a graphic to be generated, call
* <code>generate()</code> directly.
*
* @param proj the Projection
* @return true if generated, false if didn't do it (maybe a problem).
* @see #generate
*/
public boolean regenerate(Projection proj) {
boolean ret = false;
if (proj != null) {
ret = super.regenerate(proj);
}
return ret;
}
/**
* Used by the GraphicAttributes object to provide a choice on whether the
* line type choice can be changed.
*/
public boolean hasLineTypeChoice() {
return true;
}
/**
* Generic return of SinkGraphic for subclasses that don't implement clone
* properly.
*/
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return SinkGraphic.getSharedInstance();
}
}
/**
* Write this object to a stream.
*/
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
// Now write the Stroke and TexturePaint mask. Take into account they
// each could be null.
writeStroke(oos, stroke, OMGraphicAdapter.BASIC_STROKE);
writeTextureMask(oos, textureMask);
}
protected void writeStroke(ObjectOutputStream oos, Stroke stroke, Stroke defStroke)
throws IOException {
boolean writeStroke = (stroke != defStroke) && stroke != null;
if (writeStroke) {
// First write a flag indicating if a Stroke is on the
// stream.
oos.writeBoolean(true);
if (stroke instanceof BasicStroke) {
BasicStroke s = (BasicStroke) stroke;
// Then write flag indicating stroke is a BasicStroke
oos.writeBoolean(true);
// Write the Stroke data if a Stroke is on this
// object.
if (s != null) {
oos.writeFloat(s.getLineWidth());
oos.writeInt(s.getEndCap());
oos.writeInt(s.getLineJoin());
oos.writeFloat(s.getMiterLimit());
oos.writeObject(s.getDashArray());
oos.writeFloat(s.getDashPhase());
}
} else if (stroke instanceof Serializable) {
oos.writeBoolean(false);
oos.writeObject((Serializable) stroke);
}
} else {
oos.writeBoolean(false);
}
}
/**
* Will only write TexturePaint objects that are Serializable.
*
* @param oos the ObjectOutputStream to write on
* @param tMask the TexturePaint mask
* @throws IOException
*/
protected void writeTextureMask(ObjectOutputStream oos, TexturePaint tMask) throws IOException {
boolean writeMask = (tMask instanceof Serializable);
if (writeMask) {
// First write a flag indicating if the Mask is on the
// stream.
oos.writeBoolean(true);
oos.writeObject((Serializable) tMask);
} else {
oos.writeBoolean(false);
}
}
/**
* Read this object from a stream.
*/
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
// Read the Stroke
stroke = readStroke(ois, OMGraphicAdapter.BASIC_STROKE);
textureMask = readTextureMask(ois);
}
protected Stroke readStroke(ObjectInputStream ois, Stroke defStroke)
throws ClassNotFoundException, IOException {
Stroke stroke = defStroke;
// Get the flag indicating a stroke was streamed
boolean streamHasStroke = ois.readBoolean();
// Read and create the stroke
if (streamHasStroke) {
boolean isBasicStroke = ois.readBoolean();
if (isBasicStroke) {
float linewidth = ois.readFloat();
int endcap = ois.readInt();
int linejoin = ois.readInt();
float miterlimit = ois.readFloat();
float dasharray[] = (float[]) ois.readObject();
float dashphase = ois.readFloat();
stroke = new BasicStroke(linewidth, endcap, linejoin, miterlimit, dasharray, dashphase);
} else {
stroke = (Stroke) ois.readObject();
}
}
return stroke;
}
protected TexturePaint readTextureMask(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
TexturePaint tPaint = null;
// Get the flag indicating a stroke was streamed
boolean streamHasTPaint = ois.readBoolean();
// Read and create the stroke
if (streamHasTPaint) {
tPaint = (TexturePaint) ois.readObject();
}
return tPaint;
}
/**
* Takes the generic OMGraphic settings from another OMGraphic and pushes
* them to this one.
*/
public void restore(OMGeometry source) {
super.restore(source);
this.renderType = source.getRenderType();
if (source instanceof OMGraphic) {
OMGraphic omgSource = (OMGraphic) source;
this.declutterType = omgSource.getDeclutterType();
this.selected = omgSource.isSelected();
this.showEditablePalette = omgSource.getShowEditablePalette();
DrawingAttributes.sTransfer(omgSource, this);
}
}
}