001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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 * BarRenderer3D.java 029 * ------------------ 030 * (C) Copyright 2001-2007, by Serge V. Grachov and Contributors. 031 * 032 * Original Author: Serge V. Grachov; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Tin Luu; 035 * Milo Simpson; 036 * Richard Atkinson; 037 * Rich Unger; 038 * Christian W. Zuckschwerdt; 039 * 040 * Changes 041 * ------- 042 * 31-Oct-2001 : First version, contributed by Serge V. Grachov (DG); 043 * 15-Nov-2001 : Modified to allow for null data values (DG); 044 * 13-Dec-2001 : Added tooltips (DG); 045 * 16-Jan-2002 : Added fix for single category or single series datasets, 046 * pointed out by Taoufik Romdhane (DG); 047 * 24-May-2002 : Incorporated tooltips into chart entities (DG); 048 * 11-Jun-2002 : Added check for (permitted) null info object, bug and fix 049 * reported by David Basten. Also updated Javadocs. (DG); 050 * 19-Jun-2002 : Added code to draw labels on bars (TL); 051 * 26-Jun-2002 : Added bar clipping to avoid PRExceptions (DG); 052 * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs 053 * for HTML image maps (RA); 054 * 06-Aug-2002 : Value labels now use number formatter, thanks to Milo 055 * Simpson (DG); 056 * 08-Aug-2002 : Applied fixed in bug id 592218 (DG); 057 * 20-Sep-2002 : Added fix for categoryPaint by Rich Unger, and fixed errors 058 * reported by Checkstyle (DG); 059 * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 060 * CategoryToolTipGenerator interface (DG); 061 * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG); 062 * 06-Nov-2002 : Moved to the com.jrefinery.chart.renderer package (DG); 063 * 28-Jan-2003 : Added an attribute to control the shading of the left and 064 * bottom walls in the plot background (DG); 065 * 25-Mar-2003 : Implemented Serializable (DG); 066 * 10-Apr-2003 : Removed category paint usage (DG); 067 * 13-May-2003 : Renamed VerticalBarRenderer3D --> BarRenderer3D and merged with 068 * HorizontalBarRenderer3D (DG); 069 * 30-Jul-2003 : Modified entity constructor (CZ); 070 * 19-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 071 * 07-Oct-2003 : Added renderer state (DG); 072 * 08-Oct-2003 : Removed clipping (replaced with flag in CategoryPlot to 073 * control order in which the data items are processed) (DG); 074 * 20-Oct-2003 : Fixed bug (outline stroke not being used for bar 075 * outlines) (DG); 076 * 21-Oct-2003 : Bar width moved into CategoryItemRendererState (DG); 077 * 24-Nov-2003 : Fixed bug 846324 (item labels not showing) (DG); 078 * 27-Nov-2003 : Added code to respect maxBarWidth setting (DG); 079 * 02-Feb-2004 : Fixed bug where 'drawBarOutline' flag is not respected (DG); 080 * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste 081 * overriding easier (DG); 082 * 04-Oct-2004 : Fixed bug with item label positioning when plot alignment is 083 * horizontal (DG); 084 * 05-Nov-2004 : Modified drawItem() signature (DG); 085 * 20-Apr-2005 : Renamed CategoryLabelGenerator 086 * --> CategoryItemLabelGenerator (DG); 087 * 25-Apr-2005 : Override initialise() method to fix bug 1189642 (DG); 088 * 09-Jun-2005 : Use addEntityItem from super class (DG); 089 * ------------- JFREECHART 1.0.x --------------------------------------------- 090 * 07-Dec-2006 : Implemented equals() override (DG); 091 * 17-Jan-2007 : Fixed bug in drawDomainGridline() method (DG); 092 * 03-Apr-2007 : Fixed bugs in drawBackground() method (DG); 093 * 16-Oct-2007 : Fixed bug in range marker drawing (DG); 094 * 095 */ 096 097 package org.jfree.chart.renderer.category; 098 099 import java.awt.AlphaComposite; 100 import java.awt.Color; 101 import java.awt.Composite; 102 import java.awt.Font; 103 import java.awt.Graphics2D; 104 import java.awt.Image; 105 import java.awt.Paint; 106 import java.awt.Stroke; 107 import java.awt.geom.GeneralPath; 108 import java.awt.geom.Line2D; 109 import java.awt.geom.Point2D; 110 import java.awt.geom.Rectangle2D; 111 import java.io.IOException; 112 import java.io.ObjectInputStream; 113 import java.io.ObjectOutputStream; 114 import java.io.Serializable; 115 116 import org.jfree.chart.Effect3D; 117 import org.jfree.chart.axis.CategoryAxis; 118 import org.jfree.chart.axis.ValueAxis; 119 import org.jfree.chart.entity.EntityCollection; 120 import org.jfree.chart.event.RendererChangeEvent; 121 import org.jfree.chart.labels.CategoryItemLabelGenerator; 122 import org.jfree.chart.labels.ItemLabelAnchor; 123 import org.jfree.chart.labels.ItemLabelPosition; 124 import org.jfree.chart.plot.CategoryPlot; 125 import org.jfree.chart.plot.Marker; 126 import org.jfree.chart.plot.Plot; 127 import org.jfree.chart.plot.PlotOrientation; 128 import org.jfree.chart.plot.PlotRenderingInfo; 129 import org.jfree.chart.plot.ValueMarker; 130 import org.jfree.data.Range; 131 import org.jfree.data.category.CategoryDataset; 132 import org.jfree.io.SerialUtilities; 133 import org.jfree.text.TextUtilities; 134 import org.jfree.ui.LengthAdjustmentType; 135 import org.jfree.ui.RectangleAnchor; 136 import org.jfree.ui.RectangleEdge; 137 import org.jfree.ui.TextAnchor; 138 import org.jfree.util.PaintUtilities; 139 import org.jfree.util.PublicCloneable; 140 141 /** 142 * A renderer for bars with a 3D effect, for use with the 143 * {@link org.jfree.chart.plot.CategoryPlot} class. 144 */ 145 public class BarRenderer3D extends BarRenderer 146 implements Effect3D, Cloneable, PublicCloneable, 147 Serializable { 148 149 /** For serialization. */ 150 private static final long serialVersionUID = 7686976503536003636L; 151 152 /** The default x-offset for the 3D effect. */ 153 public static final double DEFAULT_X_OFFSET = 12.0; 154 155 /** The default y-offset for the 3D effect. */ 156 public static final double DEFAULT_Y_OFFSET = 8.0; 157 158 /** The default wall paint. */ 159 public static final Paint DEFAULT_WALL_PAINT = new Color(0xDD, 0xDD, 0xDD); 160 161 /** The size of x-offset for the 3D effect. */ 162 private double xOffset; 163 164 /** The size of y-offset for the 3D effect. */ 165 private double yOffset; 166 167 /** The paint used to shade the left and lower 3D wall. */ 168 private transient Paint wallPaint; 169 170 /** 171 * Default constructor, creates a renderer with a default '3D effect'. 172 */ 173 public BarRenderer3D() { 174 this(DEFAULT_X_OFFSET, DEFAULT_Y_OFFSET); 175 } 176 177 /** 178 * Constructs a new renderer with the specified '3D effect'. 179 * 180 * @param xOffset the x-offset for the 3D effect. 181 * @param yOffset the y-offset for the 3D effect. 182 */ 183 public BarRenderer3D(double xOffset, double yOffset) { 184 185 super(); 186 this.xOffset = xOffset; 187 this.yOffset = yOffset; 188 this.wallPaint = DEFAULT_WALL_PAINT; 189 // set the default item label positions 190 ItemLabelPosition p1 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12, 191 TextAnchor.TOP_CENTER); 192 setBasePositiveItemLabelPosition(p1); 193 ItemLabelPosition p2 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12, 194 TextAnchor.TOP_CENTER); 195 setBaseNegativeItemLabelPosition(p2); 196 197 } 198 199 /** 200 * Returns the x-offset for the 3D effect. 201 * 202 * @return The 3D effect. 203 * 204 * @see #getYOffset() 205 */ 206 public double getXOffset() { 207 return this.xOffset; 208 } 209 210 /** 211 * Returns the y-offset for the 3D effect. 212 * 213 * @return The 3D effect. 214 */ 215 public double getYOffset() { 216 return this.yOffset; 217 } 218 219 /** 220 * Returns the paint used to highlight the left and bottom wall in the plot 221 * background. 222 * 223 * @return The paint. 224 * 225 * @see #setWallPaint(Paint) 226 */ 227 public Paint getWallPaint() { 228 return this.wallPaint; 229 } 230 231 /** 232 * Sets the paint used to hightlight the left and bottom walls in the plot 233 * background, and sends a {@link RendererChangeEvent} to all registered 234 * listeners. 235 * 236 * @param paint the paint (<code>null</code> not permitted). 237 * 238 * @see #getWallPaint() 239 */ 240 public void setWallPaint(Paint paint) { 241 if (paint == null) { 242 throw new IllegalArgumentException("Null 'paint' argument."); 243 } 244 this.wallPaint = paint; 245 fireChangeEvent(); 246 } 247 248 249 /** 250 * Initialises the renderer and returns a state object that will be passed 251 * to subsequent calls to the drawItem method. This method gets called 252 * once at the start of the process of drawing a chart. 253 * 254 * @param g2 the graphics device. 255 * @param dataArea the area in which the data is to be plotted. 256 * @param plot the plot. 257 * @param rendererIndex the renderer index. 258 * @param info collects chart rendering information for return to caller. 259 * 260 * @return The renderer state. 261 */ 262 public CategoryItemRendererState initialise(Graphics2D g2, 263 Rectangle2D dataArea, 264 CategoryPlot plot, 265 int rendererIndex, 266 PlotRenderingInfo info) { 267 268 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 269 dataArea.getY() + getYOffset(), dataArea.getWidth() 270 - getXOffset(), dataArea.getHeight() - getYOffset()); 271 CategoryItemRendererState state = super.initialise(g2, adjusted, plot, 272 rendererIndex, info); 273 return state; 274 275 } 276 277 /** 278 * Draws the background for the plot. 279 * 280 * @param g2 the graphics device. 281 * @param plot the plot. 282 * @param dataArea the area inside the axes. 283 */ 284 public void drawBackground(Graphics2D g2, CategoryPlot plot, 285 Rectangle2D dataArea) { 286 287 float x0 = (float) dataArea.getX(); 288 float x1 = x0 + (float) Math.abs(this.xOffset); 289 float x3 = (float) dataArea.getMaxX(); 290 float x2 = x3 - (float) Math.abs(this.xOffset); 291 292 float y0 = (float) dataArea.getMaxY(); 293 float y1 = y0 - (float) Math.abs(this.yOffset); 294 float y3 = (float) dataArea.getMinY(); 295 float y2 = y3 + (float) Math.abs(this.yOffset); 296 297 GeneralPath clip = new GeneralPath(); 298 clip.moveTo(x0, y0); 299 clip.lineTo(x0, y2); 300 clip.lineTo(x1, y3); 301 clip.lineTo(x3, y3); 302 clip.lineTo(x3, y1); 303 clip.lineTo(x2, y0); 304 clip.closePath(); 305 306 Composite originalComposite = g2.getComposite(); 307 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 308 plot.getBackgroundAlpha())); 309 310 // fill background... 311 Paint backgroundPaint = plot.getBackgroundPaint(); 312 if (backgroundPaint != null) { 313 g2.setPaint(backgroundPaint); 314 g2.fill(clip); 315 } 316 317 GeneralPath leftWall = new GeneralPath(); 318 leftWall.moveTo(x0, y0); 319 leftWall.lineTo(x0, y2); 320 leftWall.lineTo(x1, y3); 321 leftWall.lineTo(x1, y1); 322 leftWall.closePath(); 323 g2.setPaint(getWallPaint()); 324 g2.fill(leftWall); 325 326 GeneralPath bottomWall = new GeneralPath(); 327 bottomWall.moveTo(x0, y0); 328 bottomWall.lineTo(x1, y1); 329 bottomWall.lineTo(x3, y1); 330 bottomWall.lineTo(x2, y0); 331 bottomWall.closePath(); 332 g2.setPaint(getWallPaint()); 333 g2.fill(bottomWall); 334 335 // highlight the background corners... 336 g2.setPaint(Color.lightGray); 337 Line2D corner = new Line2D.Double(x0, y0, x1, y1); 338 g2.draw(corner); 339 corner.setLine(x1, y1, x1, y3); 340 g2.draw(corner); 341 corner.setLine(x1, y1, x3, y1); 342 g2.draw(corner); 343 344 // draw background image, if there is one... 345 Image backgroundImage = plot.getBackgroundImage(); 346 if (backgroundImage != null) { 347 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX() 348 + getXOffset(), dataArea.getY(), 349 dataArea.getWidth() - getXOffset(), 350 dataArea.getHeight() - getYOffset()); 351 plot.drawBackgroundImage(g2, adjusted); 352 } 353 354 g2.setComposite(originalComposite); 355 356 } 357 358 /** 359 * Draws the outline for the plot. 360 * 361 * @param g2 the graphics device. 362 * @param plot the plot. 363 * @param dataArea the area inside the axes. 364 */ 365 public void drawOutline(Graphics2D g2, CategoryPlot plot, 366 Rectangle2D dataArea) { 367 368 float x0 = (float) dataArea.getX(); 369 float x1 = x0 + (float) Math.abs(this.xOffset); 370 float x3 = (float) dataArea.getMaxX(); 371 float x2 = x3 - (float) Math.abs(this.xOffset); 372 373 float y0 = (float) dataArea.getMaxY(); 374 float y1 = y0 - (float) Math.abs(this.yOffset); 375 float y3 = (float) dataArea.getMinY(); 376 float y2 = y3 + (float) Math.abs(this.yOffset); 377 378 GeneralPath clip = new GeneralPath(); 379 clip.moveTo(x0, y0); 380 clip.lineTo(x0, y2); 381 clip.lineTo(x1, y3); 382 clip.lineTo(x3, y3); 383 clip.lineTo(x3, y1); 384 clip.lineTo(x2, y0); 385 clip.closePath(); 386 387 // put an outline around the data area... 388 Stroke outlineStroke = plot.getOutlineStroke(); 389 Paint outlinePaint = plot.getOutlinePaint(); 390 if ((outlineStroke != null) && (outlinePaint != null)) { 391 g2.setStroke(outlineStroke); 392 g2.setPaint(outlinePaint); 393 g2.draw(clip); 394 } 395 396 } 397 398 /** 399 * Draws a grid line against the domain axis. 400 * 401 * @param g2 the graphics device. 402 * @param plot the plot. 403 * @param dataArea the area for plotting data (not yet adjusted for any 404 * 3D effect). 405 * @param value the Java2D value at which the grid line should be drawn. 406 * 407 */ 408 public void drawDomainGridline(Graphics2D g2, 409 CategoryPlot plot, 410 Rectangle2D dataArea, 411 double value) { 412 413 Line2D line1 = null; 414 Line2D line2 = null; 415 PlotOrientation orientation = plot.getOrientation(); 416 if (orientation == PlotOrientation.HORIZONTAL) { 417 double y0 = value; 418 double y1 = value - getYOffset(); 419 double x0 = dataArea.getMinX(); 420 double x1 = x0 + getXOffset(); 421 double x2 = dataArea.getMaxX(); 422 line1 = new Line2D.Double(x0, y0, x1, y1); 423 line2 = new Line2D.Double(x1, y1, x2, y1); 424 } 425 else if (orientation == PlotOrientation.VERTICAL) { 426 double x0 = value; 427 double x1 = value + getXOffset(); 428 double y0 = dataArea.getMaxY(); 429 double y1 = y0 - getYOffset(); 430 double y2 = dataArea.getMinY(); 431 line1 = new Line2D.Double(x0, y0, x1, y1); 432 line2 = new Line2D.Double(x1, y1, x1, y2); 433 } 434 Paint paint = plot.getDomainGridlinePaint(); 435 Stroke stroke = plot.getDomainGridlineStroke(); 436 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 437 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 438 g2.draw(line1); 439 g2.draw(line2); 440 441 } 442 443 /** 444 * Draws a grid line against the range axis. 445 * 446 * @param g2 the graphics device. 447 * @param plot the plot. 448 * @param axis the value axis. 449 * @param dataArea the area for plotting data (not yet adjusted for any 450 * 3D effect). 451 * @param value the value at which the grid line should be drawn. 452 * 453 */ 454 public void drawRangeGridline(Graphics2D g2, 455 CategoryPlot plot, 456 ValueAxis axis, 457 Rectangle2D dataArea, 458 double value) { 459 460 Range range = axis.getRange(); 461 462 if (!range.contains(value)) { 463 return; 464 } 465 466 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 467 dataArea.getY() + getYOffset(), dataArea.getWidth() 468 - getXOffset(), dataArea.getHeight() - getYOffset()); 469 470 Line2D line1 = null; 471 Line2D line2 = null; 472 PlotOrientation orientation = plot.getOrientation(); 473 if (orientation == PlotOrientation.HORIZONTAL) { 474 double x0 = axis.valueToJava2D(value, adjusted, 475 plot.getRangeAxisEdge()); 476 double x1 = x0 + getXOffset(); 477 double y0 = dataArea.getMaxY(); 478 double y1 = y0 - getYOffset(); 479 double y2 = dataArea.getMinY(); 480 line1 = new Line2D.Double(x0, y0, x1, y1); 481 line2 = new Line2D.Double(x1, y1, x1, y2); 482 } 483 else if (orientation == PlotOrientation.VERTICAL) { 484 double y0 = axis.valueToJava2D(value, adjusted, 485 plot.getRangeAxisEdge()); 486 double y1 = y0 - getYOffset(); 487 double x0 = dataArea.getMinX(); 488 double x1 = x0 + getXOffset(); 489 double x2 = dataArea.getMaxX(); 490 line1 = new Line2D.Double(x0, y0, x1, y1); 491 line2 = new Line2D.Double(x1, y1, x2, y1); 492 } 493 Paint paint = plot.getRangeGridlinePaint(); 494 Stroke stroke = plot.getRangeGridlineStroke(); 495 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 496 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 497 g2.draw(line1); 498 g2.draw(line2); 499 500 } 501 502 /** 503 * Draws a range marker. 504 * 505 * @param g2 the graphics device. 506 * @param plot the plot. 507 * @param axis the value axis. 508 * @param marker the marker. 509 * @param dataArea the area for plotting data (not including 3D effect). 510 */ 511 public void drawRangeMarker(Graphics2D g2, 512 CategoryPlot plot, 513 ValueAxis axis, 514 Marker marker, 515 Rectangle2D dataArea) { 516 517 518 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 519 dataArea.getY() + getYOffset(), dataArea.getWidth() 520 - getXOffset(), dataArea.getHeight() - getYOffset()); 521 if (marker instanceof ValueMarker) { 522 ValueMarker vm = (ValueMarker) marker; 523 double value = vm.getValue(); 524 Range range = axis.getRange(); 525 if (!range.contains(value)) { 526 return; 527 } 528 529 GeneralPath path = null; 530 PlotOrientation orientation = plot.getOrientation(); 531 if (orientation == PlotOrientation.HORIZONTAL) { 532 float x = (float) axis.valueToJava2D(value, adjusted, 533 plot.getRangeAxisEdge()); 534 float y = (float) adjusted.getMaxY(); 535 path = new GeneralPath(); 536 path.moveTo(x, y); 537 path.lineTo((float) (x + getXOffset()), 538 y - (float) getYOffset()); 539 path.lineTo((float) (x + getXOffset()), 540 (float) (adjusted.getMinY() - getYOffset())); 541 path.lineTo(x, (float) adjusted.getMinY()); 542 path.closePath(); 543 } 544 else if (orientation == PlotOrientation.VERTICAL) { 545 float y = (float) axis.valueToJava2D(value, adjusted, 546 plot.getRangeAxisEdge()); 547 float x = (float) dataArea.getX(); 548 path = new GeneralPath(); 549 path.moveTo(x, y); 550 path.lineTo(x + (float) this.xOffset, y - (float) this.yOffset); 551 path.lineTo((float) (adjusted.getMaxX() + this.xOffset), 552 y - (float) this.yOffset); 553 path.lineTo((float) (adjusted.getMaxX()), y); 554 path.closePath(); 555 } 556 g2.setPaint(marker.getPaint()); 557 g2.fill(path); 558 g2.setPaint(marker.getOutlinePaint()); 559 g2.draw(path); 560 561 String label = marker.getLabel(); 562 RectangleAnchor anchor = marker.getLabelAnchor(); 563 if (label != null) { 564 Font labelFont = marker.getLabelFont(); 565 g2.setFont(labelFont); 566 g2.setPaint(marker.getLabelPaint()); 567 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 568 g2, orientation, dataArea, path.getBounds2D(), 569 marker.getLabelOffset(), LengthAdjustmentType.EXPAND, 570 anchor); 571 TextUtilities.drawAlignedString(label, g2, 572 (float) coordinates.getX(), (float) coordinates.getY(), 573 marker.getLabelTextAnchor()); 574 } 575 576 } 577 else { 578 super.drawRangeMarker(g2, plot, axis, marker, adjusted); 579 // TODO: draw the interval marker with a 3D effect 580 } 581 } 582 583 /** 584 * Draws a 3D bar to represent one data item. 585 * 586 * @param g2 the graphics device. 587 * @param state the renderer state. 588 * @param dataArea the area for plotting the data. 589 * @param plot the plot. 590 * @param domainAxis the domain axis. 591 * @param rangeAxis the range axis. 592 * @param dataset the dataset. 593 * @param row the row index (zero-based). 594 * @param column the column index (zero-based). 595 * @param pass the pass index. 596 */ 597 public void drawItem(Graphics2D g2, 598 CategoryItemRendererState state, 599 Rectangle2D dataArea, 600 CategoryPlot plot, 601 CategoryAxis domainAxis, 602 ValueAxis rangeAxis, 603 CategoryDataset dataset, 604 int row, 605 int column, 606 int pass) { 607 608 // check the value we are plotting... 609 Number dataValue = dataset.getValue(row, column); 610 if (dataValue == null) { 611 return; 612 } 613 614 double value = dataValue.doubleValue(); 615 616 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 617 dataArea.getY() + getYOffset(), 618 dataArea.getWidth() - getXOffset(), 619 dataArea.getHeight() - getYOffset()); 620 621 PlotOrientation orientation = plot.getOrientation(); 622 623 double barW0 = calculateBarW0(plot, orientation, adjusted, domainAxis, 624 state, row, column); 625 double[] barL0L1 = calculateBarL0L1(value); 626 if (barL0L1 == null) { 627 return; // the bar is not visible 628 } 629 630 RectangleEdge edge = plot.getRangeAxisEdge(); 631 double transL0 = rangeAxis.valueToJava2D(barL0L1[0], adjusted, edge); 632 double transL1 = rangeAxis.valueToJava2D(barL0L1[1], adjusted, edge); 633 double barL0 = Math.min(transL0, transL1); 634 double barLength = Math.abs(transL1 - transL0); 635 636 // draw the bar... 637 Rectangle2D bar = null; 638 if (orientation == PlotOrientation.HORIZONTAL) { 639 bar = new Rectangle2D.Double(barL0, barW0, barLength, 640 state.getBarWidth()); 641 } 642 else { 643 bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(), 644 barLength); 645 } 646 Paint itemPaint = getItemPaint(row, column); 647 g2.setPaint(itemPaint); 648 g2.fill(bar); 649 650 double x0 = bar.getMinX(); 651 double x1 = x0 + getXOffset(); 652 double x2 = bar.getMaxX(); 653 double x3 = x2 + getXOffset(); 654 655 double y0 = bar.getMinY() - getYOffset(); 656 double y1 = bar.getMinY(); 657 double y2 = bar.getMaxY() - getYOffset(); 658 double y3 = bar.getMaxY(); 659 660 GeneralPath bar3dRight = null; 661 GeneralPath bar3dTop = null; 662 if (barLength > 0.0) { 663 bar3dRight = new GeneralPath(); 664 bar3dRight.moveTo((float) x2, (float) y3); 665 bar3dRight.lineTo((float) x2, (float) y1); 666 bar3dRight.lineTo((float) x3, (float) y0); 667 bar3dRight.lineTo((float) x3, (float) y2); 668 bar3dRight.closePath(); 669 670 if (itemPaint instanceof Color) { 671 g2.setPaint(((Color) itemPaint).darker()); 672 } 673 g2.fill(bar3dRight); 674 } 675 676 bar3dTop = new GeneralPath(); 677 bar3dTop.moveTo((float) x0, (float) y1); 678 bar3dTop.lineTo((float) x1, (float) y0); 679 bar3dTop.lineTo((float) x3, (float) y0); 680 bar3dTop.lineTo((float) x2, (float) y1); 681 bar3dTop.closePath(); 682 g2.fill(bar3dTop); 683 684 if (isDrawBarOutline() 685 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 686 g2.setStroke(getItemOutlineStroke(row, column)); 687 g2.setPaint(getItemOutlinePaint(row, column)); 688 g2.draw(bar); 689 if (bar3dRight != null) { 690 g2.draw(bar3dRight); 691 } 692 if (bar3dTop != null) { 693 g2.draw(bar3dTop); 694 } 695 } 696 697 CategoryItemLabelGenerator generator 698 = getItemLabelGenerator(row, column); 699 if (generator != null && isItemLabelVisible(row, column)) { 700 drawItemLabel(g2, dataset, row, column, plot, generator, bar, 701 (value < 0.0)); 702 } 703 704 // add an item entity, if this information is being collected 705 EntityCollection entities = state.getEntityCollection(); 706 if (entities != null) { 707 GeneralPath barOutline = new GeneralPath(); 708 barOutline.moveTo((float) x0, (float) y3); 709 barOutline.lineTo((float) x0, (float) y1); 710 barOutline.lineTo((float) x1, (float) y0); 711 barOutline.lineTo((float) x3, (float) y0); 712 barOutline.lineTo((float) x3, (float) y2); 713 barOutline.lineTo((float) x2, (float) y3); 714 barOutline.closePath(); 715 addItemEntity(entities, dataset, row, column, barOutline); 716 } 717 718 } 719 720 /** 721 * Tests this renderer for equality with an arbitrary object. 722 * 723 * @param obj the object (<code>null</code> permitted). 724 * 725 * @return A boolean. 726 */ 727 public boolean equals(Object obj) { 728 if (obj == this) { 729 return true; 730 } 731 if (!(obj instanceof BarRenderer3D)) { 732 return false; 733 } 734 BarRenderer3D that = (BarRenderer3D) obj; 735 if (this.xOffset != that.xOffset) { 736 return false; 737 } 738 if (this.yOffset != that.yOffset) { 739 return false; 740 } 741 if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) { 742 return false; 743 } 744 return super.equals(obj); 745 } 746 747 /** 748 * Provides serialization support. 749 * 750 * @param stream the output stream. 751 * 752 * @throws IOException if there is an I/O error. 753 */ 754 private void writeObject(ObjectOutputStream stream) throws IOException { 755 stream.defaultWriteObject(); 756 SerialUtilities.writePaint(this.wallPaint, stream); 757 } 758 759 /** 760 * Provides serialization support. 761 * 762 * @param stream the input stream. 763 * 764 * @throws IOException if there is an I/O error. 765 * @throws ClassNotFoundException if there is a classpath problem. 766 */ 767 private void readObject(ObjectInputStream stream) 768 throws IOException, ClassNotFoundException { 769 stream.defaultReadObject(); 770 this.wallPaint = SerialUtilities.readPaint(stream); 771 } 772 773 }