KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > annotations > CategoryPointerAnnotation


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, 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  * CategoryPointerAnnotation.java
29  * ------------------------------
30  * (C) Copyright 2006, by Object Refinery Limited.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): -;
34  *
35  * $Id: CategoryPointerAnnotation.java,v 1.1.2.2 2006/10/03 16:21:43 mungady Exp $
36  *
37  * Changes:
38  * --------
39  * 02-Oct-2006 : Version 1 (DG);
40  *
41  */

42
43 package org.jfree.chart.annotations;
44
45 import java.awt.BasicStroke JavaDoc;
46 import java.awt.Color JavaDoc;
47 import java.awt.Graphics2D JavaDoc;
48 import java.awt.Paint JavaDoc;
49 import java.awt.Stroke JavaDoc;
50 import java.awt.geom.GeneralPath JavaDoc;
51 import java.awt.geom.Line2D JavaDoc;
52 import java.awt.geom.Rectangle2D JavaDoc;
53 import java.io.IOException JavaDoc;
54 import java.io.ObjectInputStream JavaDoc;
55 import java.io.ObjectOutputStream JavaDoc;
56 import java.io.Serializable JavaDoc;
57
58 import org.jfree.chart.axis.CategoryAxis;
59 import org.jfree.chart.axis.ValueAxis;
60 import org.jfree.chart.plot.CategoryPlot;
61 import org.jfree.chart.plot.Plot;
62 import org.jfree.chart.plot.PlotOrientation;
63 import org.jfree.data.category.CategoryDataset;
64 import org.jfree.io.SerialUtilities;
65 import org.jfree.text.TextUtilities;
66 import org.jfree.ui.RectangleEdge;
67 import org.jfree.util.ObjectUtilities;
68 import org.jfree.util.PublicCloneable;
69
70 /**
71  * An arrow and label that can be placed on a {@link CategoryPlot}. The arrow
72  * is drawn at a user-definable angle so that it points towards the (category,
73  * value) location for the annotation.
74  * <p>
75  * The arrow length (and its offset from the (category, value) location) is
76  * controlled by the tip radius and the base radius attributes. Imagine two
77  * circles around the (category, value) coordinate: the inner circle defined by
78  * the tip radius, and the outer circle defined by the base radius. Now, draw
79  * the arrow starting at some point on the outer circle (the point is
80  * determined by the angle), with the arrow tip being drawn at a corresponding
81  * point on the inner circle.
82  *
83  * @since 1.0.3
84  */

