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