001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jfreechart/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * ------------------------
028: * XYPolygonAnnotation.java
029: * ------------------------
030: * (C) Copyright 2005-2007, by Object Refinery Limited and Contributors.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: XYPolygonAnnotation.java,v 1.3.2.5 2007/03/06 16:12:18 mungady Exp $
036: *
037: * Changes:
038: * --------
039: * 09-Feb-2005 : Version 1 (DG);
040: *
041: */
042:
043: package org.jfree.chart.annotations;
044:
045: import java.awt.BasicStroke;
046: import java.awt.Color;
047: import java.awt.Graphics2D;
048: import java.awt.Paint;
049: import java.awt.Stroke;
050: import java.awt.geom.GeneralPath;
051: import java.awt.geom.Rectangle2D;
052: import java.io.IOException;
053: import java.io.ObjectInputStream;
054: import java.io.ObjectOutputStream;
055: import java.io.Serializable;
056: import java.util.Arrays;
057:
058: import org.jfree.chart.HashUtilities;
059: import org.jfree.chart.axis.ValueAxis;
060: import org.jfree.chart.plot.Plot;
061: import org.jfree.chart.plot.PlotOrientation;
062: import org.jfree.chart.plot.PlotRenderingInfo;
063: import org.jfree.chart.plot.XYPlot;
064: import org.jfree.io.SerialUtilities;
065: import org.jfree.ui.RectangleEdge;
066: import org.jfree.util.ObjectUtilities;
067: import org.jfree.util.PaintUtilities;
068: import org.jfree.util.PublicCloneable;
069:
070: /**
071: * A polygon annotation that can be placed on an {@link XYPlot}. The
072: * polygon coordinates are specified in data space.
073: */
074: public class XYPolygonAnnotation extends AbstractXYAnnotation implements
075: Cloneable, PublicCloneable, Serializable {
076:
077: /** For serialization. */
078: private static final long serialVersionUID = -6984203651995900036L;
079:
080: /** The polygon. */
081: private double[] polygon;
082:
083: /** The stroke used to draw the box outline. */
084: private transient Stroke stroke;
085:
086: /** The paint used to draw the box outline. */
087: private transient Paint outlinePaint;
088:
089: /** The paint used to fill the box. */
090: private transient Paint fillPaint;
091:
092: /**
093: * Creates a new annotation (where, by default, the polygon is drawn
094: * with a black outline). The array of polygon coordinates must contain
095: * an even number of coordinates (each pair is an (x, y) location on the
096: * plot) and the last point is automatically joined back to the first point.
097: *
098: * @param polygon the coordinates of the polygon's vertices
099: * (<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, Stroke stroke,
118: 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, Stroke stroke,
136: Paint outlinePaint, Paint fillPaint) {
137: if (polygon == null) {
138: throw new IllegalArgumentException(
139: "Null 'polygon' argument.");
140: }
141: if (polygon.length % 2 != 0) {
142: throw new IllegalArgumentException(
143: "The 'polygon' array must "
144: + "contain an even number of items.");
145: }
146: this .polygon = (double[]) polygon.clone();
147: this .stroke = stroke;
148: this .outlinePaint = outlinePaint;
149: this .fillPaint = fillPaint;
150: }
151:
152: /**
153: * Returns the coordinates of the polygon's vertices. The returned array
154: * is a copy, so it is safe to modify without altering the annotation's
155: * state.
156: *
157: * @return The coordinates of the polygon's vertices.
158: *
159: * @since 1.0.2
160: */
161: public double[] getPolygonCoordinates() {
162: return (double[]) this .polygon.clone();
163: }
164:
165: /**
166: * Returns the fill paint.
167: *
168: * @return The fill paint (possibly <code>null</code>).
169: *
170: * @since 1.0.2
171: */
172: public Paint getFillPaint() {
173: return this .fillPaint;
174: }
175:
176: /**
177: * Returns the outline stroke.
178: *
179: * @return The outline stroke (possibly <code>null</code>).
180: *
181: * @since 1.0.2
182: */
183: public Stroke getOutlineStroke() {
184: return this .stroke;
185: }
186:
187: /**
188: * Returns the outline paint.
189: *
190: * @return The outline paint (possibly <code>null</code>).
191: *
192: * @since 1.0.2
193: */
194: public Paint getOutlinePaint() {
195: return this .outlinePaint;
196: }
197:
198: /**
199: * Draws the annotation. This method is usually called by the
200: * {@link XYPlot} class, you shouldn't need to call it directly.
201: *
202: * @param g2 the graphics device.
203: * @param plot the plot.
204: * @param dataArea the data area.
205: * @param domainAxis the domain axis.
206: * @param rangeAxis the range axis.
207: * @param rendererIndex the renderer index.
208: * @param info the plot rendering info.
209: */
210: public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
211: ValueAxis domainAxis, ValueAxis rangeAxis,
212: int rendererIndex, PlotRenderingInfo info) {
213:
214: // if we don't have at least 2 (x, y) coordinates, just return
215: if (this .polygon.length < 4) {
216: return;
217: }
218: PlotOrientation orientation = plot.getOrientation();
219: RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(plot
220: .getDomainAxisLocation(), orientation);
221: RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(plot
222: .getRangeAxisLocation(), orientation);
223:
224: GeneralPath area = new GeneralPath();
225: double x = domainAxis.valueToJava2D(this .polygon[0], dataArea,
226: domainEdge);
227: double y = rangeAxis.valueToJava2D(this .polygon[1], dataArea,
228: rangeEdge);
229: if (orientation == PlotOrientation.HORIZONTAL) {
230: area.moveTo((float) y, (float) x);
231: for (int i = 2; i < this .polygon.length; i += 2) {
232: x = domainAxis.valueToJava2D(this .polygon[i], dataArea,
233: domainEdge);
234: y = rangeAxis.valueToJava2D(this .polygon[i + 1],
235: dataArea, rangeEdge);
236: area.lineTo((float) y, (float) x);
237: }
238: area.closePath();
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],
245: dataArea, rangeEdge);
246: area.lineTo((float) x, (float) y);
247: }
248: area.closePath();
249: }
250:
251: if (this .fillPaint != null) {
252: g2.setPaint(this .fillPaint);
253: g2.fill(area);
254: }
255:
256: if (this .stroke != null && this .outlinePaint != null) {
257: g2.setPaint(this .outlinePaint);
258: g2.setStroke(this .stroke);
259: g2.draw(area);
260: }
261: addEntity(info, area, rendererIndex, getToolTipText(), getURL());
262:
263: }
264:
265: /**
266: * Tests this annotation for equality with an arbitrary object.
267: *
268: * @param obj the object (<code>null</code> permitted).
269: *
270: * @return A boolean.
271: */
272: public boolean equals(Object obj) {
273: if (obj == this ) {
274: return true;
275: }
276: // now try to reject equality
277: if (!super .equals(obj)) {
278: return false;
279: }
280: if (!(obj instanceof XYPolygonAnnotation)) {
281: return false;
282: }
283: XYPolygonAnnotation that = (XYPolygonAnnotation) obj;
284: if (!Arrays.equals(this .polygon, that.polygon)) {
285: return false;
286: }
287: if (!ObjectUtilities.equal(this .stroke, that.stroke)) {
288: return false;
289: }
290: if (!PaintUtilities.equal(this .outlinePaint, that.outlinePaint)) {
291: return false;
292: }
293: if (!PaintUtilities.equal(this .fillPaint, that.fillPaint)) {
294: return false;
295: }
296: // seem to be the same
297: return true;
298: }
299:
300: /**
301: * Returns a hash code for this instance.
302: *
303: * @return A hash code.
304: */
305: public int hashCode() {
306: int result = 193;
307: result = 37 * result
308: + HashUtilities.hashCodeForDoubleArray(this .polygon);
309: result = 37 * result
310: + HashUtilities.hashCodeForPaint(this .fillPaint);
311: result = 37 * result
312: + HashUtilities.hashCodeForPaint(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)
339: throws IOException {
340: stream.defaultWriteObject();
341: SerialUtilities.writeStroke(this .stroke, stream);
342: SerialUtilities.writePaint(this .outlinePaint, stream);
343: SerialUtilities.writePaint(this .fillPaint, stream);
344: }
345:
346: /**
347: * Provides serialization support.
348: *
349: * @param stream the input stream (<code>null</code> not permitted).
350: *
351: * @throws IOException if there is an I/O error.
352: * @throws ClassNotFoundException if there is a classpath problem.
353: */
354: private void readObject(ObjectInputStream stream)
355: throws IOException, ClassNotFoundException {
356: stream.defaultReadObject();
357: this.stroke = SerialUtilities.readStroke(stream);
358: this.outlinePaint = SerialUtilities.readPaint(stream);
359: this.fillPaint = SerialUtilities.readPaint(stream);
360: }
361:
362: }
|