001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2014, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ----------------------- 028 * LayeredBarRenderer.java 029 * ----------------------- 030 * (C) Copyright 2003-2014, 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 * 19-May-2009 : Fixed FindBugs warnings, patch by Michal Wozniak (DG); 051 * 052 */ 053 054package org.jfree.chart.renderer.category; 055 056import java.awt.GradientPaint; 057import java.awt.Graphics2D; 058import java.awt.Paint; 059import java.awt.Stroke; 060import java.awt.geom.Rectangle2D; 061import java.io.Serializable; 062 063import org.jfree.chart.axis.CategoryAxis; 064import org.jfree.chart.axis.ValueAxis; 065import org.jfree.chart.entity.EntityCollection; 066import org.jfree.chart.labels.CategoryItemLabelGenerator; 067import org.jfree.chart.plot.CategoryPlot; 068import org.jfree.chart.plot.PlotOrientation; 069import org.jfree.data.category.CategoryDataset; 070import org.jfree.ui.GradientPaintTransformer; 071import org.jfree.ui.RectangleEdge; 072import org.jfree.util.ObjectList; 073 074/** 075 * A {@link CategoryItemRenderer} that represents data using bars which are 076 * superimposed. The example shown here is generated by the 077 * <code>LayeredBarChartDemo1.java</code> program included in the JFreeChart 078 * Demo Collection: 079 * <br><br> 080 * <img src="../../../../../images/LayeredBarRendererSample.png" 081 * alt="LayeredBarRendererSample.png"> 082 */ 083public class LayeredBarRenderer extends BarRenderer implements Serializable { 084 085 /** For serialization. */ 086 private static final long serialVersionUID = -8716572894780469487L; 087 088 /** A list of the width of each series bar. */ 089 protected ObjectList seriesBarWidthList; 090 091 /** 092 * Default constructor. 093 */ 094 public LayeredBarRenderer() { 095 super(); 096 this.seriesBarWidthList = new ObjectList(); 097 } 098 099 /** 100 * Returns the bar width for a series, or <code>Double.NaN</code> if no 101 * width has been set. 102 * 103 * @param series the series index (zero based). 104 * 105 * @return The width for the series (1.0=100%, it is the maximum). 106 */ 107 public double getSeriesBarWidth(int series) { 108 double result = Double.NaN; 109 Number n = (Number) this.seriesBarWidthList.get(series); 110 if (n != null) { 111 result = n.doubleValue(); 112 } 113 return result; 114 } 115 116 /** 117 * Sets the width of the bars of a series. 118 * 119 * @param series the series index (zero based). 120 * @param width the width of the series bar in percentage (1.0=100%, it is 121 * the maximum). 122 */ 123 public void setSeriesBarWidth(int series, double width) { 124 this.seriesBarWidthList.set(series, new Double(width)); 125 } 126 127 /** 128 * Calculates the bar width and stores it in the renderer state. 129 * 130 * @param plot the plot. 131 * @param dataArea the data area. 132 * @param rendererIndex the renderer index. 133 * @param state the renderer state. 134 */ 135 @Override 136 protected void calculateBarWidth(CategoryPlot plot, Rectangle2D dataArea, 137 int rendererIndex, 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 @Override 188 public void drawItem(Graphics2D g2, CategoryItemRendererState state, 189 Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, 190 ValueAxis rangeAxis, CategoryDataset data, int row, int column, 191 int pass) { 192 193 PlotOrientation orientation = plot.getOrientation(); 194 if (orientation == PlotOrientation.HORIZONTAL) { 195 drawHorizontalItem(g2, state, dataArea, plot, domainAxis, 196 rangeAxis, data, row, column); 197 } 198 else if (orientation == PlotOrientation.VERTICAL) { 199 drawVerticalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, 200 data, row, column); 201 } 202 203 } 204 205 /** 206 * Draws the bar for a single (series, category) data item. 207 * 208 * @param g2 the graphics device. 209 * @param state the renderer state. 210 * @param dataArea the data area. 211 * @param plot the plot. 212 * @param domainAxis the domain axis. 213 * @param rangeAxis the range axis. 214 * @param dataset the dataset. 215 * @param row the row index (zero-based). 216 * @param column the column index (zero-based). 217 */ 218 protected void drawHorizontalItem(Graphics2D g2, 219 CategoryItemRendererState state, 220 Rectangle2D dataArea, 221 CategoryPlot plot, 222 CategoryAxis domainAxis, 223 ValueAxis rangeAxis, 224 CategoryDataset dataset, 225 int row, 226 int column) { 227 228 // nothing is drawn for null values... 229 Number dataValue = dataset.getValue(row, column); 230 if (dataValue == null) { 231 return; 232 } 233 234 // X 235 double value = dataValue.doubleValue(); 236 double base = 0.0; 237 double lclip = getLowerClip(); 238 double uclip = getUpperClip(); 239 if (uclip <= 0.0) { // cases 1, 2, 3 and 4 240 if (value >= uclip) { 241 return; // bar is not visible 242 } 243 base = uclip; 244 if (value <= lclip) { 245 value = lclip; 246 } 247 } 248 else if (lclip <= 0.0) { // cases 5, 6, 7 and 8 249 if (value >= uclip) { 250 value = uclip; 251 } 252 else { 253 if (value <= lclip) { 254 value = lclip; 255 } 256 } 257 } 258 else { // cases 9, 10, 11 and 12 259 if (value <= lclip) { 260 return; // bar is not visible 261 } 262 base = lclip; 263 if (value >= uclip) { 264 value = uclip; 265 } 266 } 267 268 RectangleEdge edge = plot.getRangeAxisEdge(); 269 double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge); 270 double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge); 271 double rectX = Math.min(transX1, transX2); 272 double rectWidth = Math.abs(transX2 - transX1); 273 274 // Y 275 double rectY = domainAxis.getCategoryMiddle(column, getColumnCount(), 276 dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; 277 278 int seriesCount = getRowCount(); 279 280 // draw the bar... 281 double shift = 0.0; 282 double rectHeight; 283 double widthFactor = 1.0; 284 double seriesBarWidth = getSeriesBarWidth(row); 285 if (!Double.isNaN(seriesBarWidth)) { 286 widthFactor = seriesBarWidth; 287 } 288 rectHeight = widthFactor * state.getBarWidth(); 289 rectY = rectY + (1 - widthFactor) * state.getBarWidth() / 2.0; 290 if (seriesCount > 1) { 291 shift = rectHeight * 0.20 / (seriesCount - 1); 292 } 293 294 Rectangle2D bar = new Rectangle2D.Double(rectX, 295 (rectY + ((seriesCount - 1 - row) * shift)), rectWidth, 296 (rectHeight - (seriesCount - 1 - row) * shift * 2)); 297 298 Paint itemPaint = getItemPaint(row, column); 299 GradientPaintTransformer t = getGradientPaintTransformer(); 300 if (t != null && itemPaint instanceof GradientPaint) { 301 itemPaint = t.transform((GradientPaint) itemPaint, bar); 302 } 303 g2.setPaint(itemPaint); 304 g2.fill(bar); 305 306 // draw the outline... 307 if (isDrawBarOutline() 308 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 309 Stroke stroke = getItemOutlineStroke(row, column); 310 Paint paint = getItemOutlinePaint(row, column); 311 if (stroke != null && paint != null) { 312 g2.setStroke(stroke); 313 g2.setPaint(paint); 314 g2.draw(bar); 315 } 316 } 317 318 CategoryItemLabelGenerator generator 319 = getItemLabelGenerator(row, column); 320 if (generator != null && isItemLabelVisible(row, column)) { 321 drawItemLabel(g2, dataset, row, column, plot, generator, bar, 322 (transX1 > transX2)); 323 } 324 325 // collect entity and tool tip information... 326 EntityCollection entities = state.getEntityCollection(); 327 if (entities != null) { 328 addItemEntity(entities, dataset, row, column, bar); 329 } 330 } 331 332 /** 333 * Draws the bar for a single (series, category) data item. 334 * 335 * @param g2 the graphics device. 336 * @param state the renderer state. 337 * @param dataArea the data area. 338 * @param plot the plot. 339 * @param domainAxis the domain axis. 340 * @param rangeAxis the range axis. 341 * @param dataset the dataset. 342 * @param row the row index (zero-based). 343 * @param column the column index (zero-based). 344 */ 345 protected void drawVerticalItem(Graphics2D g2, 346 CategoryItemRendererState state, 347 Rectangle2D dataArea, 348 CategoryPlot plot, 349 CategoryAxis domainAxis, 350 ValueAxis rangeAxis, 351 CategoryDataset dataset, 352 int row, 353 int column) { 354 355 // nothing is drawn for null values... 356 Number dataValue = dataset.getValue(row, column); 357 if (dataValue == null) { 358 return; 359 } 360 361 // BAR X 362 double rectX = domainAxis.getCategoryMiddle(column, getColumnCount(), 363 dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; 364 365 int seriesCount = getRowCount(); 366 367 // BAR Y 368 double value = dataValue.doubleValue(); 369 double base = 0.0; 370 double lclip = getLowerClip(); 371 double uclip = getUpperClip(); 372 373 if (uclip <= 0.0) { // cases 1, 2, 3 and 4 374 if (value >= uclip) { 375 return; // bar is not visible 376 } 377 base = uclip; 378 if (value <= lclip) { 379 value = lclip; 380 } 381 } 382 else if (lclip <= 0.0) { // cases 5, 6, 7 and 8 383 if (value >= uclip) { 384 value = uclip; 385 } 386 else { 387 if (value <= lclip) { 388 value = lclip; 389 } 390 } 391 } 392 else { // cases 9, 10, 11 and 12 393 if (value <= lclip) { 394 return; // bar is not visible 395 } 396 base = getLowerClip(); 397 if (value >= uclip) { 398 value = uclip; 399 } 400 } 401 402 RectangleEdge edge = plot.getRangeAxisEdge(); 403 double transY1 = rangeAxis.valueToJava2D(base, dataArea, edge); 404 double transY2 = rangeAxis.valueToJava2D(value, dataArea, edge); 405 double rectY = Math.min(transY2, transY1); 406 407 double rectWidth; 408 double rectHeight = Math.abs(transY2 - transY1); 409 410 // draw the bar... 411 double shift = 0.0; 412 double widthFactor = 1.0; 413 double seriesBarWidth = getSeriesBarWidth(row); 414 if (!Double.isNaN(seriesBarWidth)) { 415 widthFactor = seriesBarWidth; 416 } 417 rectWidth = widthFactor * state.getBarWidth(); 418 rectX = rectX + (1 - widthFactor) * state.getBarWidth() / 2.0; 419 if (seriesCount > 1) { 420 // needs to be improved !!! 421 shift = rectWidth * 0.20 / (seriesCount - 1); 422 } 423 424 Rectangle2D bar = new Rectangle2D.Double( 425 (rectX + ((seriesCount - 1 - row) * shift)), rectY, 426 (rectWidth - (seriesCount - 1 - row) * shift * 2), rectHeight); 427 Paint itemPaint = getItemPaint(row, column); 428 GradientPaintTransformer t = getGradientPaintTransformer(); 429 if (t != null && itemPaint instanceof GradientPaint) { 430 itemPaint = t.transform((GradientPaint) itemPaint, bar); 431 } 432 g2.setPaint(itemPaint); 433 g2.fill(bar); 434 435 // draw the outline... 436 if (isDrawBarOutline() 437 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 438 Stroke stroke = getItemOutlineStroke(row, column); 439 Paint paint = getItemOutlinePaint(row, column); 440 if (stroke != null && paint != null) { 441 g2.setStroke(stroke); 442 g2.setPaint(paint); 443 g2.draw(bar); 444 } 445 } 446 447 // draw the item labels if there are any... 448 double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge); 449 double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge); 450 451 CategoryItemLabelGenerator generator 452 = getItemLabelGenerator(row, column); 453 if (generator != null && isItemLabelVisible(row, column)) { 454 drawItemLabel(g2, dataset, row, column, plot, generator, bar, 455 (transX1 > transX2)); 456 } 457 458 // collect entity and tool tip information... 459 EntityCollection entities = state.getEntityCollection(); 460 if (entities != null) { 461 addItemEntity(entities, dataset, row, column, bar); 462 } 463 } 464 465}