KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > plot > RingPlot


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  * RingPlot.java
29  * -------------
30  * (C) Copyright 2004-2006, by Object Refinery Limited.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limtied);
33  * Contributor(s): -
34  *
35  * $Id: RingPlot.java,v 1.4.2.10 2006/10/24 19:47:56 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 08-Nov-2004 : Version 1 (DG);
40  * 22-Feb-2005 : Renamed DonutPlot --> RingPlot (DG);
41  * 06-Jun-2005 : Added default constructor and fixed equals() method to handle
42  * GradientPaint (DG);
43  * ------------- JFREECHART 1.0.x ---------------------------------------------
44  * 20-Dec-2005 : Fixed problem with entity shape (bug 1386328) (DG);
45  * 27-Sep-2006 : Updated drawItem() method for new lookup methods (DG);
46  * 12-Oct-2006 : Added configurable section depth (DG);
47  *
48  */

49
50 package org.jfree.chart.plot;
51
52 import java.awt.BasicStroke JavaDoc;
53 import java.awt.Color JavaDoc;
54 import java.awt.Graphics2D JavaDoc;
55 import java.awt.Paint JavaDoc;
56 import java.awt.Shape JavaDoc;
57 import java.awt.Stroke JavaDoc;
58 import java.awt.geom.Arc2D JavaDoc;
59 import java.awt.geom.GeneralPath JavaDoc;
60 import java.awt.geom.Line2D JavaDoc;
61 import java.awt.geom.Rectangle2D JavaDoc;
62 import java.io.IOException JavaDoc;
63 import java.io.ObjectInputStream JavaDoc;
64 import java.io.ObjectOutputStream JavaDoc;
65 import java.io.Serializable JavaDoc;
66
67 import org.jfree.chart.entity.EntityCollection;
68 import org.jfree.chart.entity.PieSectionEntity;
69 import org.jfree.chart.event.PlotChangeEvent;
70 import org.jfree.chart.labels.PieToolTipGenerator;
71 import org.jfree.chart.urls.PieURLGenerator;
72 import org.jfree.data.general.PieDataset;
73 import org.jfree.io.SerialUtilities;
74 import org.jfree.ui.RectangleInsets;
75 import org.jfree.util.ObjectUtilities;
76 import org.jfree.util.PaintUtilities;
77 import org.jfree.util.Rotation;
78 import org.jfree.util.ShapeUtilities;
79 import org.jfree.util.UnitType;
80
81 /**
82  * A customised pie plot that leaves a hole in the middle.
83  */

