KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > renderer > category > StackedBarRenderer3D


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  * StackedBarRenderer3D.java
29  * -------------------------
30  * (C) Copyright 2000-2006, by Serge V. Grachov and Contributors.
31  *
32  * Original Author: Serge V. Grachov;
33  * Contributor(s): David Gilbert (for Object Refinery Limited);
34  * Richard Atkinson;
35  * Christian W. Zuckschwerdt;
36  * Max Herfort (patch 1459313);
37  *
38  * $Id: StackedBarRenderer3D.java,v 1.8.2.4 2006/04/05 10:38:21 mungady Exp $
39  *
40  * Changes
41  * -------
42  * 31-Oct-2001 : Version 1, contributed by Serge V. Grachov (DG);
43  * 15-Nov-2001 : Modified to allow for null data values (DG);
44  * 13-Dec-2001 : Added tooltips (DG);
45  * 15-Feb-2002 : Added isStacked() method (DG);
46  * 24-May-2002 : Incorporated tooltips into chart entities (DG);
47  * 19-Jun-2002 : Added check for null info in drawCategoryItem method (DG);
48  * 25-Jun-2002 : Removed redundant imports (DG);
49  * 26-Jun-2002 : Small change to entity (DG);
50  * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs
51  * for HTML image maps (RA);
52  * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
53  * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
54  * CategoryToolTipGenerator interface (DG);
55  * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
56  * 26-Nov-2002 : Replaced isStacked() method with getRangeType() method (DG);
57  * 17-Jan-2003 : Moved plot classes to a separate package (DG);
58  * 25-Mar-2003 : Implemented Serializable (DG);
59  * 01-May-2003 : Added default constructor (bug 726235) and fixed bug
60  * 726260) (DG);
61  * 13-May-2003 : Renamed StackedVerticalBarRenderer3D
62  * --> StackedBarRenderer3D (DG);
63  * 30-Jul-2003 : Modified entity constructor (CZ);
64  * 07-Oct-2003 : Added renderer state (DG);
65  * 21-Nov-2003 : Added a new constructor (DG);
66  * 27-Nov-2003 : Modified code to respect maxBarWidth setting (DG);
67  * 11-Aug-2004 : Fixed bug where isDrawBarOutline() was ignored (DG);
68  * 05-Nov-2004 : Modified drawItem() signature (DG);
69  * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds (DG);
70  * 18-Mar-2005 : Override for getPassCount() method (DG);
71  * 20-Apr-2005 : Renamed CategoryLabelGenerator
72  * --> CategoryItemLabelGenerator (DG);
73  * 09-Jun-2005 : Use addItemEntity() method from superclass (DG);
74  * 22-Sep-2005 : Renamed getMaxBarWidth() --> getMaximumBarWidth() (DG);
75  * ------------- JFREECHART 1.0.0 ---------------------------------------------
76  * 31-Mar-2006 : Added renderAsPercentages option - see patch 1459313 submitted
77  * by Max Herfort (DG);
78  *
79  */

80
81 package org.jfree.chart.renderer.category;
82
83 import java.awt.Color JavaDoc;
84 import java.awt.Graphics2D JavaDoc;
85 import java.awt.Paint JavaDoc;
86 import java.awt.geom.GeneralPath JavaDoc;
87 import java.awt.geom.Rectangle2D JavaDoc;
88 import java.io.Serializable JavaDoc;
89
90 import org.jfree.chart.axis.CategoryAxis;
91 import org.jfree.chart.axis.ValueAxis;
92 import org.jfree.chart.entity.EntityCollection;
93 import org.jfree.chart.event.RendererChangeEvent;
94 import org.jfree.chart.labels.CategoryItemLabelGenerator;
95 import org.jfree.chart.plot.CategoryPlot;
96 import org.jfree.chart.plot.PlotOrientation;
97 import org.jfree.data.DataUtilities;
98 import org.jfree.data.Range;
99 import org.jfree.data.category.CategoryDataset;
100 import org.jfree.data.general.DatasetUtilities;
101 import org.jfree.ui.RectangleEdge;
102 import org.jfree.util.PublicCloneable;
103
104 /**
105  * Renders stacked bars with 3D-effect, for use with the
106  * {@link org.jfree.chart.plot.CategoryPlot} class.
107  */

