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