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