001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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 * StackedBarRenderer3D.java 029 * ------------------------- 030 * (C) Copyright 2000-2005, by Serge V. Grachov and Contributors. 031 * 032 * Original Author: Serge V. Grachov; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Richard Atkinson; 035 * Christian W. Zuckschwerdt; 036 * 037 * $Id: StackedBarRenderer3D.java,v 1.8.2.3 2005/10/25 20:54:16 mungady Exp $ 038 * 039 * Changes 040 * ------- 041 * 31-Oct-2001 : Version 1, contributed by Serge V. Grachov (DG); 042 * 15-Nov-2001 : Modified to allow for null data values (DG); 043 * 13-Dec-2001 : Added tooltips (DG); 044 * 15-Feb-2002 : Added isStacked() method (DG); 045 * 24-May-2002 : Incorporated tooltips into chart entities (DG); 046 * 19-Jun-2002 : Added check for null info in drawCategoryItem method (DG); 047 * 25-Jun-2002 : Removed redundant imports (DG); 048 * 26-Jun-2002 : Small change to entity (DG); 049 * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs 050 * for HTML image maps (RA); 051 * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG); 052 * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 053 * CategoryToolTipGenerator interface (DG); 054 * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG); 055 * 26-Nov-2002 : Replaced isStacked() method with getRangeType() method (DG); 056 * 17-Jan-2003 : Moved plot classes to a separate package (DG); 057 * 25-Mar-2003 : Implemented Serializable (DG); 058 * 01-May-2003 : Added default constructor (bug 726235) and fixed bug 059 * 726260) (DG); 060 * 13-May-2003 : Renamed StackedVerticalBarRenderer3D 061 * --> StackedBarRenderer3D (DG); 062 * 30-Jul-2003 : Modified entity constructor (CZ); 063 * 07-Oct-2003 : Added renderer state (DG); 064 * 21-Nov-2003 : Added a new constructor (DG); 065 * 27-Nov-2003 : Modified code to respect maxBarWidth setting (DG); 066 * 11-Aug-2004 : Fixed bug where isDrawBarOutline() was ignored (DG); 067 * 05-Nov-2004 : Modified drawItem() signature (DG); 068 * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds (DG); 069 * 18-Mar-2005 : Override for getPassCount() method (DG); 070 * 20-Apr-2005 : Renamed CategoryLabelGenerator 071 * --> CategoryItemLabelGenerator (DG); 072 * 09-Jun-2005 : Use addItemEntity() method from superclass (DG); 073 * 22-Sep-2005 : Renamed getMaxBarWidth() --> getMaximumBarWidth() (DG); 074 * 075 */ 076 077 package org.jfree.chart.renderer.category; 078 079 import java.awt.Color; 080 import java.awt.Graphics2D; 081 import java.awt.Paint; 082 import java.awt.geom.GeneralPath; 083 import java.awt.geom.Rectangle2D; 084 import java.io.Serializable; 085 086 import org.jfree.chart.axis.CategoryAxis; 087 import org.jfree.chart.axis.ValueAxis; 088 import org.jfree.chart.entity.EntityCollection; 089 import org.jfree.chart.labels.CategoryItemLabelGenerator; 090 import org.jfree.chart.plot.CategoryPlot; 091 import org.jfree.chart.plot.PlotOrientation; 092 import org.jfree.data.Range; 093 import org.jfree.data.category.CategoryDataset; 094 import org.jfree.data.general.DatasetUtilities; 095 import org.jfree.ui.RectangleEdge; 096 import org.jfree.util.PublicCloneable; 097 098 /** 099 * Renders stacked bars with 3D-effect, for use with the 100 * {@link org.jfree.chart.plot.CategoryPlot} class. 101 */ 102 public class StackedBarRenderer3D extends BarRenderer3D 103 implements Cloneable, PublicCloneable, 104 Serializable { 105 106 /** For serialization. */ 107 private static final long serialVersionUID = -5832945916493247123L; 108 109 /** 110 * Creates a new renderer with no tool tip generator and no URL generator. 111 * <P> 112 * The defaults (no tool tip or URL generators) have been chosen to 113 * minimise the processing required to generate a default chart. If you 114 * require tool tips or URLs, then you can easily add the required 115 * generators. 116 */ 117 public StackedBarRenderer3D() { 118 super(); 119 } 120 121 /** 122 * Constructs a new renderer with the specified '3D effect'. 123 * 124 * @param xOffset the x-offset for the 3D effect. 125 * @param yOffset the y-offset for the 3D effect. 126 */ 127 public StackedBarRenderer3D(double xOffset, double yOffset) { 128 super(xOffset, yOffset); 129 } 130 131 /** 132 * Returns the range of values the renderer requires to display all the 133 * items from the specified dataset. 134 * 135 * @param dataset the dataset (<code>null</code> not permitted). 136 * 137 * @return The range (or <code>null</code> if the dataset is empty). 138 */ 139 public Range findRangeBounds(CategoryDataset dataset) { 140 return DatasetUtilities.findStackedRangeBounds(dataset); 141 } 142 143 /** 144 * Calculates the bar width and stores it in the renderer state. 145 * 146 * @param plot the plot. 147 * @param dataArea the data area. 148 * @param rendererIndex the renderer index. 149 * @param state the renderer state. 150 */ 151 protected void calculateBarWidth(CategoryPlot plot, 152 Rectangle2D dataArea, 153 int rendererIndex, 154 CategoryItemRendererState state) { 155 156 // calculate the bar width 157 CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); 158 CategoryDataset data = plot.getDataset(rendererIndex); 159 if (data != null) { 160 PlotOrientation orientation = plot.getOrientation(); 161 double space = 0.0; 162 if (orientation == PlotOrientation.HORIZONTAL) { 163 space = dataArea.getHeight(); 164 } 165 else if (orientation == PlotOrientation.VERTICAL) { 166 space = dataArea.getWidth(); 167 } 168 double maxWidth = space * getMaximumBarWidth(); 169 int columns = data.getColumnCount(); 170 double categoryMargin = 0.0; 171 if (columns > 1) { 172 categoryMargin = domainAxis.getCategoryMargin(); 173 } 174 175 double used = space * (1 - domainAxis.getLowerMargin() 176 - domainAxis.getUpperMargin() 177 - categoryMargin); 178 if (columns > 0) { 179 state.setBarWidth(Math.min(used / columns, maxWidth)); 180 } 181 else { 182 state.setBarWidth(Math.min(used, maxWidth)); 183 } 184 } 185 186 } 187 188 /** 189 * Draws a stacked bar (with 3D-effect) for a specific item. 190 * 191 * @param g2 the graphics device. 192 * @param state the renderer state. 193 * @param dataArea the plot area. 194 * @param plot the plot. 195 * @param domainAxis the domain (category) axis. 196 * @param rangeAxis the range (value) axis. 197 * @param dataset the data. 198 * @param row the row index (zero-based). 199 * @param column the column index (zero-based). 200 * @param pass the pass index. 201 */ 202 public void drawItem(Graphics2D g2, 203 CategoryItemRendererState state, 204 Rectangle2D dataArea, 205 CategoryPlot plot, 206 CategoryAxis domainAxis, 207 ValueAxis rangeAxis, 208 CategoryDataset dataset, 209 int row, 210 int column, 211 int pass) { 212 213 // check the value we are plotting... 214 Number dataValue = dataset.getValue(row, column); 215 if (dataValue == null) { 216 return; 217 } 218 219 double value = dataValue.doubleValue(); 220 221 Rectangle2D adjusted = new Rectangle2D.Double( 222 dataArea.getX(), dataArea.getY() + getYOffset(), 223 dataArea.getWidth() - getXOffset(), 224 dataArea.getHeight() - getYOffset() 225 ); 226 227 PlotOrientation orientation = plot.getOrientation(); 228 229 double barW0 = domainAxis.getCategoryMiddle( 230 column, getColumnCount(), adjusted, plot.getDomainAxisEdge() 231 ) - state.getBarWidth() / 2.0; 232 233 double positiveBase = getBase(); 234 double negativeBase = positiveBase; 235 for (int i = 0; i < row; i++) { 236 Number v = dataset.getValue(i, column); 237 if (v != null) { 238 double d = v.doubleValue(); 239 if (d > 0) { 240 positiveBase = positiveBase + d; 241 } 242 else { 243 negativeBase = negativeBase + d; 244 } 245 } 246 } 247 248 double translatedBase; 249 double translatedValue; 250 RectangleEdge location = plot.getRangeAxisEdge(); 251 if (value > 0.0) { 252 translatedBase = rangeAxis.valueToJava2D(positiveBase, adjusted, 253 location); 254 translatedValue = rangeAxis.valueToJava2D(positiveBase + value, 255 adjusted, location); 256 } 257 else { 258 translatedBase = rangeAxis.valueToJava2D(negativeBase, adjusted, 259 location); 260 translatedValue = rangeAxis.valueToJava2D(negativeBase + value, 261 adjusted, location); 262 } 263 double barL0 = Math.min(translatedBase, translatedValue); 264 double barLength = Math.max( 265 Math.abs(translatedValue - translatedBase), getMinimumBarLength() 266 ); 267 268 Rectangle2D bar = null; 269 if (orientation == PlotOrientation.HORIZONTAL) { 270 bar = new Rectangle2D.Double(barL0, barW0, barLength, 271 state.getBarWidth()); 272 } 273 else { 274 bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(), 275 barLength); 276 } 277 Paint itemPaint = getItemPaint(row, column); 278 g2.setPaint(itemPaint); 279 g2.fill(bar); 280 281 if (pass == 0) { 282 double x0 = bar.getMinX(); 283 double x1 = x0 + getXOffset(); 284 double x2 = bar.getMaxX(); 285 double x3 = x2 + getXOffset(); 286 287 double y0 = bar.getMinY() - getYOffset(); 288 double y1 = bar.getMinY(); 289 double y2 = bar.getMaxY() - getYOffset(); 290 double y3 = bar.getMaxY(); 291 292 GeneralPath bar3dRight = null; 293 GeneralPath bar3dTop = null; 294 if (value > 0.0 || orientation == PlotOrientation.VERTICAL) { 295 bar3dRight = new GeneralPath(); 296 bar3dRight.moveTo((float) x2, (float) y3); 297 bar3dRight.lineTo((float) x2, (float) y1); 298 bar3dRight.lineTo((float) x3, (float) y0); 299 bar3dRight.lineTo((float) x3, (float) y2); 300 bar3dRight.closePath(); 301 302 if (itemPaint instanceof Color) { 303 g2.setPaint(((Color) itemPaint).darker()); 304 } 305 g2.fill(bar3dRight); 306 } 307 308 if (value > 0.0 || orientation == PlotOrientation.HORIZONTAL) { 309 bar3dTop = new GeneralPath(); 310 bar3dTop.moveTo((float) x0, (float) y1); 311 bar3dTop.lineTo((float) x1, (float) y0); 312 bar3dTop.lineTo((float) x3, (float) y0); 313 bar3dTop.lineTo((float) x2, (float) y1); 314 bar3dTop.closePath(); 315 g2.fill(bar3dTop); 316 } 317 318 if (isDrawBarOutline() && state.getBarWidth() > 3) { 319 g2.setStroke(getItemOutlineStroke(row, column)); 320 g2.setPaint(getItemOutlinePaint(row, column)); 321 g2.draw(bar); 322 if (bar3dRight != null) { 323 g2.draw(bar3dRight); 324 } 325 if (bar3dTop != null) { 326 g2.draw(bar3dTop); 327 } 328 } 329 330 // add an item entity, if this information is being collected 331 EntityCollection entities = state.getEntityCollection(); 332 if (entities != null) { 333 addItemEntity(entities, dataset, row, column, bar); 334 } 335 } 336 else if (pass == 1) { 337 CategoryItemLabelGenerator generator 338 = getItemLabelGenerator(row, column); 339 if (generator != null && isItemLabelVisible(row, column)) { 340 drawItemLabel( 341 g2, dataset, row, column, plot, generator, bar, 342 (value < 0.0) 343 ); 344 } 345 } 346 347 } 348 349 /** 350 * Returns the number of passes through the dataset required by the 351 * renderer. This method returns <code>2</code>, the second pass is used 352 * to draw the item labels. 353 * 354 * @return The pass count. 355 */ 356 public int getPassCount() { 357 return 2; 358 } 359 360 }