Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ------------------------ 28: * XYPolygonAnnotation.java 29: * ------------------------ 30: * (C) Copyright 2005-2007, by Object Refinery Limited and Contributors. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * $Id: XYPolygonAnnotation.java,v 1.3.2.5 2007/03/06 16:12:18 mungady Exp $ 36: * 37: * Changes: 38: * -------- 39: * 09-Feb-2005 : Version 1 (DG); 40: * 41: */ 42: 43: package org.jfree.chart.annotations; 44: 45: import java.awt.BasicStroke; 46: import java.awt.Color; 47: import java.awt.Graphics2D; 48: import java.awt.Paint; 49: import java.awt.Stroke; 50: import java.awt.geom.GeneralPath; 51: import java.awt.geom.Rectangle2D; 52: import java.io.IOException; 53: import java.io.ObjectInputStream; 54: import java.io.ObjectOutputStream; 55: import java.io.Serializable; 56: import java.util.Arrays; 57: 58: import org.jfree.chart.HashUtilities; 59: import org.jfree.chart.axis.ValueAxis; 60: import org.jfree.chart.plot.Plot; 61: import org.jfree.chart.plot.PlotOrientation; 62: import org.jfree.chart.plot.PlotRenderingInfo; 63: import org.jfree.chart.plot.XYPlot; 64: import org.jfree.io.SerialUtilities; 65: import org.jfree.ui.RectangleEdge; 66: import org.jfree.util.ObjectUtilities; 67: import org.jfree.util.PaintUtilities; 68: import org.jfree.util.PublicCloneable; 69: 70: /** 71: * A polygon annotation that can be placed on an {@link XYPlot}. The 72: * polygon coordinates are specified in data space. 73: */ 74: public class XYPolygonAnnotation extends AbstractXYAnnotation 75: implements Cloneable, 76: PublicCloneable, 77: Serializable { 78: 79: /** For serialization. */ 80: private static final long serialVersionUID = -6984203651995900036L; 81: 82: /** The polygon. */ 83: private double[] polygon; 84: 85: /** The stroke used to draw the box outline. */ 86: private transient Stroke stroke; 87: 88: /** The paint used to draw the box outline. */ 89: private transient Paint outlinePaint; 90: 91: /** The paint used to fill the box. */ 92: private transient Paint fillPaint; 93: 94: /** 95: * Creates a new annotation (where, by default, the polygon is drawn 96: * with a black outline). The array of polygon coordinates must contain 97: * an even number of coordinates (each pair is an (x, y) location on the 98: * plot) and the last point is automatically joined back to the first point. 99: * 100: * @param polygon the coordinates of the polygon's vertices 101: * (<code>null</code> not permitted). 102: */ 103: public XYPolygonAnnotation(double[] polygon) { 104: this(polygon, new BasicStroke(1.0f), Color.black); 105: } 106: 107: /** 108: * Creates a new annotation where the box is drawn as an outline using 109: * the specified <code>stroke</code> and <code>outlinePaint</code>. 110: * The array of polygon coordinates must contain an even number of 111: * coordinates (each pair is an (x, y) location on the plot) and the last 112: * point is automatically joined back to the first point. 113: * 114: * @param polygon the coordinates of the polygon's vertices 115: * (<code>null</code> not permitted). 116: * @param stroke the shape stroke (<code>null</code> permitted). 117: * @param outlinePaint the shape color (<code>null</code> permitted). 118: */ 119: public XYPolygonAnnotation(double[] polygon, 120: Stroke stroke, Paint outlinePaint) { 121: this(polygon, stroke, outlinePaint, null); 122: } 123: 124: /** 125: * Creates a new annotation. The array of polygon coordinates must 126: * contain an even number of coordinates (each pair is an (x, y) location 127: * on the plot) and the last point is automatically joined back to the 128: * first point. 129: * 130: * @param polygon the coordinates of the polygon's vertices 131: * (<code>null</code> not permitted). 132: * @param stroke the shape stroke (<code>null</code> permitted). 133: * @param outlinePaint the shape color (<code>null</code> permitted). 134: * @param fillPaint the paint used to fill the shape (<code>null</code> 135: * permitted). 136: */ 137: public XYPolygonAnnotation(double[] polygon, 138: Stroke stroke, 139: Paint outlinePaint, Paint fillPaint) { 140: if (polygon == null) { 141: throw new IllegalArgumentException("Null 'polygon' argument."); 142: } 143: if (polygon.length % 2 != 0) { 144: throw new IllegalArgumentException("The 'polygon' array must " 145: + "contain an even number of items."); 146: } 147: this.polygon = (double[]) polygon.clone(); 148: this.stroke = stroke; 149: this.outlinePaint = outlinePaint; 150: this.fillPaint = fillPaint; 151: } 152: 153: /** 154: * Returns the coordinates of the polygon's vertices. The returned array 155: * is a copy, so it is safe to modify without altering the annotation's 156: * state. 157: * 158: * @return The coordinates of the polygon's vertices. 159: * 160: * @since 1.0.2 161: */ 162: public double[] getPolygonCoordinates() { 163: return (double[]) this.polygon.clone(); 164: } 165: 166: /** 167: * Returns the fill paint. 168: * 169: * @return The fill paint (possibly <code>null</code>). 170: * 171: * @since 1.0.2 172: */ 173: public Paint getFillPaint() { 174: return this.fillPaint; 175: } 176: 177: /** 178: * Returns the outline stroke. 179: * 180: * @return The outline stroke (possibly <code>null</code>). 181: * 182: * @since 1.0.2 183: */ 184: public Stroke getOutlineStroke() { 185: return this.stroke; 186: } 187: 188: /** 189: * Returns the outline paint. 190: * 191: * @return The outline paint (possibly <code>null</code>). 192: * 193: * @since 1.0.2 194: */ 195: public Paint getOutlinePaint() { 196: return this.outlinePaint; 197: } 198: 199: /** 200: * Draws the annotation. This method is usually called by the 201: * {@link XYPlot} class, you shouldn't need to call it directly. 202: * 203: * @param g2 the graphics device. 204: * @param plot the plot. 205: * @param dataArea the data area. 206: * @param domainAxis the domain axis. 207: * @param rangeAxis the range axis. 208: * @param rendererIndex the renderer index. 209: * @param info the plot rendering info. 210: */ 211: public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, 212: ValueAxis domainAxis, ValueAxis rangeAxis, 213: int rendererIndex, PlotRenderingInfo info) { 214: 215: // if we don't have at least 2 (x, y) coordinates, just return 216: if (this.polygon.length < 4) { 217: return; 218: } 219: PlotOrientation orientation = plot.getOrientation(); 220: RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( 221: plot.getDomainAxisLocation(), orientation); 222: RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( 223: plot.getRangeAxisLocation(), orientation); 224: 225: GeneralPath area = new GeneralPath(); 226: double x = domainAxis.valueToJava2D(this.polygon[0], dataArea, 227: domainEdge); 228: double y = rangeAxis.valueToJava2D(this.polygon[1], dataArea, 229: rangeEdge); 230: if (orientation == PlotOrientation.HORIZONTAL) { 231: area.moveTo((float) y, (float) x); 232: for (int i = 2; i < this.polygon.length; i += 2) { 233: x = domainAxis.valueToJava2D(this.polygon[i], dataArea, 234: domainEdge); 235: y = rangeAxis.valueToJava2D(this.polygon[i + 1], dataArea, 236: rangeEdge); 237: area.lineTo((float) y, (float) x); 238: } 239: area.closePath(); 240: } 241: else if (orientation == PlotOrientation.VERTICAL) { 242: area.moveTo((float) x, (float) y); 243: for (int i = 2; i < this.polygon.length; i += 2) { 244: x = domainAxis.valueToJava2D(this.polygon[i], dataArea, 245: domainEdge); 246: y = rangeAxis.valueToJava2D(this.polygon[i + 1], dataArea, 247: rangeEdge); 248: area.lineTo((float) x, (float) y); 249: } 250: area.closePath(); 251: } 252: 253: 254: if (this.fillPaint != null) { 255: g2.setPaint(this.fillPaint); 256: g2.fill(area); 257: } 258: 259: if (this.stroke != null && this.outlinePaint != null) { 260: g2.setPaint(this.outlinePaint); 261: g2.setStroke(this.stroke); 262: g2.draw(area); 263: } 264: addEntity(info, area, rendererIndex, getToolTipText(), getURL()); 265: 266: } 267: 268: /** 269: * Tests this annotation for equality with an arbitrary object. 270: * 271: * @param obj the object (<code>null</code> permitted). 272: * 273: * @return A boolean. 274: */ 275: public boolean equals(Object obj) { 276: if (obj == this) { 277: return true; 278: } 279: // now try to reject equality 280: if (!super.equals(obj)) { 281: return false; 282: } 283: if (!(obj instanceof XYPolygonAnnotation)) { 284: return false; 285: } 286: XYPolygonAnnotation that = (XYPolygonAnnotation) obj; 287: if (!Arrays.equals(this.polygon, that.polygon)) { 288: return false; 289: } 290: if (!ObjectUtilities.equal(this.stroke, that.stroke)) { 291: return false; 292: } 293: if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) { 294: return false; 295: } 296: if (!PaintUtilities.equal(this.fillPaint, that.fillPaint)) { 297: return false; 298: } 299: // seem to be the same 300: return true; 301: } 302: 303: /** 304: * Returns a hash code for this instance. 305: * 306: * @return A hash code. 307: */ 308: public int hashCode() { 309: int result = 193; 310: result = 37 * result + HashUtilities.hashCodeForDoubleArray( 311: this.polygon); 312: result = 37 * result + HashUtilities.hashCodeForPaint(this.fillPaint); 313: result = 37 * result + HashUtilities.hashCodeForPaint( 314: this.outlinePaint); 315: if (this.stroke != null) { 316: result = 37 * result + this.stroke.hashCode(); 317: } 318: return result; 319: } 320: 321: /** 322: * Returns a clone. 323: * 324: * @return A clone. 325: * 326: * @throws CloneNotSupportedException not thrown by this class, but may be 327: * by subclasses. 328: */ 329: public Object clone() throws CloneNotSupportedException { 330: return super.clone(); 331: } 332: 333: /** 334: * Provides serialization support. 335: * 336: * @param stream the output stream (<code>null</code> not permitted). 337: * 338: * @throws IOException if there is an I/O error. 339: */ 340: private void writeObject(ObjectOutputStream stream) throws IOException { 341: stream.defaultWriteObject(); 342: SerialUtilities.writeStroke(this.stroke, stream); 343: SerialUtilities.writePaint(this.outlinePaint, stream); 344: SerialUtilities.writePaint(this.fillPaint, stream); 345: } 346: 347: /** 348: * Provides serialization support. 349: * 350: * @param stream the input stream (<code>null</code> not permitted). 351: * 352: * @throws IOException if there is an I/O error. 353: * @throws ClassNotFoundException if there is a classpath problem. 354: */ 355: private void readObject(ObjectInputStream stream) 356: throws IOException, ClassNotFoundException { 357: stream.defaultReadObject(); 358: this.stroke = SerialUtilities.readStroke(stream); 359: this.outlinePaint = SerialUtilities.readPaint(stream); 360: this.fillPaint = SerialUtilities.readPaint(stream); 361: } 362: 363: }