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 * $Id: BarRenderer3D.java,v 1.10.2.6 2007/01/17 14:16:11 mungady Exp $ 041 * 042 * Changes 043 * ------- 044 * 31-Oct-2001 : First version, contributed by Serge V. Grachov (DG); 045 * 15-Nov-2001 : Modified to allow for null data values (DG); 046 * 13-Dec-2001 : Added tooltips (DG); 047 * 16-Jan-2002 : Added fix for single category or single series datasets, 048 * pointed out by Taoufik Romdhane (DG); 049 * 24-May-2002 : Incorporated tooltips into chart entities (DG); 050 * 11-Jun-2002 : Added check for (permitted) null info object, bug and fix 051 * reported by David Basten. Also updated Javadocs. (DG); 052 * 19-Jun-2002 : Added code to draw labels on bars (TL); 053 * 26-Jun-2002 : Added bar clipping to avoid PRExceptions (DG); 054 * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs 055 * for HTML image maps (RA); 056 * 06-Aug-2002 : Value labels now use number formatter, thanks to Milo 057 * Simpson (DG); 058 * 08-Aug-2002 : Applied fixed in bug id 592218 (DG); 059 * 20-Sep-2002 : Added fix for categoryPaint by Rich Unger, and fixed errors 060 * reported by Checkstyle (DG); 061 * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 062 * CategoryToolTipGenerator interface (DG); 063 * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG); 064 * 06-Nov-2002 : Moved to the com.jrefinery.chart.renderer package (DG); 065 * 28-Jan-2003 : Added an attribute to control the shading of the left and 066 * bottom walls in the plot background (DG); 067 * 25-Mar-2003 : Implemented Serializable (DG); 068 * 10-Apr-2003 : Removed category paint usage (DG); 069 * 13-May-2003 : Renamed VerticalBarRenderer3D --> BarRenderer3D and merged with 070 * HorizontalBarRenderer3D (DG); 071 * 30-Jul-2003 : Modified entity constructor (CZ); 072 * 19-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 073 * 07-Oct-2003 : Added renderer state (DG); 074 * 08-Oct-2003 : Removed clipping (replaced with flag in CategoryPlot to 075 * control order in which the data items are processed) (DG); 076 * 20-Oct-2003 : Fixed bug (outline stroke not being used for bar 077 * outlines) (DG); 078 * 21-Oct-2003 : Bar width moved into CategoryItemRendererState (DG); 079 * 24-Nov-2003 : Fixed bug 846324 (item labels not showing) (DG); 080 * 27-Nov-2003 : Added code to respect maxBarWidth setting (DG); 081 * 02-Feb-2004 : Fixed bug where 'drawBarOutline' flag is not respected (DG); 082 * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste 083 * overriding easier (DG); 084 * 04-Oct-2004 : Fixed bug with item label positioning when plot alignment is 085 * horizontal (DG); 086 * 05-Nov-2004 : Modified drawItem() signature (DG); 087 * 20-Apr-2005 : Renamed CategoryLabelGenerator 088 * --> CategoryItemLabelGenerator (DG); 089 * 25-Apr-2005 : Override initialise() method to fix bug 1189642 (DG); 090 * 09-Jun-2005 : Use addEntityItem from super class (DG); 091 * ------------- JFREECHART 1.0.x --------------------------------------------- 092 * 07-Dec-2006 : Implemented equals() override (DG); 093 * 17-Jan-2007 : Fixed bug in drawDomainGridline() method (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 setPositiveItemLabelPosition(p1); 193 ItemLabelPosition p2 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12, 194 TextAnchor.TOP_CENTER); 195 setNegativeItemLabelPosition(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 notifyListeners(new RendererChangeEvent(this)); 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 // fill background... 307 Paint backgroundPaint = plot.getBackgroundPaint(); 308 if (backgroundPaint != null) { 309 g2.setPaint(backgroundPaint); 310 g2.fill(clip); 311 } 312 313 GeneralPath leftWall = new GeneralPath(); 314 leftWall.moveTo(x0, y0); 315 leftWall.lineTo(x0, y2); 316 leftWall.lineTo(x1, y3); 317 leftWall.lineTo(x1, y1); 318 leftWall.closePath(); 319 g2.setPaint(getWallPaint()); 320 g2.fill(leftWall); 321 322 GeneralPath bottomWall = new GeneralPath(); 323 bottomWall.moveTo(x0, y0); 324 bottomWall.lineTo(x1, y1); 325 bottomWall.lineTo(x3, y1); 326 bottomWall.lineTo(x2, y0); 327 bottomWall.closePath(); 328 g2.setPaint(getWallPaint()); 329 g2.fill(bottomWall); 330 331 // higlight the background corners... 332 g2.setPaint(Color.lightGray); 333 Line2D corner = new Line2D.Double(x0, y0, x1, y1); 334 g2.draw(corner); 335 corner.setLine(x1, y1, x1, y3); 336 g2.draw(corner); 337 corner.setLine(x1, y1, x3, y1); 338 g2.draw(corner); 339 340 // draw background image, if there is one... 341 Image backgroundImage = plot.getBackgroundImage(); 342 if (backgroundImage != null) { 343 Composite originalComposite = g2.getComposite(); 344 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 345 plot.getBackgroundAlpha())); 346 g2.drawImage(backgroundImage, (int) x1, (int) y3, 347 (int) (x3 - x1 + 1), (int) (y1 - y3 + 1), null); 348 g2.setComposite(originalComposite); 349 } 350 351 } 352 353 /** 354 * Draws the outline for the plot. 355 * 356 * @param g2 the graphics device. 357 * @param plot the plot. 358 * @param dataArea the area inside the axes. 359 */ 360 public void drawOutline(Graphics2D g2, CategoryPlot plot, 361 Rectangle2D dataArea) { 362 363 float x0 = (float) dataArea.getX(); 364 float x1 = x0 + (float) Math.abs(this.xOffset); 365 float x3 = (float) dataArea.getMaxX(); 366 float x2 = x3 - (float) Math.abs(this.xOffset); 367 368 float y0 = (float) dataArea.getMaxY(); 369 float y1 = y0 - (float) Math.abs(this.yOffset); 370 float y3 = (float) dataArea.getMinY(); 371 float y2 = y3 + (float) Math.abs(this.yOffset); 372 373 GeneralPath clip = new GeneralPath(); 374 clip.moveTo(x0, y0); 375 clip.lineTo(x0, y2); 376 clip.lineTo(x1, y3); 377 clip.lineTo(x3, y3); 378 clip.lineTo(x3, y1); 379 clip.lineTo(x2, y0); 380 clip.closePath(); 381 382 // put an outline around the data area... 383 Stroke outlineStroke = plot.getOutlineStroke(); 384 Paint outlinePaint = plot.getOutlinePaint(); 385 if ((outlineStroke != null) && (outlinePaint != null)) { 386 g2.setStroke(outlineStroke); 387 g2.setPaint(outlinePaint); 388 g2.draw(clip); 389 } 390 391 } 392 393 /** 394 * Draws a grid line against the domain axis. 395 * 396 * @param g2 the graphics device. 397 * @param plot the plot. 398 * @param dataArea the area for plotting data (not yet adjusted for any 399 * 3D effect). 400 * @param value the Java2D value at which the grid line should be drawn. 401 * 402 */ 403 public void drawDomainGridline(Graphics2D g2, 404 CategoryPlot plot, 405 Rectangle2D dataArea, 406 double value) { 407 408 Line2D line1 = null; 409 Line2D line2 = null; 410 PlotOrientation orientation = plot.getOrientation(); 411 if (orientation == PlotOrientation.HORIZONTAL) { 412 double y0 = value; 413 double y1 = value - getYOffset(); 414 double x0 = dataArea.getMinX(); 415 double x1 = x0 + getXOffset(); 416 double x2 = dataArea.getMaxX(); 417 line1 = new Line2D.Double(x0, y0, x1, y1); 418 line2 = new Line2D.Double(x1, y1, x2, y1); 419 } 420 else if (orientation == PlotOrientation.VERTICAL) { 421 double x0 = value; 422 double x1 = value + getXOffset(); 423 double y0 = dataArea.getMaxY(); 424 double y1 = y0 - getYOffset(); 425 double y2 = dataArea.getMinY(); 426 line1 = new Line2D.Double(x0, y0, x1, y1); 427 line2 = new Line2D.Double(x1, y1, x1, y2); 428 } 429 Paint paint = plot.getDomainGridlinePaint(); 430 Stroke stroke = plot.getDomainGridlineStroke(); 431 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 432 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 433 g2.draw(line1); 434 g2.draw(line2); 435 436 } 437 438 /** 439 * Draws a grid line against the range axis. 440 * 441 * @param g2 the graphics device. 442 * @param plot the plot. 443 * @param axis the value axis. 444 * @param dataArea the area for plotting data (not yet adjusted for any 445 * 3D effect). 446 * @param value the value at which the grid line should be drawn. 447 * 448 */ 449 public void drawRangeGridline(Graphics2D g2, 450 CategoryPlot plot, 451 ValueAxis axis, 452 Rectangle2D dataArea, 453 double value) { 454 455 Range range = axis.getRange(); 456 457 if (!range.contains(value)) { 458 return; 459 } 460 461 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 462 dataArea.getY() + getYOffset(), dataArea.getWidth() 463 - getXOffset(), dataArea.getHeight() - getYOffset()); 464 465 Line2D line1 = null; 466 Line2D line2 = null; 467 PlotOrientation orientation = plot.getOrientation(); 468 if (orientation == PlotOrientation.HORIZONTAL) { 469 double x0 = axis.valueToJava2D(value, adjusted, 470 plot.getRangeAxisEdge()); 471 double x1 = x0 + getXOffset(); 472 double y0 = dataArea.getMaxY(); 473 double y1 = y0 - getYOffset(); 474 double y2 = dataArea.getMinY(); 475 line1 = new Line2D.Double(x0, y0, x1, y1); 476 line2 = new Line2D.Double(x1, y1, x1, y2); 477 } 478 else if (orientation == PlotOrientation.VERTICAL) { 479 double y0 = axis.valueToJava2D(value, adjusted, 480 plot.getRangeAxisEdge()); 481 double y1 = y0 - getYOffset(); 482 double x0 = dataArea.getMinX(); 483 double x1 = x0 + getXOffset(); 484 double x2 = dataArea.getMaxX(); 485 line1 = new Line2D.Double(x0, y0, x1, y1); 486 line2 = new Line2D.Double(x1, y1, x2, y1); 487 } 488 Paint paint = plot.getRangeGridlinePaint(); 489 Stroke stroke = plot.getRangeGridlineStroke(); 490 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 491 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 492 g2.draw(line1); 493 g2.draw(line2); 494 495 } 496 497 /** 498 * Draws a range marker. 499 * 500 * @param g2 the graphics device. 501 * @param plot the plot. 502 * @param axis the value axis. 503 * @param marker the marker. 504 * @param dataArea the area for plotting data (not including 3D effect). 505 */ 506 public void drawRangeMarker(Graphics2D g2, 507 CategoryPlot plot, 508 ValueAxis axis, 509 Marker marker, 510 Rectangle2D dataArea) { 511 512 if (marker instanceof ValueMarker) { 513 ValueMarker vm = (ValueMarker) marker; 514 double value = vm.getValue(); 515 Range range = axis.getRange(); 516 if (!range.contains(value)) { 517 return; 518 } 519 520 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 521 dataArea.getY() + getYOffset(), dataArea.getWidth() 522 - getXOffset(), dataArea.getHeight() - getYOffset()); 523 524 GeneralPath path = null; 525 PlotOrientation orientation = plot.getOrientation(); 526 if (orientation == PlotOrientation.HORIZONTAL) { 527 float x = (float) axis.valueToJava2D(value, adjusted, 528 plot.getRangeAxisEdge()); 529 float y = (float) adjusted.getMaxY(); 530 path = new GeneralPath(); 531 path.moveTo(x, y); 532 path.lineTo((float) (x + getXOffset()), 533 y - (float) getYOffset()); 534 path.lineTo((float) (x + getXOffset()), 535 (float) (adjusted.getMinY() - getYOffset())); 536 path.lineTo(x, (float) adjusted.getMinY()); 537 path.closePath(); 538 } 539 else if (orientation == PlotOrientation.VERTICAL) { 540 float y = (float) axis.valueToJava2D(value, adjusted, 541 plot.getRangeAxisEdge()); 542 float x = (float) dataArea.getX(); 543 path = new GeneralPath(); 544 path.moveTo(x, y); 545 path.lineTo(x + (float) this.xOffset, y - (float) this.yOffset); 546 path.lineTo((float) (adjusted.getMaxX() + this.xOffset), 547 y - (float) this.yOffset); 548 path.lineTo((float) (adjusted.getMaxX()), y); 549 path.closePath(); 550 } 551 g2.setPaint(marker.getPaint()); 552 g2.fill(path); 553 g2.setPaint(marker.getOutlinePaint()); 554 g2.draw(path); 555 556 String label = marker.getLabel(); 557 RectangleAnchor anchor = marker.getLabelAnchor(); 558 if (label != null) { 559 Font labelFont = marker.getLabelFont(); 560 g2.setFont(labelFont); 561 g2.setPaint(marker.getLabelPaint()); 562 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 563 g2, orientation, dataArea, path.getBounds2D(), 564 marker.getLabelOffset(), LengthAdjustmentType.EXPAND, 565 anchor); 566 TextUtilities.drawAlignedString(label, g2, 567 (float) coordinates.getX(), (float) coordinates.getY(), 568 marker.getLabelTextAnchor()); 569 } 570 571 } 572 else { 573 super.drawRangeMarker(g2, plot, axis, marker, dataArea); 574 // TODO: draw the interval marker with a 3D effect 575 } 576 } 577 578 /** 579 * Draws a 3D bar to represent one data item. 580 * 581 * @param g2 the graphics device. 582 * @param state the renderer state. 583 * @param dataArea the area for plotting the data. 584 * @param plot the plot. 585 * @param domainAxis the domain axis. 586 * @param rangeAxis the range axis. 587 * @param dataset the dataset. 588 * @param row the row index (zero-based). 589 * @param column the column index (zero-based). 590 * @param pass the pass index. 591 */ 592 public void drawItem(Graphics2D g2, 593 CategoryItemRendererState state, 594 Rectangle2D dataArea, 595 CategoryPlot plot, 596 CategoryAxis domainAxis, 597 ValueAxis rangeAxis, 598 CategoryDataset dataset, 599 int row, 600 int column, 601 int pass) { 602 603 // check the value we are plotting... 604 Number dataValue = dataset.getValue(row, column); 605 if (dataValue == null) { 606 return; 607 } 608 609 double value = dataValue.doubleValue(); 610 611 Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 612 dataArea.getY() + getYOffset(), 613 dataArea.getWidth() - getXOffset(), 614 dataArea.getHeight() - getYOffset()); 615 616 PlotOrientation orientation = plot.getOrientation(); 617 618 double barW0 = calculateBarW0(plot, orientation, adjusted, domainAxis, 619 state, row, column); 620 double[] barL0L1 = calculateBarL0L1(value); 621 if (barL0L1 == null) { 622 return; // the bar is not visible 623 } 624 625 RectangleEdge edge = plot.getRangeAxisEdge(); 626 double transL0 = rangeAxis.valueToJava2D(barL0L1[0], adjusted, edge); 627 double transL1 = rangeAxis.valueToJava2D(barL0L1[1], adjusted, edge); 628 double barL0 = Math.min(transL0, transL1); 629 double barLength = Math.abs(transL1 - transL0); 630 631 // draw the bar... 632 Rectangle2D bar = null; 633 if (orientation == PlotOrientation.HORIZONTAL) { 634 bar = new Rectangle2D.Double(barL0, barW0, barLength, 635 state.getBarWidth()); 636 } 637 else { 638 bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(), 639 barLength); 640 } 641 Paint itemPaint = getItemPaint(row, column); 642 g2.setPaint(itemPaint); 643 g2.fill(bar); 644 645 double x0 = bar.getMinX(); 646 double x1 = x0 + getXOffset(); 647 double x2 = bar.getMaxX(); 648 double x3 = x2 + getXOffset(); 649 650 double y0 = bar.getMinY() - getYOffset(); 651 double y1 = bar.getMinY(); 652 double y2 = bar.getMaxY() - getYOffset(); 653 double y3 = bar.getMaxY(); 654 655 GeneralPath bar3dRight = null; 656 GeneralPath bar3dTop = null; 657 if (barLength > 0.0) { 658 bar3dRight = new GeneralPath(); 659 bar3dRight.moveTo((float) x2, (float) y3); 660 bar3dRight.lineTo((float) x2, (float) y1); 661 bar3dRight.lineTo((float) x3, (float) y0); 662 bar3dRight.lineTo((float) x3, (float) y2); 663 bar3dRight.closePath(); 664 665 if (itemPaint instanceof Color) { 666 g2.setPaint(((Color) itemPaint).darker()); 667 } 668 g2.fill(bar3dRight); 669 } 670 671 bar3dTop = new GeneralPath(); 672 bar3dTop.moveTo((float) x0, (float) y1); 673 bar3dTop.lineTo((float) x1, (float) y0); 674 bar3dTop.lineTo((float) x3, (float) y0); 675 bar3dTop.lineTo((float) x2, (float) y1); 676 bar3dTop.closePath(); 677 g2.fill(bar3dTop); 678 679 if (isDrawBarOutline() 680 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 681 g2.setStroke(getItemOutlineStroke(row, column)); 682 g2.setPaint(getItemOutlinePaint(row, column)); 683 g2.draw(bar); 684 if (bar3dRight != null) { 685 g2.draw(bar3dRight); 686 } 687 if (bar3dTop != null) { 688 g2.draw(bar3dTop); 689 } 690 } 691 692 CategoryItemLabelGenerator generator 693 = getItemLabelGenerator(row, column); 694 if (generator != null && isItemLabelVisible(row, column)) { 695 drawItemLabel(g2, dataset, row, column, plot, generator, bar, 696 (value < 0.0)); 697 } 698 699 // add an item entity, if this information is being collected 700 EntityCollection entities = state.getEntityCollection(); 701 if (entities != null) { 702 GeneralPath barOutline = new GeneralPath(); 703 barOutline.moveTo((float) x0, (float) y3); 704 barOutline.lineTo((float) x0, (float) y1); 705 barOutline.lineTo((float) x1, (float) y0); 706 barOutline.lineTo((float) x3, (float) y0); 707 barOutline.lineTo((float) x3, (float) y2); 708 barOutline.lineTo((float) x2, (float) y3); 709 barOutline.closePath(); 710 addItemEntity(entities, dataset, row, column, barOutline); 711 } 712 713 } 714 715 /** 716 * Tests this renderer for equality with an arbitrary object. 717 * 718 * @param obj the object (<code>null</code> permitted). 719 * 720 * @return A boolean. 721 */ 722 public boolean equals(Object obj) { 723 if (obj == this) { 724 return true; 725 } 726 if (!(obj instanceof BarRenderer3D)) { 727 return false; 728 } 729 BarRenderer3D that = (BarRenderer3D) obj; 730 if (this.xOffset != that.xOffset) { 731 return false; 732 } 733 if (this.yOffset != that.yOffset) { 734 return false; 735 } 736 if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) { 737 return false; 738 } 739 return super.equals(obj); 740 } 741 742 /** 743 * Provides serialization support. 744 * 745 * @param stream the output stream. 746 * 747 * @throws IOException if there is an I/O error. 748 */ 749 private void writeObject(ObjectOutputStream stream) throws IOException { 750 stream.defaultWriteObject(); 751 SerialUtilities.writePaint(this.wallPaint, stream); 752 } 753 754 /** 755 * Provides serialization support. 756 * 757 * @param stream the input stream. 758 * 759 * @throws IOException if there is an I/O error. 760 * @throws ClassNotFoundException if there is a classpath problem. 761 */ 762 private void readObject(ObjectInputStream stream) 763 throws IOException, ClassNotFoundException { 764 stream.defaultReadObject(); 765 this.wallPaint = SerialUtilities.readPaint(stream); 766 } 767 768 }