85 public class CategoryPointerAnnotation extends CategoryTextAnnotation
86                                  implements Cloneable JavaDoc, PublicCloneable,
87                                             Serializable JavaDoc {
88
89     /** For serialization. */
90     private static final long serialVersionUID = -4031161445009858551L;
91     
92     /** The default tip radius (in Java2D units). */
93     public static final double DEFAULT_TIP_RADIUS = 10.0;
94     
95     /** The default base radius (in Java2D units). */
96     public static final double DEFAULT_BASE_RADIUS = 30.0;
97     
98     /** The default label offset (in Java2D units). */
99     public static final double DEFAULT_LABEL_OFFSET = 3.0;
100     
101     /** The default arrow length (in Java2D units). */
102     public static final double DEFAULT_ARROW_LENGTH = 5.0;
103
104     /** The default arrow width (in Java2D units). */
105     public static final double DEFAULT_ARROW_WIDTH = 3.0;
106     
107     /** The angle of the arrow's line (in radians). */
108     private double angle;
109
110     /**
111      * The radius from the (x, y) point to the tip of the arrow (in Java2D
112      * units).
113      */

114     private double tipRadius;
115
116     /**
117      * The radius from the (x, y) point to the start of the arrow line (in
118      * Java2D units).
119      */

120     private double baseRadius;
121
122     /** The length of the arrow head (in Java2D units). */
123     private double arrowLength;
124
125     /** The arrow width (in Java2D units, per side). */
126     private double arrowWidth;
127     
128     /** The arrow stroke. */
129     private transient Stroke JavaDoc arrowStroke;
130
131     /** The arrow paint. */
132     private transient Paint JavaDoc arrowPaint;
133     
134     /** The radius from the base point to the anchor point for the label. */
135     private double labelOffset;
136
137     /**
138      * Creates a new label and arrow annotation.
139      *
140      * @param label the label (<code>null</code> permitted).
141      * @param key the category key.
142      * @param value the y-value (measured against the chart's range axis).
143      * @param angle the angle of the arrow's line (in radians).
144      */

145     public CategoryPointerAnnotation(String JavaDoc label, Comparable JavaDoc key, double value,
146             double angle) {
147
148         super(label, key, value);
149         this.angle = angle;
150         this.tipRadius = DEFAULT_TIP_RADIUS;
151         this.baseRadius = DEFAULT_BASE_RADIUS;
152         this.arrowLength = DEFAULT_ARROW_LENGTH;
153         this.arrowWidth = DEFAULT_ARROW_WIDTH;
154         this.labelOffset = DEFAULT_LABEL_OFFSET;
155         this.arrowStroke = new BasicStroke JavaDoc(1.0f);
156         this.arrowPaint = Color.black;
157
158     }
159     
160     /**
161      * Returns the angle of the arrow.
162      *
163      * @return The angle (in radians).
164      */

165     public double getAngle() {
166         return this.angle;
167     }
168     
169     /**
170      * Sets the angle of the arrow.
171      *
172      * @param angle the angle (in radians).
173      */

174     public void setAngle(double angle) {
175         this.angle = angle;
176     }
177     
178     /**
179      * Returns the tip radius.
180      *
181      * @return The tip radius (in Java2D units).
182      */

183     public double getTipRadius() {
184         return this.tipRadius;
185     }
186     
187     /**
188      * Sets the tip radius.
189      *
190      * @param radius the radius (in Java2D units).
191      */

192     public void setTipRadius(double radius) {
193         this.tipRadius = radius;
194     }
195     
196     /**
197      * Returns the base radius.
198      *
199      * @return The base radius (in Java2D units).
200      */

201     public double getBaseRadius() {
202         return this.baseRadius;
203     }
204     
205     /**
206      * Sets the base radius.
207      *
208      * @param radius the radius (in Java2D units).
209      */

210     public void setBaseRadius(double radius) {
211         this.baseRadius = radius;
212     }
213
214     /**
215      * Returns the label offset.
216      *
217      * @return The label offset (in Java2D units).
218      */

219     public double getLabelOffset() {
220         return this.labelOffset;
221     }
222     
223     /**
224      * Sets the label offset (from the arrow base, continuing in a straight
225      * line, in Java2D units).
226      *
227      * @param offset the offset (in Java2D units).
228      */

229     public void setLabelOffset(double offset) {
230         this.labelOffset = offset;
231     }
232     
233     /**
234      * Returns the arrow length.
235      *
236      * @return The arrow length.
237      */

238     public double getArrowLength() {
239         return this.arrowLength;
240     }
241     
242     /**
243      * Sets the arrow length.
244      *
245      * @param length the length.
246      */

247     public void setArrowLength(double length) {
248         this.arrowLength = length;
249     }
250
251     /**
252      * Returns the arrow width.
253      *
254      * @return The arrow width (in Java2D units).
255      */

256     public double getArrowWidth() {
257         return this.arrowWidth;
258     }
259     
260     /**
261      * Sets the arrow width.
262      *
263      * @param width the width (in Java2D units).
264      */

265     public void setArrowWidth(double width) {
266         this.arrowWidth = width;
267     }
268     
269     /**
270      * Returns the stroke used to draw the arrow line.
271      *
272      * @return The arrow stroke (never <code>null</code>).
273      */

274     public Stroke JavaDoc getArrowStroke() {
275         return this.arrowStroke;
276     }
277
278     /**
279      * Sets the stroke used to draw the arrow line.
280      *
281      * @param stroke the stroke (<code>null</code> not permitted).
282      */

283     public void setArrowStroke(Stroke JavaDoc stroke) {
284         if (stroke == null) {
285             throw new IllegalArgumentException JavaDoc("Null 'stroke' not permitted.");
286         }
287         this.arrowStroke = stroke;
288     }
289     
290     /**
291      * Returns the paint used for the arrow.
292      *
293      * @return The arrow paint (never <code>null</code>).
294      */

295     public Paint JavaDoc getArrowPaint() {
296         return this.arrowPaint;
297     }
298     
299     /**
300      * Sets the paint used for the arrow.
301      *
302      * @param paint the arrow paint (<code>null</code> not permitted).
303      */

304     public void setArrowPaint(Paint JavaDoc paint) {
305         if (paint == null) {
306             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
307         }
308         this.arrowPaint = paint;
309     }
310     
311     /**
312      * Draws the annotation.
313      *
314      * @param g2 the graphics device.
315      * @param plot the plot.
316      * @param dataArea the data area.
317      * @param domainAxis the domain axis.
318      * @param rangeAxis the range axis.
319      */

320     public void draw(Graphics2D JavaDoc g2, CategoryPlot plot, Rectangle2D JavaDoc dataArea,
321             CategoryAxis domainAxis, ValueAxis rangeAxis) {
322
323         PlotOrientation orientation = plot.getOrientation();
324         RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
325                 plot.getDomainAxisLocation(), orientation);
326         RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
327                 plot.getRangeAxisLocation(), orientation);
328         CategoryDataset dataset = plot.getDataset();
329         int catIndex = dataset.getColumnIndex(getCategory());
330         int catCount = dataset.getColumnCount();
331         double j2DX = domainAxis.getCategoryMiddle(catIndex, catCount,
332                 dataArea, domainEdge);
333         double j2DY = rangeAxis.valueToJava2D(getValue(), dataArea, rangeEdge);
334         if (orientation == PlotOrientation.HORIZONTAL) {
335             double temp = j2DX;
336             j2DX = j2DY;
337             j2DY = temp;
338         }
339         double startX = j2DX + Math.cos(this.angle) * this.baseRadius;
340         double startY = j2DY + Math.sin(this.angle) * this.baseRadius;
341
342         double endX = j2DX + Math.cos(this.angle) * this.tipRadius;
343         double endY = j2DY + Math.sin(this.angle) * this.tipRadius;
344
345         double arrowBaseX = endX + Math.cos(this.angle) * this.arrowLength;
346         double arrowBaseY = endY + Math.sin(this.angle) * this.arrowLength;
347
348         double arrowLeftX = arrowBaseX
349             + Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth;
350         double arrowLeftY = arrowBaseY
351             + Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth;
352
353         double arrowRightX = arrowBaseX
354             - Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth;
355         double arrowRightY = arrowBaseY
356             - Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth;
357
358         GeneralPath JavaDoc arrow = new GeneralPath JavaDoc();
359         arrow.moveTo((float) endX, (float) endY);
360         arrow.lineTo((float) arrowLeftX, (float) arrowLeftY);
361         arrow.lineTo((float) arrowRightX, (float) arrowRightY);
362         arrow.closePath();
363
364         g2.setStroke(this.arrowStroke);
365         g2.setPaint(this.arrowPaint);
366         Line2D JavaDoc line = new Line2D.Double JavaDoc(startX, startY, endX, endY);
367         g2.draw(line);
368         g2.fill(arrow);
369
370         // draw the label
371
g2.setFont(getFont());
372         g2.setPaint(getPaint());
373         double labelX = j2DX
374             + Math.cos(this.angle) * (this.baseRadius + this.labelOffset);
375         double labelY = j2DY
376             + Math.sin(this.angle) * (this.baseRadius + this.labelOffset);
377         /* Rectangle2D hotspot = */ TextUtilities.drawAlignedString(getText(),
378                 g2, (float) labelX, (float) labelY, getTextAnchor());
379         // TODO: implement the entity for the annotation
380