84 public class RingPlot extends PiePlot implements Cloneable JavaDoc, Serializable JavaDoc {
85     
86     /** For serialization. */
87     private static final long serialVersionUID = 1556064784129676620L;
88     
89     /**
90      * A flag that controls whether or not separators are drawn between the
91      * sections of the chart.
92      */

93     private boolean separatorsVisible;
94     
95     /** The stroke used to draw separators. */
96     private transient Stroke JavaDoc separatorStroke;
97     
98     /** The paint used to draw separators. */
99     private transient Paint JavaDoc separatorPaint;
100     
101     /**
102      * The length of the inner separator extension (as a percentage of the
103      * depth of the sections).
104      */

105     private double innerSeparatorExtension;
106     
107     /**
108      * The length of the outer separator extension (as a percentage of the
109      * depth of the sections).
110      */

111     private double outerSeparatorExtension;
112
113     /**
114      * The depth of the section as a percentage of the diameter.
115      */

116     private double sectionDepth;
117
118     /**
119      * Creates a new plot with a <code>null</code> dataset.
120      */

121     public RingPlot() {
122         this(null);
123     }
124     
125     /**
126      * Creates a new plot for the specified dataset.
127      *
128      * @param dataset the dataset (<code>null</code> permitted).
129      */

130     public RingPlot(PieDataset dataset) {
131         super(dataset);
132         this.separatorsVisible = true;
133         this.separatorStroke = new BasicStroke JavaDoc(0.5f);
134         this.separatorPaint = Color.gray;
135         this.innerSeparatorExtension = 0.20; // twenty percent
136
this.outerSeparatorExtension = 0.20; // twenty percent
137
this.sectionDepth = 0.20; // 20%
138
}
139     
140     /**
141      * Returns a flag that indicates whether or not separators are drawn between
142      * the sections in the chart.
143      *
144      * @return A boolean.
145      *
146      * @see #setSeparatorsVisible(boolean)
147      */

148     public boolean getSeparatorsVisible() {
149         return this.separatorsVisible;
150     }
151     
152     /**
153      * Sets the flag that controls whether or not separators are drawn between
154      * the sections in the chart, and sends a {@link PlotChangeEvent} to all
155      * registered listeners.
156      *
157      * @param visible the flag.
158      *
159      * @see #getSeparatorsVisible()
160      */

161     public void setSeparatorsVisible(boolean visible) {
162         this.separatorsVisible = visible;
163         notifyListeners(new PlotChangeEvent(this));
164     }
165     
166     /**
167      * Returns the separator stroke.
168      *
169      * @return The stroke (never <code>null</code>).
170      *
171      * @see #setSeparatorStroke(Stroke)
172      */

173     public Stroke JavaDoc getSeparatorStroke() {
174         return this.separatorStroke;
175     }
176     
177     /**
178      * Sets the stroke used to draw the separator between sections.
179      *
180      * @param stroke the stroke (<code>null</code> not permitted).
181      *
182      * @see #getSeparatorStroke()
183      */

184     public void setSeparatorStroke(Stroke JavaDoc stroke) {
185         if (stroke == null) {
186             throw new IllegalArgumentException JavaDoc("Null 'stroke' argument.");
187         }
188         this.separatorStroke = stroke;
189         notifyListeners(new PlotChangeEvent(this));
190     }
191     
192     /**
193      * Returns the separator paint.
194      *
195      * @return The paint (never <code>null</code>).
196      *
197      * @see #setSeparatorPaint(Paint)
198      */

199     public Paint JavaDoc getSeparatorPaint() {
200         return this.separatorPaint;
201     }
202     
203     /**
204      * Sets the paint used to draw the separator between sections.
205      *
206      * @param paint the paint (<code>null</code> not permitted).
207      *
208      * @see #getSeparatorPaint()
209      */

210     public void setSeparatorPaint(Paint JavaDoc paint) {
211         if (paint == null) {
212             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
213         }
214         this.separatorPaint = paint;
215         notifyListeners(new PlotChangeEvent(this));
216     }
217     
218     /**
219      * Returns the length of the inner extension of the separator line that
220      * is drawn between sections, expressed as a percentage of the depth of
221      * the section.
222      *
223      * @return The inner separator extension (as a percentage).
224      *
225      * @see #setInnerSeparatorExtension(double)
226      */

227     public double getInnerSeparatorExtension() {
228         return this.innerSeparatorExtension;
229     }
230     
231     /**
232      * Sets the length of the inner extension of the separator line that is
233      * drawn between sections, as a percentage of the depth of the
234      * sections, and sends a {@link PlotChangeEvent} to all registered
235      * listeners.
236      *
237      * @param percent the percentage.
238      *
239      * @see #getInnerSeparatorExtension()
240      * @see #setOuterSeparatorExtension(double)
241      */

242     public void setInnerSeparatorExtension(double percent) {
243         this.innerSeparatorExtension = percent;
244         notifyListeners(new PlotChangeEvent(this));
245     }
246     
247     /**
248      * Returns the length of the outer extension of the separator line that
249      * is drawn between sections, expressed as a percentage of the depth of
250      * the section.
251      *
252      * @return The outer separator extension (as a percentage).
253      *
254      * @see #setOuterSeparatorExtension(double)
255      */

256     public double getOuterSeparatorExtension() {
257         return this.outerSeparatorExtension;
258     }
259     
260     /**
261      * Sets the length of the outer extension of the separator line that is
262      * drawn between sections, as a percentage of the depth of the
263      * sections, and sends a {@link PlotChangeEvent} to all registered
264      * listeners.
265      *
266      * @param percent the percentage.
267      *
268      * @see #getOuterSeparatorExtension()
269      */

270     public void setOuterSeparatorExtension(double percent) {
271         this.outerSeparatorExtension = percent;
272         notifyListeners(new PlotChangeEvent(this));
273     }
274     
275     /**
276      * Returns the depth of each section, expressed as a percentage of the
277      * plot radius.
278      *
279      * @return The depth of each section.
280      *
281      * @see #setSectionDepth(double)
282      * @since 1.0.3
283      */

284     public double getSectionDepth() {
285         return this.sectionDepth;
286     }
287     
288     /**
289      * The section depth is given as percentage of the plot radius.
290      * Specifying 1.0 results in a straightforward pie chart.
291      *
292      * @param sectionDepth the section depth.
293      *
294      * @see #getSectionDepth()
295      * @since 1.0.3
296      */

297     public void setSectionDepth(double sectionDepth) {
298         this.sectionDepth = sectionDepth;
299     }
300
301     /**
302      * Initialises the plot state (which will store the total of all dataset
303      * values, among other things). This method is called once at the
304      * beginning of each drawing.
305      *
306      * @param g2 the graphics device.
307      * @param plotArea the plot area (<code>null</code> not permitted).
308      * @param plot the plot.
309      * @param index the secondary index (<code>null</code> for primary
310      * renderer).
311      * @param info collects chart rendering information for return to caller.
312      *
313      * @return A state object (maintains state information relevant to one
314      * chart drawing).
315      */

316     public PiePlotState initialise(Graphics2D JavaDoc g2, Rectangle2D JavaDoc plotArea,
317             PiePlot plot, Integer JavaDoc index, PlotRenderingInfo info) {
318
319         PiePlotState state = super.initialise(g2, plotArea, plot, index, info);
320         state.setPassesRequired(3);
321         return state;
322
323     }
324
325     /**
326      * Draws a single data item.
327      *
328      * @param g2 the graphics device (<code>null</code> not permitted).
329      * @param section the section index.
330      * @param dataArea the data plot area.
331      * @param state state information for one chart.
332      * @param currentPass the current pass index.
333      */

334     protected void drawItem(Graphics2D JavaDoc g2,
335                             int section,
336                             Rectangle2D JavaDoc dataArea,
337                             PiePlotState state,
338                             int currentPass) {
339     
340         PieDataset dataset = getDataset();
341         Number JavaDoc n = dataset.getValue(section);
342         if (n == null) {
343             return;
344         }
345         double value = n.doubleValue();
346         double angle1 = 0.0;
347         double angle2 = 0.0;
348         
349         Rotation direction = getDirection();
350         if (direction == Rotation.CLOCKWISE) {
351             angle1 = state.getLatestAngle();
352             angle2 = angle1 - value / state.getTotal() * 360.0;
353         }
354         else if (direction == Rotation.ANTICLOCKWISE) {
355             angle1 = state.getLatestAngle();
356             angle2 = angle1 + value / state.getTotal() * 360.0;
357         }
358         else {
359             throw new IllegalStateException JavaDoc("Rotation type not recognised.");
360         }
361         
362         double angle = (angle2 - angle1);
363         if (Math.abs(angle) > getMinimumArcAngleToDraw()) {
364             Comparable JavaDoc key = getSectionKey(section);
365             double ep = 0.0;
366             double mep = getMaximumExplodePercent();
367             if (mep > 0.0) {
368                 ep = getExplodePercent(key) / mep;
369             }
370             Rectangle2D JavaDoc arcBounds = getArcBounds(state.getPieArea(),
371                     state.getExplodedPieArea(), angle1, angle, ep);
372             Arc2D.Double JavaDoc arc = new Arc2D.Double JavaDoc(arcBounds, angle1, angle,
373                     Arc2D.OPEN);
374
375             // create the bounds for the inner arc
376
double depth = this.sectionDepth / 2.0;
377             RectangleInsets s = new RectangleInsets(UnitType.RELATIVE,
378                 depth, depth, depth, depth);
379             Rectangle2D JavaDoc innerArcBounds = new Rectangle2D.Double JavaDoc();
380             innerArcBounds.setRect(arcBounds);
381             s.trim(innerArcBounds);
382             // calculate inner arc in reverse direction, for later
383
// GeneralPath construction
384
Arc2D.Double JavaDoc arc2 = new Arc2D.Double JavaDoc(innerArcBounds, angle1
385                     + angle, -angle, Arc2D.OPEN);
386             GeneralPath JavaDoc path = new GeneralPath JavaDoc();
387             path.moveTo((float) arc.getStartPoint().getX(),
388                     (float) arc.getStartPoint().getY());
389             path.append(arc.getPathIterator(null), false);
390             path.append(arc2.getPathIterator(null), true);
391             path.closePath();
392             
393             Line2D JavaDoc separator = new Line2D.Double JavaDoc(arc2.getEndPoint(),
394                     arc.getStartPoint());
395             
396             if (currentPass == 0) {
397                 Paint JavaDoc shadowPaint = getShadowPaint();
398                 double shadowXOffset = getShadowXOffset();
399                 double shadowYOffset = getShadowYOffset();
400                 if (shadowPaint != null) {
401                     Shape JavaDoc shadowArc = ShapeUtilities.createTranslatedShape(
402                             path, (float) shadowXOffset, (float) shadowYOffset);
403                     g2.setPaint(shadowPaint);
404                     g2.fill(shadowArc);
405                 }
406             }
407             else if (currentPass == 1) {
408                 Paint JavaDoc paint = lookupSectionPaint(key, true);
409                 g2.setPaint(paint);
410                 g2.fill(path);
411                 Paint JavaDoc outlinePaint = lookupSectionOutlinePaint(key);
412                 Stroke JavaDoc outlineStroke = lookupSectionOutlineStroke(key);
413                 if (outlinePaint != null && outlineStroke != null) {
414                     g2.setPaint(outlinePaint);
415                     g2.setStroke(outlineStroke);
416                     g2.draw(path);
417                 }
418                 
419                 // add an entity for the pie section
420
if (state.getInfo() != null) {
421                     EntityCollection entities = state.getEntityCollection();
422                     if (entities != null) {
423                         String JavaDoc tip = null;
424                         PieToolTipGenerator toolTipGenerator
425                                 = getToolTipGenerator();
426                         if (toolTipGenerator != null) {
427                             tip = toolTipGenerator.generateToolTip(dataset,
428                                     key);
429                         }
430                         String JavaDoc url = null;
431                         PieURLGenerator urlGenerator = getURLGenerator();
432                         if (urlGenerator != null) {
433                             url = urlGenerator.generateURL(dataset, key,
434                                     getPieIndex());
435                         }
436                         PieSectionEntity entity = new PieSectionEntity(path,
437                                 dataset, getPieIndex(), section, key, tip,
438                                 url);
439                         entities.add(entity);
440                     }
441                 }
442             }
443             else if (currentPass == 2) {
444                 if (this.separatorsVisible) {
445                     Line2D JavaDoc extendedSeparator = extendLine(separator,
446                         this.innerSeparatorExtension,
447                         this.outerSeparatorExtension);
448                     g2.setStroke(this.separatorStroke);
449                     g2.setPaint(this.separatorPaint);
450                     g2.draw(extendedSeparator);
451                 }
452             }
453         }
454         state.setLatestAngle(angle2);
455     }
456
457     /**
458      * Tests this plot for equality with an arbitrary object.
459      *
460      * @param obj the object to test against (<code>null</code> permitted).
461      *
462      * @return A boolean.
463      */

464     public boolean equals(Object JavaDoc obj) {
465         if (this == obj) {
466             return true;
467         }
468         if (!(obj instanceof RingPlot)) {
469             return false;
470         }
471         RingPlot that = (RingPlot) obj;
472         if (this.separatorsVisible != that.separatorsVisible) {
473             return false;
474         }
475         if (!ObjectUtilities.equal(this.separatorStroke,
476                 that.separatorStroke)) {
477             return false;
478         }
479         if (!PaintUtilities.equal(this.separatorPaint, that.separatorPaint)) {
480             return false;
481         }
482         if (this.innerSeparatorExtension != that.innerSeparatorExtension) {
483             return false;
484         }
485         if (this.outerSeparatorExtension != that.outerSeparatorExtension) {
486             return false;
487         }
488         if (this.sectionDepth != that.sectionDepth) {
489             return false;
490         }
491         return super.equals(obj);
492     }
493     
494     /**
495      * Creates a new line by extending an existing line.
496      *
497      * @param line the line (<code>null</code> not permitted).
498      * @param startPercent the amount to extend the line at the start point
499      * end.
500      * @param endPercent the amount to extend the line at the end point end.
501      *
502      * @return A new line.
503      */

504     private Line2D JavaDoc extendLine(Line2D JavaDoc line, double startPercent,
505                               double endPercent) {
506         if (line == null) {
507             throw new IllegalArgumentException JavaDoc("Null 'line' argument.");
508         }
509         double x1 = line.getX1();
510         double x2 = line.getX2();
511         double deltaX = x2 - x1;
512         double y1 = line.getY1();
513         double y2 = line.getY2();
514         double deltaY = y2 - y1;
515         x1 = x1 - (startPercent * deltaX);
516         y1 = y1 - (startPercent * deltaY);
517         x2 = x2 + (endPercent * deltaX);
518         y2 = y2 + (endPercent * deltaY);
519         return new Line2D.Double JavaDoc(x1, y1, x2, y2);
520     }
521     
522     /**
523      * Provides serialization support.
524      *
525      * @param stream the output stream.
526      *
527      * @throws IOException if there is an I/O error.
528      */

529     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
530         stream.defaultWriteObject();
531         SerialUtilities.writeStroke(this.separatorStroke, stream);
532         SerialUtilities.writePaint(this.separatorPaint, stream);
533     }
534
535     /**
536      * Provides serialization support.
537      *
538      * @param stream the input stream.
539      *
540      * @throws IOException if there is an I/O error.
541      * @throws ClassNotFoundException if there is a classpath problem.
542      */

543     private void readObject(ObjectInputStream JavaDoc stream)
544         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
545         stream.defaultReadObject();
546         this.separatorStroke = SerialUtilities.readStroke(stream);
547         this.separatorPaint = SerialUtilities.readPaint(stream);
548     }
549     
550 }
551
Popular Tags