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