381     }
382     
383     /**
384      * Tests this annotation for equality with an arbitrary object.
385      *
386      * @param obj the object (<code>null</code> permitted).
387      *
388      * @return <code>true</code> or <code>false</code>.
389      */

390     public boolean equals(Object JavaDoc obj) {
391         
392         if (obj == null) {
393             return false;
394         }
395         if (obj == this) {
396             return true;
397         }
398         if (!(obj instanceof CategoryPointerAnnotation)) {
399             return false;
400         }
401         if (!super.equals(obj)) {
402             return false;
403         }
404         CategoryPointerAnnotation that = (CategoryPointerAnnotation) obj;
405         if (this.angle != that.angle) {
406             return false;
407         }
408         if (this.tipRadius != that.tipRadius) {
409             return false;
410         }
411         if (this.baseRadius != that.baseRadius) {
412             return false;
413         }
414         if (this.arrowLength != that.arrowLength) {
415             return false;
416         }
417         if (this.arrowWidth != that.arrowWidth) {
418             return false;
419         }
420         if (!this.arrowPaint.equals(that.arrowPaint)) {
421             return false;
422         }
423         if (!ObjectUtilities.equal(this.arrowStroke, that.arrowStroke)) {
424             return false;
425         }
426         if (this.labelOffset != that.labelOffset) {
427             return false;
428         }
429         return true;
430     }
431     
432     /**
433      * Returns a clone of the annotation.
434      *
435      * @return A clone.
436      *
437      * @throws CloneNotSupportedException if the annotation can't be cloned.
438      */

439     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
440         return super.clone();
441     }
442
443     /**
444      * Provides serialization support.
445      *
446      * @param stream the output stream.
447      *
448      * @throws IOException if there is an I/O error.
449      */

450     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
451         stream.defaultWriteObject();
452         SerialUtilities.writePaint(this.arrowPaint, stream);
453         SerialUtilities.writeStroke(this.arrowStroke, stream);
454     }
455
456     /**
457      * Provides serialization support.
458      *
459      * @param stream the input stream.
460      *
461      * @throws IOException if there is an I/O error.
462      * @throws ClassNotFoundException if there is a classpath problem.
463      */

464     private void readObject(ObjectInputStream JavaDoc stream)
465         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
466         stream.defaultReadObject();
467         this.arrowPaint = SerialUtilities.readPaint(stream);
468         this.arrowStroke = SerialUtilities.readStroke(stream);
469     }
470
471 }
472
Popular Tags