108 public class StackedBarRenderer3D extends BarRenderer3D
109                                   implements Cloneable JavaDoc, PublicCloneable,
110                                              Serializable JavaDoc {
111
112     /** For serialization. */
113     private static final long serialVersionUID = -5832945916493247123L;
114     
115     /** A flag that controls whether the bars display values or percentages. */
116     private boolean renderAsPercentages;
117     
118     /**
119      * Creates a new renderer with no tool tip generator and no URL generator.
120      * <P>
121      * The defaults (no tool tip or URL generators) have been chosen to
122      * minimise the processing required to generate a default chart. If you
123      * require tool tips or URLs, then you can easily add the required
124      * generators.
125      */

126     public StackedBarRenderer3D() {
127         this(false);
128     }
129
130     /**
131      * Constructs a new renderer with the specified '3D effect'.
132      *
133      * @param xOffset the x-offset for the 3D effect.
134      * @param yOffset the y-offset for the 3D effect.
135      */

136     public StackedBarRenderer3D(double xOffset, double yOffset) {
137         super(xOffset, yOffset);
138     }
139     
140     /**
141      * Creates a new renderer.
142      *
143      * @param renderAsPercentages a flag that controls whether the data values
144      * are rendered as percentages.
145      *
146      * @since 1.0.2
147      */

148     public StackedBarRenderer3D(boolean renderAsPercentages) {
149         super();
150         this.renderAsPercentages = renderAsPercentages;
151     }
152     
153     /**
154      * Constructs a new renderer with the specified '3D effect'.
155      *
156      * @param xOffset the x-offset for the 3D effect.
157      * @param yOffset the y-offset for the 3D effect.
158      * @param renderAsPercentages a flag that controls whether the data values
159      * are rendered as percentages.
160      *
161      * @since 1.0.2
162      */

163     public StackedBarRenderer3D(double xOffset, double yOffset,
164             boolean renderAsPercentages) {
165         super(xOffset, yOffset);
166         this.renderAsPercentages = renderAsPercentages;
167     }
168     
169     /**
170      * Returns <code>true</code> if the renderer displays each item value as
171      * a percentage (so that the stacked bars add to 100%), and
172      * <code>false</code> otherwise.
173      *
174      * @return A boolean.
175      *
176      * @since 1.0.2
177      */

178     public boolean getRenderAsPercentages() {
179         return this.renderAsPercentages;
180     }
181     
182     /**
183      * Sets the flag that controls whether the renderer displays each item
184      * value as a percentage (so that the stacked bars add to 100%), and sends
185      * a {@link RendererChangeEvent} to all registered listeners.
186      *
187      * @param asPercentages the flag.
188      *
189      * @since 1.0.2
190      */

191     public void setRenderAsPercentages(boolean asPercentages) {
192         this.renderAsPercentages = asPercentages;
193         notifyListeners(new RendererChangeEvent(this));
194     }
195
196     /**
197      * Returns the range of values the renderer requires to display all the
198      * items from the specified dataset.
199      *
200      * @param dataset the dataset (<code>null</code> not permitted).
201      *
202      * @return The range (or <code>null</code> if the dataset is empty).
203      */

204     public Range findRangeBounds(CategoryDataset dataset) {
205         if (this.renderAsPercentages) {
206             return new Range(0.0, 1.0);
207         }
208         else {
209             return DatasetUtilities.findStackedRangeBounds(dataset);
210         }
211     }
212
213     /**
214      * Calculates the bar width and stores it in the renderer state.
215      *
216      * @param plot the plot.
217      * @param dataArea the data area.
218      * @param rendererIndex the renderer index.
219      * @param state the renderer state.
220      */

221     protected void calculateBarWidth(CategoryPlot plot,
222                                      Rectangle2D JavaDoc dataArea,
223                                      int rendererIndex,
224                                      CategoryItemRendererState state) {
225
226         // calculate the bar width
227
CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
228         CategoryDataset data = plot.getDataset(rendererIndex);
229         if (data != null) {
230             PlotOrientation orientation = plot.getOrientation();
231             double space = 0.0;
232             if (orientation == PlotOrientation.HORIZONTAL) {
233                 space = dataArea.getHeight();
234             }
235             else if (orientation == PlotOrientation.VERTICAL) {
236                 space = dataArea.getWidth();
237             }
238             double maxWidth = space * getMaximumBarWidth();
239             int columns = data.getColumnCount();
240             double categoryMargin = 0.0;
241             if (columns > 1) {
242                 categoryMargin = domainAxis.getCategoryMargin();
243             }
244
245             double used = space * (1 - domainAxis.getLowerMargin()
246                                      - domainAxis.getUpperMargin()
247                                      - categoryMargin);
248             if (columns > 0) {
249                 state.setBarWidth(Math.min(used / columns, maxWidth));
250             }
251             else {
252                 state.setBarWidth(Math.min(used, maxWidth));
253             }
254         }
255
256     }
257     
258     /**
259      * Draws a stacked bar (with 3D-effect) for a specific item.
260      *
261      * @param g2 the graphics device.
262      * @param state the renderer state.
263      * @param dataArea the plot area.
264      * @param plot the plot.
265      * @param domainAxis the domain (category) axis.
266      * @param rangeAxis the range (value) axis.
267      * @param dataset the data.
268      * @param row the row index (zero-based).
269      * @param column the column index (zero-based).
270      * @param pass the pass index.
271      */

272     public void drawItem(Graphics2D JavaDoc g2,
273                          CategoryItemRendererState state,
274                          Rectangle2D JavaDoc dataArea,
275                          CategoryPlot plot,
276                          CategoryAxis domainAxis,
277                          ValueAxis rangeAxis,
278                          CategoryDataset dataset,
279                          int row,
280                          int column,
281                          int pass) {
282
283         // check the value we are plotting...
284
Number JavaDoc dataValue = dataset.getValue(row, column);
285         if (dataValue == null) {
286             return;
287         }
288         
289         double value = dataValue.doubleValue();
290         double total = 0.0; // only needed if calculating percentages
291
if (this.renderAsPercentages) {
292             total = DataUtilities.calculateColumnTotal(dataset, column);
293             value = value / total;
294         }
295         
296         Rectangle2D JavaDoc adjusted = new Rectangle2D.Double JavaDoc(
297             dataArea.getX(), dataArea.getY() + getYOffset(),
298             dataArea.getWidth() - getXOffset(),
299             dataArea.getHeight() - getYOffset()
300         );
301
302         PlotOrientation orientation = plot.getOrientation();
303
304         double barW0 = domainAxis.getCategoryMiddle(
305             column, getColumnCount(), adjusted, plot.getDomainAxisEdge()
306         ) - state.getBarWidth() / 2.0;
307
308         double positiveBase = getBase();
309         double negativeBase = positiveBase;
310         for (int i = 0; i < row; i++) {
311             Number JavaDoc v = dataset.getValue(i, column);
312             if (v != null) {
313                 double d = v.doubleValue();
314                 if (this.renderAsPercentages) {
315                     d = d / total;
316                 }
317                 if (d > 0) {
318                     positiveBase = positiveBase + d;
319                 }
320                 else {
321                     negativeBase = negativeBase + d;
322                 }
323             }
324         }
325
326         double translatedBase;
327         double translatedValue;
328         RectangleEdge location = plot.getRangeAxisEdge();
329         if (value > 0.0) {
330             translatedBase = rangeAxis.valueToJava2D(positiveBase, adjusted,
331                     location);
332             translatedValue = rangeAxis.valueToJava2D(positiveBase + value,
333                     adjusted, location);
334         }
335         else {
336             translatedBase = rangeAxis.valueToJava2D(negativeBase, adjusted,
337                     location);
338             translatedValue = rangeAxis.valueToJava2D(negativeBase + value,
339                     adjusted, location);
340         }
341         double barL0 = Math.min(translatedBase, translatedValue);
342         double barLength = Math.max(
343             Math.abs(translatedValue - translatedBase), getMinimumBarLength()
344         );
345
346         Rectangle2D JavaDoc bar = null;
347         if (orientation == PlotOrientation.HORIZONTAL) {
348             bar = new Rectangle2D.Double JavaDoc(barL0, barW0, barLength,
349                     state.getBarWidth());
350         }
351         else {
352             bar = new Rectangle2D.Double JavaDoc(barW0, barL0, state.getBarWidth(),
353                     barLength);
354         }
355         Paint JavaDoc itemPaint = getItemPaint(row, column);
356         g2.setPaint(itemPaint);
357         g2.fill(bar);
358
359         if (pass == 0) {
360             double x0 = bar.getMinX();
361             double x1 = x0 + getXOffset();
362             double x2 = bar.getMaxX();
363             double x3 = x2 + getXOffset();
364         
365             double y0 = bar.getMinY() - getYOffset();
366             double y1 = bar.getMinY();
367             double y2 = bar.getMaxY() - getYOffset();
368             double y3 = bar.getMaxY();
369         
370             GeneralPath JavaDoc bar3dRight = null;
371             GeneralPath JavaDoc bar3dTop = null;
372             if (value > 0.0 || orientation == PlotOrientation.VERTICAL) {
373                 bar3dRight = new GeneralPath JavaDoc();
374                 bar3dRight.moveTo((float) x2, (float) y3);
375                 bar3dRight.lineTo((float) x2, (float) y1);
376                 bar3dRight.lineTo((float) x3, (float) y0);
377                 bar3dRight.lineTo((float) x3, (float) y2);
378                 bar3dRight.closePath();
379
380                 if (itemPaint instanceof Color JavaDoc) {
381                     g2.setPaint(((Color JavaDoc) itemPaint).darker());
382                 }
383                 g2.fill(bar3dRight);
384             }
385
386             if (value > 0.0 || orientation == PlotOrientation.HORIZONTAL) {
387                 bar3dTop = new GeneralPath JavaDoc();
388                 bar3dTop.moveTo((float) x0, (float) y1);
389                 bar3dTop.lineTo((float) x1, (float) y0);
390                 bar3dTop.lineTo((float) x3, (float) y0);
391                 bar3dTop.lineTo((float) x2, (float) y1);
392                 bar3dTop.closePath();
393                 g2.fill(bar3dTop);
394             }
395
396             if (isDrawBarOutline() && state.getBarWidth() > 3) {
397                 g2.setStroke(getItemOutlineStroke(row, column));
398                 g2.setPaint(getItemOutlinePaint(row, column));
399                 g2.draw(bar);
400                 if (bar3dRight != null) {
401                     g2.draw(bar3dRight);
402                 }
403                 if (bar3dTop != null) {
404                     g2.draw(bar3dTop);
405                 }
406             }
407
408             // add an item entity, if this information is being collected
409
EntityCollection entities = state.getEntityCollection();
410             if (entities != null) {
411                 addItemEntity(entities, dataset, row, column, bar);
412             }
413         }
414         else if (pass == 1) {
415             CategoryItemLabelGenerator generator
416                 = getItemLabelGenerator(row, column);
417             if (generator != null && isItemLabelVisible(row, column)) {
418                 drawItemLabel(
419                     g2, dataset, row, column, plot, generator, bar,
420                     (value < 0.0)
421                 );
422             }
423         }
424
425     }
426     
427     /**
428      * Returns the number of passes through the dataset required by the
429      * renderer. This method returns <code>2</code>, the second pass is used
430      * to draw the item labels.
431      *
432      * @return The pass count.
433      */

434     public int getPassCount() {
435         return 2;
436     }
437     
438     /**
439      * Tests this renderer for equality with an arbitrary object.
440      *
441      * @param obj the object (<code>null</code> permitted).
442      *
443      * @return A boolean.
444      */

445     public boolean equals(Object JavaDoc obj) {
446         if (obj == this) {
447             return true;
448         }
449         if (!(obj instanceof StackedBarRenderer3D)) {
450             return false;
451         }
452         if (!super.equals(obj)) {
453             return false;
454         }
455         StackedBarRenderer3D that = (StackedBarRenderer3D) obj;
456         if (this.renderAsPercentages != that.renderAsPercentages) {
457             return false;
458         }
459         return true;
460     }
461
462 }
463
Popular Tags