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, Serializable { 147 148 /** For serialization. */ 149 private static final long serialVersionUID = 7686976503536003636L; 150 151 /** The default x-offset for the 3D effect. */ 152 public static final double DEFAULT_X_OFFSET = 12.0; 153 154 /** The default y-offset for the 3D effect. */ 155 public static final double DEFAULT_Y_OFFSET = 8.0; 156 157 /** The default wall paint. */ 158 public static final Paint DEFAULT_WALL_PAINT = new Color(0xDD, 0xDD, 0xDD); 159 160 /** The size of x-offset for the 3D effect. */ 161 private double xOffset; 162 163 /** The size of y-offset for the 3D effect. */ 164 private double yOffset; 165 166 /** The paint used to shade the left and lower 3D wall. */ 167 private transient Paint wallPaint; 168 169 /** 170 * Default constructor, creates a renderer with a default '3D effect'. 171 */ 172 public BarRenderer3D() { 173 this(DEFAULT_X_OFFSET, DEFAULT_Y_OFFSET); 174 } 175 176 /** 177 * Constructs a new renderer with the specified '3D effect'. 178 * 179 * @param xOffset the x-offset for the 3D effect. 180 * @param yOffset the y-offset for the 3D effect. 181 */ 182 public BarRenderer3D(double xOffset, double yOffset) { 183 184 super(); 185 this.xOffset = xOffset; 186 this.yOffset = yOffset; 187 this.wallPaint = DEFAULT_WALL_PAINT; 188 // set the default item label positions 189 ItemLabelPosition p1 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12, 190 TextAnchor.TOP_CENTER); 191 setBasePositiveItemLabelPosition(p1); 192 ItemLabelPosition p2 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12, 193 TextAnchor.TOP_CENTER); 194 setBaseNegativeItemLabelPosition(p2); 195 196 } 197 198 /** 199 * Returns the x-offset for the 3D effect. 200 * 201 * @return The 3D effect. 202 * 203 * @see #getYOffset() 204 */ 205 public double getXOffset() { 206 return this.xOffset; 207 } 208 209 /** 210 * Returns the y-offset for the 3D effect. 211 * 212 * @return The 3D effect. 213 */ 214 public double getYOffset() { 215 return this.yOffset; 216 } 217 218 /** 219 * Returns the paint used to highlight the left and bottom wall in the plot 220 * background. 221 * 222 * @return The paint. 223 * 224 * @see #setWallPaint(Paint) 225 */ 226 public Paint getWallPaint() { 227 return this.wallPaint; 228 } 229 230 /** 231 * Sets the paint used to hightlight the left and bottom walls in the plot 232 * background, and sends a {@link RendererChangeEvent} to all registered 233 * listeners. 234 * 235 * @param paint the paint (<code>null</code> not permitted). 236 * 237 * @see #getWallPaint() 238 */ 239 public void setWallPaint(Paint paint) { 240 if (paint == null) { 241 throw new IllegalArgumentException("Null 'paint' argument."); 242 } 243 this.wallPaint = paint; 244 fireChangeEvent(); 245 } 246 247 248 /** 249 * Initialises the renderer and returns a state object that will be passed 250 * to subsequent calls to the drawItem method. This method gets called 251 * once at the start of the process of drawing a chart. 252 * 253 * @param g2 the graphics device. 254 * @param dataArea the area in which the data is to be plotted. 255 * @param plot the plot. 256 * @param rendererIndex the renderer index. 257 * @param info collects chart rendering information for return to caller. 258 * 259 * @return The renderer state. 260 */ 261 public CategoryItemRendererState initialise(Graphics2D g2, 262 Rectangle2D dataArea, 263 CategoryPlot plot, 264 int rendererIndex, 265 PlotRenderingInfo info) { 266 267 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 268 dataArea.getY() + getYOffset(), dataArea.getWidth() 269 - getXOffset(), dataArea.getHeight() - getYOffset()); 270 CategoryItemRendererState state = super.initialise(g2, adjusted, plot, 271 rendererIndex, info); 272 return state; 273 274 } 275 276 /** 277 * Draws the background for the plot. 278 * 279 * @param g2 the graphics device. 280 * @param plot the plot. 281 * @param dataArea the area inside the axes. 282 */ 283 public void drawBackground(Graphics2D g2, CategoryPlot plot, 284 Rectangle2D dataArea) { 285 286 float x0 = (float) dataArea.getX(); 287 float x1 = x0 + (float) Math.abs(this.xOffset); 288 float x3 = (float) dataArea.getMaxX(); 289 float x2 = x3 - (float) Math.abs(this.xOffset); 290 291 float y0 = (float) dataArea.getMaxY(); 292 float y1 = y0 - (float) Math.abs(this.yOffset); 293 float y3 = (float) dataArea.getMinY(); 294 float y2 = y3 + (float) Math.abs(this.yOffset); 295 296 GeneralPath clip = new GeneralPath(); 297 clip.moveTo(x0, y0); 298 clip.lineTo(x0, y2); 299 clip.lineTo(x1, y3); 300 clip.lineTo(x3, y3); 301 clip.lineTo(x3, y1); 302 clip.lineTo(x2, y0); 303 clip.closePath(); 304 305 Composite originalComposite = g2.getComposite(); 306 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 307 plot.getBackgroundAlpha())); 308 309 // fill background... 310 Paint backgroundPaint = plot.getBackgroundPaint(); 311 if (backgroundPaint != null) { 312 g2.setPaint(backgroundPaint); 313 g2.fill(clip); 314 } 315 316 GeneralPath leftWall = new GeneralPath(); 317 leftWall.moveTo(x0, y0); 318 leftWall.lineTo(x0, y2); 319 leftWall.lineTo(x1, y3); 320 leftWall.lineTo(x1, y1); 321 leftWall.closePath(); 322 g2.setPaint(getWallPaint()); 323 g2.fill(leftWall); 324 325 GeneralPath bottomWall = new GeneralPath(); 326 bottomWall.moveTo(x0, y0); 327 bottomWall.lineTo(x1, y1); 328 bottomWall.lineTo(x3, y1); 329 bottomWall.lineTo(x2, y0); 330 bottomWall.closePath(); 331 g2.setPaint(getWallPaint()); 332 g2.fill(bottomWall); 333 334 // highlight the background corners... 335 g2.setPaint(Color.lightGray); 336 Line2D corner = new Line2D.Double(x0, y0, x1, y1); 337 g2.draw(corner); 338 corner.setLine(x1, y1, x1, y3); 339 g2.draw(corner); 340 corner.setLine(x1, y1, x3, y1); 341 g2.draw(corner); 342 343 // draw background image, if there is one... 344 Image backgroundImage = plot.getBackgroundImage(); 345 if (backgroundImage != null) { 346 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX() 347 + getXOffset(), dataArea.getY(), 348 dataArea.getWidth() - getXOffset(), 349 dataArea.getHeight() - getYOffset()); 350 plot.drawBackgroundImage(g2, adjusted); 351 } 352 353 g2.setComposite(originalComposite); 354 355 } 356 357 /** 358 * Draws the outline for the plot. 359 * 360 * @param g2 the graphics device. 361 * @param plot the plot. 362 * @param dataArea the area inside the axes. 363 */ 364 public void drawOutline(Graphics2D g2, CategoryPlot plot, 365 Rectangle2D dataArea) { 366 367 float x0 = (float) dataArea.getX(); 368 float x1 = x0 + (float) Math.abs(this.xOffset); 369 float x3 = (float) dataArea.getMaxX(); 370 float x2 = x3 - (float) Math.abs(this.xOffset); 371 372 float y0 = (float) dataArea.getMaxY(); 373 float y1 = y0 - (float) Math.abs(this.yOffset); 374 float y3 = (float) dataArea.getMinY(); 375 float y2 = y3 + (float) Math.abs(this.yOffset); 376 377 GeneralPath clip = new GeneralPath(); 378 clip.moveTo(x0, y0); 379 clip.lineTo(x0, y2); 380 clip.lineTo(x1, y3); 381 clip.lineTo(x3, y3); 382 clip.lineTo(x3, y1); 383 clip.lineTo(x2, y0); 384 clip.closePath(); 385 386 // put an outline around the data area... 387 Stroke outlineStroke = plot.getOutlineStroke(); 388 Paint outlinePaint = plot.getOutlinePaint(); 389 if ((outlineStroke != null) && (outlinePaint != null)) { 390 g2.setStroke(outlineStroke); 391 g2.setPaint(outlinePaint); 392 g2.draw(clip); 393 } 394 395 } 396 397 /** 398 * Draws a grid line against the domain axis. 399 * 400 * @param g2 the graphics device. 401 * @param plot the plot. 402 * @param dataArea the area for plotting data (not yet adjusted for any 403 * 3D effect). 404 * @param value the Java2D value at which the grid line should be drawn. 405 * 406 */ 407 public void drawDomainGridline(Graphics2D g2, 408 CategoryPlot plot, 409 Rectangle2D dataArea, 410 double value) { 411 412 Line2D line1 = null; 413 Line2D line2 = null; 414 PlotOrientation orientation = plot.getOrientation(); 415 if (orientation == PlotOrientation.HORIZONTAL) { 416 double y0 = value; 417 double y1 = value - getYOffset(); 418 double x0 = dataArea.getMinX(); 419 double x1 = x0 + getXOffset(); 420 double x2 = dataArea.getMaxX(); 421 line1 = new Line2D.Double(x0, y0, x1, y1); 422 line2 = new Line2D.Double(x1, y1, x2, y1); 423 } 424 else if (orientation == PlotOrientation.VERTICAL) { 425 double x0 = value; 426 double x1 = value + getXOffset(); 427 double y0 = dataArea.getMaxY(); 428 double y1 = y0 - getYOffset(); 429 double y2 = dataArea.getMinY(); 430 line1 = new Line2D.Double(x0, y0, x1, y1); 431 line2 = new Line2D.Double(x1, y1, x1, y2); 432 } 433 Paint paint = plot.getDomainGridlinePaint(); 434 Stroke stroke = plot.getDomainGridlineStroke(); 435 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 436 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 437 g2.draw(line1); 438 g2.draw(line2); 439 440 } 441 442 /** 443 * Draws a grid line against the range axis. 444 * 445 * @param g2 the graphics device. 446 * @param plot the plot. 447 * @param axis the value axis. 448 * @param dataArea the area for plotting data (not yet adjusted for any 449 * 3D effect). 450 * @param value the value at which the grid line should be drawn. 451 * 452 */ 453 public void drawRangeGridline(Graphics2D g2, 454 CategoryPlot plot, 455 ValueAxis axis, 456 Rectangle2D dataArea, 457 double value) { 458 459 Range range = axis.getRange(); 460 461 if (!range.contains(value)) { 462 return; 463 } 464 465 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 466 dataArea.getY() + getYOffset(), dataArea.getWidth() 467 - getXOffset(), dataArea.getHeight() - getYOffset()); 468 469 Line2D line1 = null; 470 Line2D line2 = null; 471 PlotOrientation orientation = plot.getOrientation(); 472 if (orientation == PlotOrientation.HORIZONTAL) { 473 double x0 = axis.valueToJava2D(value, adjusted, 474 plot.getRangeAxisEdge()); 475 double x1 = x0 + getXOffset(); 476 double y0 = dataArea.getMaxY(); 477 double y1 = y0 - getYOffset(); 478 double y2 = dataArea.getMinY(); 479 line1 = new Line2D.Double(x0, y0, x1, y1); 480 line2 = new Line2D.Double(x1, y1, x1, y2); 481 } 482 else if (orientation == PlotOrientation.VERTICAL) { 483 double y0 = axis.valueToJava2D(value, adjusted, 484 plot.getRangeAxisEdge()); 485 double y1 = y0 - getYOffset(); 486 double x0 = dataArea.getMinX(); 487 double x1 = x0 + getXOffset(); 488 double x2 = dataArea.getMaxX(); 489 line1 = new Line2D.Double(x0, y0, x1, y1); 490 line2 = new Line2D.Double(x1, y1, x2, y1); 491 } 492 Paint paint = plot.getRangeGridlinePaint(); 493 Stroke stroke = plot.getRangeGridlineStroke(); 494 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 495 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 496 g2.draw(line1); 497 g2.draw(line2); 498 499 } 500 501 /** 502 * Draws a range marker. 503 * 504 * @param g2 the graphics device. 505 * @param plot the plot. 506 * @param axis the value axis. 507 * @param marker the marker. 508 * @param dataArea the area for plotting data (not including 3D effect). 509 */ 510 public void drawRangeMarker(Graphics2D g2, 511 CategoryPlot plot, 512 ValueAxis axis, 513 Marker marker, 514 Rectangle2D dataArea) { 515 516 517 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 518 dataArea.getY() + getYOffset(), dataArea.getWidth() 519 - getXOffset(), dataArea.getHeight() - getYOffset()); 520 if (marker instanceof ValueMarker) { 521 ValueMarker vm = (ValueMarker) marker; 522 double value = vm.getValue(); 523 Range range = axis.getRange(); 524 if (!range.contains(value)) { 525 return; 526 } 527 528 GeneralPath path = null; 529 PlotOrientation orientation = plot.getOrientation(); 530 if (orientation == PlotOrientation.HORIZONTAL) { 531 float x = (float) axis.valueToJava2D(value, adjusted, 532 plot.getRangeAxisEdge()); 533 float y = (float) adjusted.getMaxY(); 534 path = new GeneralPath(); 535 path.moveTo(x, y); 536 path.lineTo((float) (x + getXOffset()), 537 y - (float) getYOffset()); 538 path.lineTo((float) (x + getXOffset()), 539 (float) (adjusted.getMinY() - getYOffset())); 540 path.lineTo(x, (float) adjusted.getMinY()); 541 path.closePath(); 542 } 543 else if (orientation == PlotOrientation.VERTICAL) { 544 float y = (float) axis.valueToJava2D(value, adjusted, 545 plot.getRangeAxisEdge()); 546 float x = (float) dataArea.getX(); 547 path = new GeneralPath(); 548 path.moveTo(x, y); 549 path.lineTo(x + (float) this.xOffset, y - (float) this.yOffset); 550 path.lineTo((float) (adjusted.getMaxX() + this.xOffset), 551 y - (float) this.yOffset); 552 path.lineTo((float) (adjusted.getMaxX()), y); 553 path.closePath(); 554 } 555 g2.setPaint(marker.getPaint()); 556 g2.fill(path); 557 g2.setPaint(marker.getOutlinePaint()); 558 g2.draw(path); 559 560 String label = marker.getLabel(); 561 RectangleAnchor anchor = marker.getLabelAnchor(); 562 if (label != null) { 563 Font labelFont = marker.getLabelFont(); 564 g2.setFont(labelFont); 565 g2.setPaint(marker.getLabelPaint()); 566 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 567 g2, orientation, dataArea, path.getBounds2D(), 568 marker.getLabelOffset(), LengthAdjustmentType.EXPAND, 569 anchor); 570 TextUtilities.drawAlignedString(label, g2, 571 (float) coordinates.getX(), (float) coordinates.getY(), 572 marker.getLabelTextAnchor()); 573 } 574 575 } 576 else { 577 super.drawRangeMarker(g2, plot, axis, marker, adjusted); 578 // TODO: draw the interval marker with a 3D effect 579 } 580 } 581 582 /** 583 * Draws a 3D bar to represent one data item. 584 * 585 * @param g2 the graphics device. 586 * @param state the renderer state. 587 * @param dataArea the area for plotting the data. 588 * @param plot the plot. 589 * @param domainAxis the domain axis. 590 * @param rangeAxis the range axis. 591 * @param dataset the dataset. 592 * @param row the row index (zero-based). 593 * @param column the column index (zero-based). 594 * @param pass the pass index. 595 */ 596 public void drawItem(Graphics2D g2, 597 CategoryItemRendererState state, 598 Rectangle2D dataArea, 599 CategoryPlot plot, 600 CategoryAxis domainAxis, 601 ValueAxis rangeAxis, 602 CategoryDataset dataset, 603 int row, 604 int column, 605 int pass) { 606 607 // check the value we are plotting... 608 Number dataValue = dataset.getValue(row, column); 609 if (dataValue == null) { 610 return; 611 } 612 613 double value = dataValue.doubleValue(); 614 615 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 616 dataArea.getY() + getYOffset(), 617 dataArea.getWidth() - getXOffset(), 618 dataArea.getHeight() - getYOffset()); 619 620 PlotOrientation orientation = plot.getOrientation(); 621 622 double barW0 = calculateBarW0(plot, orientation, adjusted, domainAxis, 623 state, row, column); 624 double[] barL0L1 = calculateBarL0L1(value); 625 if (barL0L1 == null) { 626 return; // the bar is not visible 627 } 628 629 RectangleEdge edge = plot.getRangeAxisEdge(); 630 double transL0 = rangeAxis.valueToJava2D(barL0L1[0], adjusted, edge); 631 double transL1 = rangeAxis.valueToJava2D(barL0L1[1], adjusted, edge); 632 double barL0 = Math.min(transL0, transL1); 633 double barLength = Math.abs(transL1 - transL0); 634 635 // draw the bar... 636 Rectangle2D bar = null; 637 if (orientation == PlotOrientation.HORIZONTAL) { 638 bar = new Rectangle2D.Double(barL0, barW0, barLength, 639 state.getBarWidth()); 640 } 641 else { 642 bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(), 643 barLength); 644 } 645 Paint itemPaint = getItemPaint(row, column); 646 g2.setPaint(itemPaint); 647 g2.fill(bar); 648 649 double x0 = bar.getMinX(); 650 double x1 = x0 + getXOffset(); 651 double x2 = bar.getMaxX(); 652 double x3 = x2 + getXOffset(); 653 654 double y0 = bar.getMinY() - getYOffset(); 655 double y1 = bar.getMinY(); 656 double y2 = bar.getMaxY() - getYOffset(); 657 double y3 = bar.getMaxY(); 658 659 GeneralPath bar3dRight = null; 660 GeneralPath bar3dTop = null; 661 if (barLength > 0.0) { 662 bar3dRight = new GeneralPath(); 663 bar3dRight.moveTo((float) x2, (float) y3); 664 bar3dRight.lineTo((float) x2, (float) y1); 665 bar3dRight.lineTo((float) x3, (float) y0); 666 bar3dRight.lineTo((float) x3, (float) y2); 667 bar3dRight.closePath(); 668 669 if (itemPaint instanceof Color) { 670 g2.setPaint(((Color) itemPaint).darker()); 671 } 672 g2.fill(bar3dRight); 673 } 674 675 bar3dTop = new GeneralPath(); 676 bar3dTop.moveTo((float) x0, (float) y1); 677 bar3dTop.lineTo((float) x1, (float) y0); 678 bar3dTop.lineTo((float) x3, (float) y0); 679 bar3dTop.lineTo((float) x2, (float) y1); 680 bar3dTop.closePath(); 681 g2.fill(bar3dTop); 682 683 if (isDrawBarOutline() 684 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 685 g2.setStroke(getItemOutlineStroke(row, column)); 686 g2.setPaint(getItemOutlinePaint(row, column)); 687 g2.draw(bar); 688 if (bar3dRight != null) { 689 g2.draw(bar3dRight); 690 } 691 if (bar3dTop != null) { 692 g2.draw(bar3dTop); 693 } 694 } 695 696 CategoryItemLabelGenerator generator 697 = getItemLabelGenerator(row, column); 698 if (generator != null && isItemLabelVisible(row, column)) { 699 drawItemLabel(g2, dataset, row, column, plot, generator, bar, 700 (value < 0.0)); 701 } 702 703 // add an item entity, if this information is being collected 704 EntityCollection entities = state.getEntityCollection(); 705 if (entities != null) { 706 GeneralPath barOutline = new GeneralPath(); 707 barOutline.moveTo((float) x0, (float) y3); 708 barOutline.lineTo((float) x0, (float) y1); 709 barOutline.lineTo((float) x1, (float) y0); 710 barOutline.lineTo((float) x3, (float) y0); 711 barOutline.lineTo((float) x3, (float) y2); 712 barOutline.lineTo((float) x2, (float) y3); 713 barOutline.closePath(); 714 addItemEntity(entities, dataset, row, column, barOutline); 715 } 716 717 } 718 719 /** 720 * Tests this renderer for equality with an arbitrary object. 721 * 722 * @param obj the object (<code>null</code> permitted). 723 * 724 * @return A boolean. 725 */ 726 public boolean equals(Object obj) { 727 if (obj == this) { 728 return true; 729 } 730 if (!(obj instanceof BarRenderer3D)) { 731 return false; 732 } 733 BarRenderer3D that = (BarRenderer3D) obj; 734 if (this.xOffset != that.xOffset) { 735 return false; 736 } 737 if (this.yOffset != that.yOffset) { 738 return false; 739 } 740 if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) { 741 return false; 742 } 743 return super.equals(obj); 744 } 745 746 /** 747 * Provides serialization support. 748 * 749 * @param stream the output stream. 750 * 751 * @throws IOException if there is an I/O error. 752 */ 753 private void writeObject(ObjectOutputStream stream) throws IOException { 754 stream.defaultWriteObject(); 755 SerialUtilities.writePaint(this.wallPaint, stream); 756 } 757 758 /** 759 * Provides serialization support. 760 * 761 * @param stream the input stream. 762 * 763 * @throws IOException if there is an I/O error. 764 * @throws ClassNotFoundException if there is a classpath problem. 765 */ 766 private void readObject(ObjectInputStream stream) 767 throws IOException, ClassNotFoundException { 768 stream.defaultReadObject(); 769 this.wallPaint = SerialUtilities.readPaint(stream); 770 } 771 772 }