001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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 * LayeredBarRenderer.java 029 * ----------------------- 030 * (C) Copyright 2003-2008, by Arnaud Lelievre and Contributors. 031 * 032 * Original Author: Arnaud Lelievre (for Garden); 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Zoheb Borbora; 035 * 036 * Changes 037 * ------- 038 * 28-Aug-2003 : Version 1 (AL); 039 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 040 * 07-Oct-2003 : Added renderer state (DG); 041 * 21-Oct-2003 : Bar width moved to renderer state (DG); 042 * 05-Nov-2004 : Modified drawItem() signature (DG); 043 * 20-Apr-2005 : Renamed CategoryLabelGenerator 044 * --> CategoryItemLabelGenerator (DG); 045 * 17-Nov-2005 : Added support for gradient paint (DG); 046 * ------------- JFREECHART 1.0.x --------------------------------------------- 047 * 18-Aug-2006 : Fixed the bar width calculation to respect the maximum bar 048 * width setting (thanks to Zoheb Borbora) (DG); 049 * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG); 050 * 051 */ 052 053 package org.jfree.chart.renderer.category; 054 055 import java.awt.GradientPaint; 056 import java.awt.Graphics2D; 057 import java.awt.Paint; 058 import java.awt.Stroke; 059 import java.awt.geom.Rectangle2D; 060 import java.io.Serializable; 061 062 import org.jfree.chart.axis.CategoryAxis; 063 import org.jfree.chart.axis.ValueAxis; 064 import org.jfree.chart.entity.EntityCollection; 065 import org.jfree.chart.labels.CategoryItemLabelGenerator; 066 import org.jfree.chart.plot.CategoryPlot; 067 import org.jfree.chart.plot.PlotOrientation; 068 import org.jfree.data.category.CategoryDataset; 069 import org.jfree.ui.GradientPaintTransformer; 070 import org.jfree.ui.RectangleEdge; 071 import org.jfree.util.ObjectList; 072 073 /** 074 * A {@link CategoryItemRenderer} that represents data using bars which are 075 * superimposed. The example shown here is generated by the 076 * <code>LayeredBarChartDemo1.java</code> program included in the JFreeChart 077 * Demo Collection: 078 * <br><br> 079 * <img src="../../../../../images/LayeredBarRendererSample.png" 080 * alt="LayeredBarRendererSample.png" /> 081 */ 082 public class LayeredBarRenderer extends BarRenderer implements Serializable { 083 084 /** For serialization. */ 085 private static final long serialVersionUID = -8716572894780469487L; 086 087 /** A list of the width of each series bar. */ 088 protected ObjectList seriesBarWidthList; 089 090 /** 091 * Default constructor. 092 */ 093 public LayeredBarRenderer() { 094 super(); 095 this.seriesBarWidthList = new ObjectList(); 096 } 097 098 /** 099 * Returns the bar width for a series, or <code>Double.NaN</code> if no 100 * width has been set. 101 * 102 * @param series the series index (zero based). 103 * 104 * @return The width for the series (1.0=100%, it is the maximum). 105 */ 106 public double getSeriesBarWidth(int series) { 107 double result = Double.NaN; 108 Number n = (Number) this.seriesBarWidthList.get(series); 109 if (n != null) { 110 result = n.doubleValue(); 111 } 112 return result; 113 } 114 115 /** 116 * Sets the width of the bars of a series. 117 * 118 * @param series the series index (zero based). 119 * @param width the width of the series bar in percentage (1.0=100%, it is 120 * the maximum). 121 */ 122 public void setSeriesBarWidth(int series, double width) { 123 this.seriesBarWidthList.set(series, new Double(width)); 124 } 125 126 /** 127 * Calculates the bar width and stores it in the renderer state. 128 * 129 * @param plot the plot. 130 * @param dataArea the data area. 131 * @param rendererIndex the renderer index. 132 * @param state the renderer state. 133 */ 134 protected void calculateBarWidth(CategoryPlot plot, 135 Rectangle2D dataArea, 136 int rendererIndex, 137 CategoryItemRendererState state) { 138 139 // calculate the bar width - this calculation differs from the 140 // BarRenderer calculation because the bars are layered on top of one 141 // another, so there is effectively only one bar per category for 142 // the purpose of the bar width calculation 143 CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); 144 CategoryDataset dataset = plot.getDataset(rendererIndex); 145 if (dataset != null) { 146 int columns = dataset.getColumnCount(); 147 int rows = dataset.getRowCount(); 148 double space = 0.0; 149 PlotOrientation orientation = plot.getOrientation(); 150 if (orientation == PlotOrientation.HORIZONTAL) { 151 space = dataArea.getHeight(); 152 } 153 else if (orientation == PlotOrientation.VERTICAL) { 154 space = dataArea.getWidth(); 155 } 156 double maxWidth = space * getMaximumBarWidth(); 157 double categoryMargin = 0.0; 158 if (columns > 1) { 159 categoryMargin = domainAxis.getCategoryMargin(); 160 } 161 double used = space * (1 - domainAxis.getLowerMargin() 162 - domainAxis.getUpperMargin() - categoryMargin); 163 if ((rows * columns) > 0) { 164 state.setBarWidth(Math.min(used / (dataset.getColumnCount()), 165 maxWidth)); 166 } 167 else { 168 state.setBarWidth(Math.min(used, maxWidth)); 169 } 170 } 171 } 172 173 /** 174 * Draws the bar for one item in the dataset. 175 * 176 * @param g2 the graphics device. 177 * @param state the renderer state. 178 * @param dataArea the plot area. 179 * @param plot the plot. 180 * @param domainAxis the domain (category) axis. 181 * @param rangeAxis the range (value) axis. 182 * @param data the data. 183 * @param row the row index (zero-based). 184 * @param column the column index (zero-based). 185 * @param pass the pass index. 186 */ 187 public void drawItem(Graphics2D g2, 188 CategoryItemRendererState state, 189 Rectangle2D dataArea, 190 CategoryPlot plot, 191 CategoryAxis domainAxis, 192 ValueAxis rangeAxis, 193 CategoryDataset data, 194 int row, 195 int column, 196 int pass) { 197 198 PlotOrientation orientation = plot.getOrientation(); 199 if (orientation == PlotOrientation.HORIZONTAL) { 200 drawHorizontalItem(g2, state, dataArea, plot, domainAxis, 201 rangeAxis, data, row, column); 202 } 203 else if (orientation == PlotOrientation.VERTICAL) { 204 drawVerticalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, 205 data, row, column); 206 } 207 208 } 209 210 /** 211 * Draws the bar for a single (series, category) data item. 212 * 213 * @param g2 the graphics device. 214 * @param state the renderer state. 215 * @param dataArea the data area. 216 * @param plot the plot. 217 * @param domainAxis the domain axis. 218 * @param rangeAxis the range axis. 219 * @param dataset the dataset. 220 * @param row the row index (zero-based). 221 * @param column the column index (zero-based). 222 */ 223 protected void drawHorizontalItem(Graphics2D g2, 224 CategoryItemRendererState state, 225 Rectangle2D dataArea, 226 CategoryPlot plot, 227 CategoryAxis domainAxis, 228 ValueAxis rangeAxis, 229 CategoryDataset dataset, 230 int row, 231 int column) { 232 233 // nothing is drawn for null values... 234 Number dataValue = dataset.getValue(row, column); 235 if (dataValue == null) { 236 return; 237 } 238 239 // X 240 double value = dataValue.doubleValue(); 241 double base = 0.0; 242 double lclip = getLowerClip(); 243 double uclip = getUpperClip(); 244 if (uclip <= 0.0) { // cases 1, 2, 3 and 4 245 if (value >= uclip) { 246 return; // bar is not visible 247 } 248 base = uclip; 249 if (value <= lclip) { 250 value = lclip; 251 } 252 } 253 else if (lclip <= 0.0) { // cases 5, 6, 7 and 8 254 if (value >= uclip) { 255 value = uclip; 256 } 257 else { 258 if (value <= lclip) { 259 value = lclip; 260 } 261 } 262 } 263 else { // cases 9, 10, 11 and 12 264 if (value <= lclip) { 265 return; // bar is not visible 266 } 267 base = lclip; 268 if (value >= uclip) { 269 value = uclip; 270 } 271 } 272 273 RectangleEdge edge = plot.getRangeAxisEdge(); 274 double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge); 275 double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge); 276 double rectX = Math.min(transX1, transX2); 277 double rectWidth = Math.abs(transX2 - transX1); 278 279 // Y 280 double rectY = domainAxis.getCategoryMiddle(column, getColumnCount(), 281 dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; 282 283 int seriesCount = getRowCount(); 284 285 // draw the bar... 286 double shift = 0.0; 287 double rectHeight = 0.0; 288 double widthFactor = 1.0; 289 double seriesBarWidth = getSeriesBarWidth(row); 290 if (!Double.isNaN(seriesBarWidth)) { 291 widthFactor = seriesBarWidth; 292 } 293 rectHeight = widthFactor * state.getBarWidth(); 294 rectY = rectY + (1 - widthFactor) * state.getBarWidth() / 2.0; 295 if (seriesCount > 1) { 296 shift = rectHeight * 0.20 / (seriesCount - 1); 297 } 298 299 Rectangle2D bar = new Rectangle2D.Double(rectX, 300 (rectY + ((seriesCount - 1 - row) * shift)), rectWidth, 301 (rectHeight - (seriesCount - 1 - row) * shift * 2)); 302 303 Paint itemPaint = getItemPaint(row, column); 304 GradientPaintTransformer t = getGradientPaintTransformer(); 305 if (t != null && itemPaint instanceof GradientPaint) { 306 itemPaint = t.transform((GradientPaint) itemPaint, bar); 307 } 308 g2.setPaint(itemPaint); 309 g2.fill(bar); 310 311 // draw the outline... 312 if (isDrawBarOutline() 313 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 314 Stroke stroke = getItemOutlineStroke(row, column); 315 Paint paint = getItemOutlinePaint(row, column); 316 if (stroke != null && paint != null) { 317 g2.setStroke(stroke); 318 g2.setPaint(paint); 319 g2.draw(bar); 320 } 321 } 322 323 CategoryItemLabelGenerator generator 324 = getItemLabelGenerator(row, column); 325 if (generator != null && isItemLabelVisible(row, column)) { 326 drawItemLabel(g2, dataset, row, column, plot, generator, bar, 327 (transX1 > transX2)); 328 } 329 330 // collect entity and tool tip information... 331 EntityCollection entities = state.getEntityCollection(); 332 if (entities != null) { 333 addItemEntity(entities, dataset, row, column, bar); 334 } 335 } 336 337 /** 338 * Draws the bar for a single (series, category) data item. 339 * 340 * @param g2 the graphics device. 341 * @param state the renderer state. 342 * @param dataArea the data area. 343 * @param plot the plot. 344 * @param domainAxis the domain axis. 345 * @param rangeAxis the range axis. 346 * @param dataset the dataset. 347 * @param row the row index (zero-based). 348 * @param column the column index (zero-based). 349 */ 350 protected void drawVerticalItem(Graphics2D g2, 351 CategoryItemRendererState state, 352 Rectangle2D dataArea, 353 CategoryPlot plot, 354 CategoryAxis domainAxis, 355 ValueAxis rangeAxis, 356 CategoryDataset dataset, 357 int row, 358 int column) { 359 360 // nothing is drawn for null values... 361 Number dataValue = dataset.getValue(row, column); 362 if (dataValue == null) { 363 return; 364 } 365 366 // BAR X 367 double rectX = domainAxis.getCategoryMiddle(column, getColumnCount(), 368 dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; 369 370 int seriesCount = getRowCount(); 371 372 // BAR Y 373 double value = dataValue.doubleValue(); 374 double base = 0.0; 375 double lclip = getLowerClip(); 376 double uclip = getUpperClip(); 377 378 if (uclip <= 0.0) { // cases 1, 2, 3 and 4 379 if (value >= uclip) { 380 return; // bar is not visible 381 } 382 base = uclip; 383 if (value <= lclip) { 384 value = lclip; 385 } 386 } 387 else if (lclip <= 0.0) { // cases 5, 6, 7 and 8 388 if (value >= uclip) { 389 value = uclip; 390 } 391 else { 392 if (value <= lclip) { 393 value = lclip; 394 } 395 } 396 } 397 else { // cases 9, 10, 11 and 12 398 if (value <= lclip) { 399 return; // bar is not visible 400 } 401 base = getLowerClip(); 402 if (value >= uclip) { 403 value = uclip; 404 } 405 } 406 407 RectangleEdge edge = plot.getRangeAxisEdge(); 408 double transY1 = rangeAxis.valueToJava2D(base, dataArea, edge); 409 double transY2 = rangeAxis.valueToJava2D(value, dataArea, edge); 410 double rectY = Math.min(transY2, transY1); 411 412 double rectWidth = state.getBarWidth(); 413 double rectHeight = Math.abs(transY2 - transY1); 414 415 // draw the bar... 416 double shift = 0.0; 417 rectWidth = 0.0; 418 double widthFactor = 1.0; 419 double seriesBarWidth = getSeriesBarWidth(row); 420 if (!Double.isNaN(seriesBarWidth)) { 421 widthFactor = seriesBarWidth; 422 } 423 rectWidth = widthFactor * state.getBarWidth(); 424 rectX = rectX + (1 - widthFactor) * state.getBarWidth() / 2.0; 425 if (seriesCount > 1) { 426 // needs to be improved !!! 427 shift = rectWidth * 0.20 / (seriesCount - 1); 428 } 429 430 Rectangle2D bar = new Rectangle2D.Double( 431 (rectX + ((seriesCount - 1 - row) * shift)), rectY, 432 (rectWidth - (seriesCount - 1 - row) * shift * 2), rectHeight); 433 Paint itemPaint = getItemPaint(row, column); 434 GradientPaintTransformer t = getGradientPaintTransformer(); 435 if (t != null && itemPaint instanceof GradientPaint) { 436 itemPaint = t.transform((GradientPaint) itemPaint, bar); 437 } 438 g2.setPaint(itemPaint); 439 g2.fill(bar); 440 441 // draw the outline... 442 if (isDrawBarOutline() 443 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 444 Stroke stroke = getItemOutlineStroke(row, column); 445 Paint paint = getItemOutlinePaint(row, column); 446 if (stroke != null && paint != null) { 447 g2.setStroke(stroke); 448 g2.setPaint(paint); 449 g2.draw(bar); 450 } 451 } 452 453 // draw the item labels if there are any... 454 double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge); 455 double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge); 456 457 CategoryItemLabelGenerator generator 458 = getItemLabelGenerator(row, column); 459 if (generator != null && isItemLabelVisible(row, column)) { 460 drawItemLabel(g2, dataset, row, column, plot, generator, bar, 461 (transX1 > transX2)); 462 } 463 464 // collect entity and tool tip information... 465 EntityCollection entities = state.getEntityCollection(); 466 if (entities != null) { 467 addItemEntity(entities, dataset, row, column, bar); 468 } 469 } 470 471 }