Source for org.jfree.chart.renderer.category.BarRenderer3D

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it
  10:  * under the terms of the GNU Lesser General Public License as published by
  11:  * the Free Software Foundation; either version 2.1 of the License, or
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
  22:  * USA.
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  25:  * in the United States and other countries.]
  26:  *
  27:  * ------------------
  28:  * BarRenderer3D.java
  29:  * ------------------
  30:  * (C) Copyright 2001-2007, by Serge V. Grachov and Contributors.
  31:  *
  32:  * Original Author:  Serge V. Grachov;
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *                   Tin Luu;
  35:  *                   Milo Simpson;
  36:  *                   Richard Atkinson;
  37:  *                   Rich Unger;
  38:  *                   Christian W. Zuckschwerdt;
  39:  *
  40:  * Changes
  41:  * -------
  42:  * 31-Oct-2001 : First version, contributed by Serge V. Grachov (DG);
  43:  * 15-Nov-2001 : Modified to allow for null data values (DG);
  44:  * 13-Dec-2001 : Added tooltips (DG);
  45:  * 16-Jan-2002 : Added fix for single category or single series datasets,
  46:  *               pointed out by Taoufik Romdhane (DG);
  47:  * 24-May-2002 : Incorporated tooltips into chart entities (DG);
  48:  * 11-Jun-2002 : Added check for (permitted) null info object, bug and fix
  49:  *               reported by David Basten.  Also updated Javadocs. (DG);
  50:  * 19-Jun-2002 : Added code to draw labels on bars (TL);
  51:  * 26-Jun-2002 : Added bar clipping to avoid PRExceptions (DG);
  52:  * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs
  53:  *               for HTML image maps (RA);
  54:  * 06-Aug-2002 : Value labels now use number formatter, thanks to Milo
  55:  *               Simpson (DG);
  56:  * 08-Aug-2002 : Applied fixed in bug id 592218 (DG);
  57:  * 20-Sep-2002 : Added fix for categoryPaint by Rich Unger, and fixed errors
  58:  *               reported by Checkstyle (DG);
  59:  * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
  60:  *               CategoryToolTipGenerator interface (DG);
  61:  * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
  62:  * 06-Nov-2002 : Moved to the com.jrefinery.chart.renderer package (DG);
  63:  * 28-Jan-2003 : Added an attribute to control the shading of the left and
  64:  *               bottom walls in the plot background (DG);
  65:  * 25-Mar-2003 : Implemented Serializable (DG);
  66:  * 10-Apr-2003 : Removed category paint usage (DG);
  67:  * 13-May-2003 : Renamed VerticalBarRenderer3D --> BarRenderer3D and merged with
  68:  *               HorizontalBarRenderer3D (DG);
  69:  * 30-Jul-2003 : Modified entity constructor (CZ);
  70:  * 19-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
  71:  * 07-Oct-2003 : Added renderer state (DG);
  72:  * 08-Oct-2003 : Removed clipping (replaced with flag in CategoryPlot to
  73:  *               control order in which the data items are processed) (DG);
  74:  * 20-Oct-2003 : Fixed bug (outline stroke not being used for bar
  75:  *               outlines) (DG);
  76:  * 21-Oct-2003 : Bar width moved into CategoryItemRendererState (DG);
  77:  * 24-Nov-2003 : Fixed bug 846324 (item labels not showing) (DG);
  78:  * 27-Nov-2003 : Added code to respect maxBarWidth setting (DG);
  79:  * 02-Feb-2004 : Fixed bug where 'drawBarOutline' flag is not respected (DG);
  80:  * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste
  81:  *               overriding easier (DG);
  82:  * 04-Oct-2004 : Fixed bug with item label positioning when plot alignment is
  83:  *               horizontal (DG);
  84:  * 05-Nov-2004 : Modified drawItem() signature (DG);
  85:  * 20-Apr-2005 : Renamed CategoryLabelGenerator
  86:  *               --> CategoryItemLabelGenerator (DG);
  87:  * 25-Apr-2005 : Override initialise() method to fix bug 1189642 (DG);
  88:  * 09-Jun-2005 : Use addEntityItem from super class (DG);
  89:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  90:  * 07-Dec-2006 : Implemented equals() override (DG);
  91:  * 17-Jan-2007 : Fixed bug in drawDomainGridline() method (DG);
  92:  * 03-Apr-2007 : Fixed bugs in drawBackground() method (DG);
  93:  * 16-Oct-2007 : Fixed bug in range marker drawing (DG);
  94:  *
  95:  */
  96: 
  97: package org.jfree.chart.renderer.category;
  98: 
  99: import java.awt.AlphaComposite;
 100: import java.awt.Color;
 101: import java.awt.Composite;
 102: import java.awt.Font;
 103: import java.awt.Graphics2D;
 104: import java.awt.Image;
 105: import java.awt.Paint;
 106: import java.awt.Stroke;
 107: import java.awt.geom.GeneralPath;
 108: import java.awt.geom.Line2D;
 109: import java.awt.geom.Point2D;
 110: import java.awt.geom.Rectangle2D;
 111: import java.io.IOException;
 112: import java.io.ObjectInputStream;
 113: import java.io.ObjectOutputStream;
 114: import java.io.Serializable;
 115: 
 116: import org.jfree.chart.Effect3D;
 117: import org.jfree.chart.axis.CategoryAxis;
 118: import org.jfree.chart.axis.ValueAxis;
 119: import org.jfree.chart.entity.EntityCollection;
 120: import org.jfree.chart.event.RendererChangeEvent;
 121: import org.jfree.chart.labels.CategoryItemLabelGenerator;
 122: import org.jfree.chart.labels.ItemLabelAnchor;
 123: import org.jfree.chart.labels.ItemLabelPosition;
 124: import org.jfree.chart.plot.CategoryPlot;
 125: import org.jfree.chart.plot.Marker;
 126: import org.jfree.chart.plot.Plot;
 127: import org.jfree.chart.plot.PlotOrientation;
 128: import org.jfree.chart.plot.PlotRenderingInfo;
 129: import org.jfree.chart.plot.ValueMarker;
 130: import org.jfree.data.Range;
 131: import org.jfree.data.category.CategoryDataset;
 132: import org.jfree.io.SerialUtilities;
 133: import org.jfree.text.TextUtilities;
 134: import org.jfree.ui.LengthAdjustmentType;
 135: import org.jfree.ui.RectangleAnchor;
 136: import org.jfree.ui.RectangleEdge;
 137: import org.jfree.ui.TextAnchor;
 138: import org.jfree.util.PaintUtilities;
 139: import org.jfree.util.PublicCloneable;
 140: 
 141: /**
 142:  * A renderer for bars with a 3D effect, for use with the
 143:  * {@link org.jfree.chart.plot.CategoryPlot} class.
 144:  */
 145: public class BarRenderer3D extends BarRenderer
 146:         implements Effect3D, Cloneable, PublicCloneable, Serializable {
 147: 
 148:     /** For serialization. */
 149:     private static final long serialVersionUID = 7686976503536003636L;
 150: 
 151:     /** The default x-offset for the 3D effect. */
 152:     public static final double DEFAULT_X_OFFSET = 12.0;
 153: 
 154:     /** The default y-offset for the 3D effect. */
 155:     public static final double DEFAULT_Y_OFFSET = 8.0;
 156: 
 157:     /** The default wall paint. */
 158:     public static final Paint DEFAULT_WALL_PAINT = new Color(0xDD, 0xDD, 0xDD);
 159: 
 160:     /** The size of x-offset for the 3D effect. */
 161:     private double xOffset;
 162: 
 163:     /** The size of y-offset for the 3D effect. */
 164:     private double yOffset;
 165: 
 166:     /** The paint used to shade the left and lower 3D wall. */
 167:     private transient Paint wallPaint;
 168: 
 169:     /**
 170:      * Default constructor, creates a renderer with a default '3D effect'.
 171:      */
 172:     public BarRenderer3D() {
 173:         this(DEFAULT_X_OFFSET, DEFAULT_Y_OFFSET);
 174:     }
 175: 
 176:     /**
 177:      * Constructs a new renderer with the specified '3D effect'.
 178:      *
 179:      * @param xOffset  the x-offset for the 3D effect.
 180:      * @param yOffset  the y-offset for the 3D effect.
 181:      */
 182:     public BarRenderer3D(double xOffset, double yOffset) {
 183: 
 184:         super();
 185:         this.xOffset = xOffset;
 186:         this.yOffset = yOffset;
 187:         this.wallPaint = DEFAULT_WALL_PAINT;
 188:         // set the default item label positions
 189:         ItemLabelPosition p1 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12,
 190:                 TextAnchor.TOP_CENTER);
 191:         setBasePositiveItemLabelPosition(p1);
 192:         ItemLabelPosition p2 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12,
 193:                 TextAnchor.TOP_CENTER);
 194:         setBaseNegativeItemLabelPosition(p2);
 195: 
 196:     }
 197: 
 198:     /**
 199:      * Returns the x-offset for the 3D effect.
 200:      *
 201:      * @return The 3D effect.
 202:      *
 203:      * @see #getYOffset()
 204:      */
 205:     public double getXOffset() {
 206:         return this.xOffset;
 207:     }
 208: 
 209:     /**
 210:      * Returns the y-offset for the 3D effect.
 211:      *
 212:      * @return The 3D effect.
 213:      */
 214:     public double getYOffset() {
 215:         return this.yOffset;
 216:     }
 217: 
 218:     /**
 219:      * Returns the paint used to highlight the left and bottom wall in the plot
 220:      * background.
 221:      *
 222:      * @return The paint.
 223:      *
 224:      * @see #setWallPaint(Paint)
 225:      */
 226:     public Paint getWallPaint() {
 227:         return this.wallPaint;
 228:     }
 229: 
 230:     /**
 231:      * Sets the paint used to hightlight the left and bottom walls in the plot
 232:      * background, and sends a {@link RendererChangeEvent} to all registered
 233:      * listeners.
 234:      *
 235:      * @param paint  the paint (<code>null</code> not permitted).
 236:      *
 237:      * @see #getWallPaint()
 238:      */
 239:     public void setWallPaint(Paint paint) {
 240:         if (paint == null) {
 241:             throw new IllegalArgumentException("Null 'paint' argument.");
 242:         }
 243:         this.wallPaint = paint;
 244:         fireChangeEvent();
 245:     }
 246: 
 247: 
 248:     /**
 249:      * Initialises the renderer and returns a state object that will be passed
 250:      * to subsequent calls to the drawItem method.  This method gets called
 251:      * once at the start of the process of drawing a chart.
 252:      *
 253:      * @param g2  the graphics device.
 254:      * @param dataArea  the area in which the data is to be plotted.
 255:      * @param plot  the plot.
 256:      * @param rendererIndex  the renderer index.
 257:      * @param info  collects chart rendering information for return to caller.
 258:      *
 259:      * @return The renderer state.
 260:      */
 261:     public CategoryItemRendererState initialise(Graphics2D g2,
 262:                                                 Rectangle2D dataArea,
 263:                                                 CategoryPlot plot,
 264:                                                 int rendererIndex,
 265:                                                 PlotRenderingInfo info) {
 266: 
 267:         Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
 268:                 dataArea.getY() + getYOffset(), dataArea.getWidth()
 269:                 - getXOffset(), dataArea.getHeight() - getYOffset());
 270:         CategoryItemRendererState state = super.initialise(g2, adjusted, plot,
 271:                 rendererIndex, info);
 272:         return state;
 273: 
 274:     }
 275: 
 276:     /**
 277:      * Draws the background for the plot.
 278:      *
 279:      * @param g2  the graphics device.
 280:      * @param plot  the plot.
 281:      * @param dataArea  the area inside the axes.
 282:      */
 283:     public void drawBackground(Graphics2D g2, CategoryPlot plot,
 284:                                Rectangle2D dataArea) {
 285: 
 286:         float x0 = (float) dataArea.getX();
 287:         float x1 = x0 + (float) Math.abs(this.xOffset);
 288:         float x3 = (float) dataArea.getMaxX();
 289:         float x2 = x3 - (float) Math.abs(this.xOffset);
 290: 
 291:         float y0 = (float) dataArea.getMaxY();
 292:         float y1 = y0 - (float) Math.abs(this.yOffset);
 293:         float y3 = (float) dataArea.getMinY();
 294:         float y2 = y3 + (float) Math.abs(this.yOffset);
 295: 
 296:         GeneralPath clip = new GeneralPath();
 297:         clip.moveTo(x0, y0);
 298:         clip.lineTo(x0, y2);
 299:         clip.lineTo(x1, y3);
 300:         clip.lineTo(x3, y3);
 301:         clip.lineTo(x3, y1);
 302:         clip.lineTo(x2, y0);
 303:         clip.closePath();
 304: 
 305:         Composite originalComposite = g2.getComposite();
 306:         g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
 307:                 plot.getBackgroundAlpha()));
 308: 
 309:         // fill background...
 310:         Paint backgroundPaint = plot.getBackgroundPaint();
 311:         if (backgroundPaint != null) {
 312:             g2.setPaint(backgroundPaint);
 313:             g2.fill(clip);
 314:         }
 315: 
 316:         GeneralPath leftWall = new GeneralPath();
 317:         leftWall.moveTo(x0, y0);
 318:         leftWall.lineTo(x0, y2);
 319:         leftWall.lineTo(x1, y3);
 320:         leftWall.lineTo(x1, y1);
 321:         leftWall.closePath();
 322:         g2.setPaint(getWallPaint());
 323:         g2.fill(leftWall);
 324: 
 325:         GeneralPath bottomWall = new GeneralPath();
 326:         bottomWall.moveTo(x0, y0);
 327:         bottomWall.lineTo(x1, y1);
 328:         bottomWall.lineTo(x3, y1);
 329:         bottomWall.lineTo(x2, y0);
 330:         bottomWall.closePath();
 331:         g2.setPaint(getWallPaint());
 332:         g2.fill(bottomWall);
 333: 
 334:         // highlight the background corners...
 335:         g2.setPaint(Color.lightGray);
 336:         Line2D corner = new Line2D.Double(x0, y0, x1, y1);
 337:         g2.draw(corner);
 338:         corner.setLine(x1, y1, x1, y3);
 339:         g2.draw(corner);
 340:         corner.setLine(x1, y1, x3, y1);
 341:         g2.draw(corner);
 342: 
 343:         // draw background image, if there is one...
 344:         Image backgroundImage = plot.getBackgroundImage();
 345:         if (backgroundImage != null) {
 346:             Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX()
 347:                     + getXOffset(), dataArea.getY(),
 348:                     dataArea.getWidth() - getXOffset(),
 349:                     dataArea.getHeight() - getYOffset());
 350:             plot.drawBackgroundImage(g2, adjusted);
 351:         }
 352: 
 353:         g2.setComposite(originalComposite);
 354: 
 355:     }
 356: 
 357:     /**
 358:      * Draws the outline for the plot.
 359:      *
 360:      * @param g2  the graphics device.
 361:      * @param plot  the plot.
 362:      * @param dataArea  the area inside the axes.
 363:      */
 364:     public void drawOutline(Graphics2D g2, CategoryPlot plot,
 365:                             Rectangle2D dataArea) {
 366: 
 367:         float x0 = (float) dataArea.getX();
 368:         float x1 = x0 + (float) Math.abs(this.xOffset);
 369:         float x3 = (float) dataArea.getMaxX();
 370:         float x2 = x3 - (float) Math.abs(this.xOffset);
 371: 
 372:         float y0 = (float) dataArea.getMaxY();
 373:         float y1 = y0 - (float) Math.abs(this.yOffset);
 374:         float y3 = (float) dataArea.getMinY();
 375:         float y2 = y3 + (float) Math.abs(this.yOffset);
 376: 
 377:         GeneralPath clip = new GeneralPath();
 378:         clip.moveTo(x0, y0);
 379:         clip.lineTo(x0, y2);
 380:         clip.lineTo(x1, y3);
 381:         clip.lineTo(x3, y3);
 382:         clip.lineTo(x3, y1);
 383:         clip.lineTo(x2, y0);
 384:         clip.closePath();
 385: 
 386:         // put an outline around the data area...
 387:         Stroke outlineStroke = plot.getOutlineStroke();
 388:         Paint outlinePaint = plot.getOutlinePaint();
 389:         if ((outlineStroke != null) && (outlinePaint != null)) {
 390:             g2.setStroke(outlineStroke);
 391:             g2.setPaint(outlinePaint);
 392:             g2.draw(clip);
 393:         }
 394: 
 395:     }
 396: 
 397:     /**
 398:      * Draws a grid line against the domain axis.
 399:      *
 400:      * @param g2  the graphics device.
 401:      * @param plot  the plot.
 402:      * @param dataArea  the area for plotting data (not yet adjusted for any
 403:      *                  3D effect).
 404:      * @param value  the Java2D value at which the grid line should be drawn.
 405:      *
 406:      */
 407:     public void drawDomainGridline(Graphics2D g2,
 408:                                    CategoryPlot plot,
 409:                                    Rectangle2D dataArea,
 410:                                    double value) {
 411: 
 412:         Line2D line1 = null;
 413:         Line2D line2 = null;
 414:         PlotOrientation orientation = plot.getOrientation();
 415:         if (orientation == PlotOrientation.HORIZONTAL) {
 416:             double y0 = value;
 417:             double y1 = value - getYOffset();
 418:             double x0 = dataArea.getMinX();
 419:             double x1 = x0 + getXOffset();
 420:             double x2 = dataArea.getMaxX();
 421:             line1 = new Line2D.Double(x0, y0, x1, y1);
 422:             line2 = new Line2D.Double(x1, y1, x2, y1);
 423:         }
 424:         else if (orientation == PlotOrientation.VERTICAL) {
 425:             double x0 = value;
 426:             double x1 = value + getXOffset();
 427:             double y0 = dataArea.getMaxY();
 428:             double y1 = y0 - getYOffset();
 429:             double y2 = dataArea.getMinY();
 430:             line1 = new Line2D.Double(x0, y0, x1, y1);
 431:             line2 = new Line2D.Double(x1, y1, x1, y2);
 432:         }
 433:         Paint paint = plot.getDomainGridlinePaint();
 434:         Stroke stroke = plot.getDomainGridlineStroke();
 435:         g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
 436:         g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
 437:         g2.draw(line1);
 438:         g2.draw(line2);
 439: 
 440:     }
 441: 
 442:     /**
 443:      * Draws a grid line against the range axis.
 444:      *
 445:      * @param g2  the graphics device.
 446:      * @param plot  the plot.
 447:      * @param axis  the value axis.
 448:      * @param dataArea  the area for plotting data (not yet adjusted for any
 449:      *                  3D effect).
 450:      * @param value  the value at which the grid line should be drawn.
 451:      *
 452:      */
 453:     public void drawRangeGridline(Graphics2D g2,
 454:                                   CategoryPlot plot,
 455:                                   ValueAxis axis,
 456:                                   Rectangle2D dataArea,
 457:                                   double value) {
 458: 
 459:         Range range = axis.getRange();
 460: 
 461:         if (!range.contains(value)) {
 462:             return;
 463:         }
 464: 
 465:         Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
 466:                 dataArea.getY() + getYOffset(), dataArea.getWidth()
 467:                 - getXOffset(), dataArea.getHeight() - getYOffset());
 468: 
 469:         Line2D line1 = null;
 470:         Line2D line2 = null;
 471:         PlotOrientation orientation = plot.getOrientation();
 472:         if (orientation == PlotOrientation.HORIZONTAL) {
 473:             double x0 = axis.valueToJava2D(value, adjusted,
 474:                     plot.getRangeAxisEdge());
 475:             double x1 = x0 + getXOffset();
 476:             double y0 = dataArea.getMaxY();
 477:             double y1 = y0 - getYOffset();
 478:             double y2 = dataArea.getMinY();
 479:             line1 = new Line2D.Double(x0, y0, x1, y1);
 480:             line2 = new Line2D.Double(x1, y1, x1, y2);
 481:         }
 482:         else if (orientation == PlotOrientation.VERTICAL) {
 483:             double y0 = axis.valueToJava2D(value, adjusted,
 484:                     plot.getRangeAxisEdge());
 485:             double y1 = y0 - getYOffset();
 486:             double x0 = dataArea.getMinX();
 487:             double x1 = x0 + getXOffset();
 488:             double x2 = dataArea.getMaxX();
 489:             line1 = new Line2D.Double(x0, y0, x1, y1);
 490:             line2 = new Line2D.Double(x1, y1, x2, y1);
 491:         }
 492:         Paint paint = plot.getRangeGridlinePaint();
 493:         Stroke stroke = plot.getRangeGridlineStroke();
 494:         g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
 495:         g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
 496:         g2.draw(line1);
 497:         g2.draw(line2);
 498: 
 499:     }
 500: 
 501:     /**
 502:      * Draws a range marker.
 503:      *
 504:      * @param g2  the graphics device.
 505:      * @param plot  the plot.
 506:      * @param axis  the value axis.
 507:      * @param marker  the marker.
 508:      * @param dataArea  the area for plotting data (not including 3D effect).
 509:      */
 510:     public void drawRangeMarker(Graphics2D g2,
 511:                                 CategoryPlot plot,
 512:                                 ValueAxis axis,
 513:                                 Marker marker,
 514:                                 Rectangle2D dataArea) {
 515: 
 516: 
 517:         Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
 518:                 dataArea.getY() + getYOffset(), dataArea.getWidth()
 519:                 - getXOffset(), dataArea.getHeight() - getYOffset());
 520:         if (marker instanceof ValueMarker) {
 521:             ValueMarker vm = (ValueMarker) marker;
 522:             double value = vm.getValue();
 523:             Range range = axis.getRange();
 524:             if (!range.contains(value)) {
 525:                 return;
 526:             }
 527: 
 528:             GeneralPath path = null;
 529:             PlotOrientation orientation = plot.getOrientation();
 530:             if (orientation == PlotOrientation.HORIZONTAL) {
 531:                 float x = (float) axis.valueToJava2D(value, adjusted,
 532:                         plot.getRangeAxisEdge());
 533:                 float y = (float) adjusted.getMaxY();
 534:                 path = new GeneralPath();
 535:                 path.moveTo(x, y);
 536:                 path.lineTo((float) (x + getXOffset()),
 537:                         y - (float) getYOffset());
 538:                 path.lineTo((float) (x + getXOffset()),
 539:                         (float) (adjusted.getMinY() - getYOffset()));
 540:                 path.lineTo(x, (float) adjusted.getMinY());
 541:                 path.closePath();
 542:             }
 543:             else if (orientation == PlotOrientation.VERTICAL) {
 544:                 float y = (float) axis.valueToJava2D(value, adjusted,
 545:                         plot.getRangeAxisEdge());
 546:                 float x = (float) dataArea.getX();
 547:                 path = new GeneralPath();
 548:                 path.moveTo(x, y);
 549:                 path.lineTo(x + (float) this.xOffset, y - (float) this.yOffset);
 550:                 path.lineTo((float) (adjusted.getMaxX() + this.xOffset),
 551:                         y - (float) this.yOffset);
 552:                 path.lineTo((float) (adjusted.getMaxX()), y);
 553:                 path.closePath();
 554:             }
 555:             g2.setPaint(marker.getPaint());
 556:             g2.fill(path);
 557:             g2.setPaint(marker.getOutlinePaint());
 558:             g2.draw(path);
 559: 
 560:             String label = marker.getLabel();
 561:             RectangleAnchor anchor = marker.getLabelAnchor();
 562:             if (label != null) {
 563:                 Font labelFont = marker.getLabelFont();
 564:                 g2.setFont(labelFont);
 565:                 g2.setPaint(marker.getLabelPaint());
 566:                 Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
 567:                         g2, orientation, dataArea, path.getBounds2D(),
 568:                         marker.getLabelOffset(), LengthAdjustmentType.EXPAND,
 569:                         anchor);
 570:                 TextUtilities.drawAlignedString(label, g2,
 571:                         (float) coordinates.getX(), (float) coordinates.getY(),
 572:                         marker.getLabelTextAnchor());
 573:             }
 574: 
 575:         }
 576:         else {
 577:             super.drawRangeMarker(g2, plot, axis, marker, adjusted);
 578:             // TODO: draw the interval marker with a 3D effect
 579:         }
 580:     }
 581: 
 582:     /**
 583:      * Draws a 3D bar to represent one data item.
 584:      *
 585:      * @param g2  the graphics device.
 586:      * @param state  the renderer state.
 587:      * @param dataArea  the area for plotting the data.
 588:      * @param plot  the plot.
 589:      * @param domainAxis  the domain axis.
 590:      * @param rangeAxis  the range axis.
 591:      * @param dataset  the dataset.
 592:      * @param row  the row index (zero-based).
 593:      * @param column  the column index (zero-based).
 594:      * @param pass  the pass index.
 595:      */
 596:     public void drawItem(Graphics2D g2,
 597:                          CategoryItemRendererState state,
 598:                          Rectangle2D dataArea,
 599:                          CategoryPlot plot,
 600:                          CategoryAxis domainAxis,
 601:                          ValueAxis rangeAxis,
 602:                          CategoryDataset dataset,
 603:                          int row,
 604:                          int column,
 605:                          int pass) {
 606: 
 607:         // check the value we are plotting...
 608:         Number dataValue = dataset.getValue(row, column);
 609:         if (dataValue == null) {
 610:             return;
 611:         }
 612: 
 613:         double value = dataValue.doubleValue();
 614: 
 615:         Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
 616:                 dataArea.getY() + getYOffset(),
 617:                 dataArea.getWidth() - getXOffset(),
 618:                 dataArea.getHeight() - getYOffset());
 619: 
 620:         PlotOrientation orientation = plot.getOrientation();
 621: 
 622:         double barW0 = calculateBarW0(plot, orientation, adjusted, domainAxis,
 623:                 state, row, column);
 624:         double[] barL0L1 = calculateBarL0L1(value);
 625:         if (barL0L1 == null) {
 626:             return;  // the bar is not visible
 627:         }
 628: 
 629:         RectangleEdge edge = plot.getRangeAxisEdge();
 630:         double transL0 = rangeAxis.valueToJava2D(barL0L1[0], adjusted, edge);
 631:         double transL1 = rangeAxis.valueToJava2D(barL0L1[1], adjusted, edge);
 632:         double barL0 = Math.min(transL0, transL1);
 633:         double barLength = Math.abs(transL1 - transL0);
 634: 
 635:         // draw the bar...
 636:         Rectangle2D bar = null;
 637:         if (orientation == PlotOrientation.HORIZONTAL) {
 638:             bar = new Rectangle2D.Double(barL0, barW0, barLength,
 639:                     state.getBarWidth());
 640:         }
 641:         else {
 642:             bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(),
 643:                     barLength);
 644:         }
 645:         Paint itemPaint = getItemPaint(row, column);
 646:         g2.setPaint(itemPaint);
 647:         g2.fill(bar);
 648: 
 649:         double x0 = bar.getMinX();
 650:         double x1 = x0 + getXOffset();
 651:         double x2 = bar.getMaxX();
 652:         double x3 = x2 + getXOffset();
 653: 
 654:         double y0 = bar.getMinY() - getYOffset();
 655:         double y1 = bar.getMinY();
 656:         double y2 = bar.getMaxY() - getYOffset();
 657:         double y3 = bar.getMaxY();
 658: 
 659:         GeneralPath bar3dRight = null;
 660:         GeneralPath bar3dTop = null;
 661:         if (barLength > 0.0) {
 662:             bar3dRight = new GeneralPath();
 663:             bar3dRight.moveTo((float) x2, (float) y3);
 664:             bar3dRight.lineTo((float) x2, (float) y1);
 665:             bar3dRight.lineTo((float) x3, (float) y0);
 666:             bar3dRight.lineTo((float) x3, (float) y2);
 667:             bar3dRight.closePath();
 668: 
 669:             if (itemPaint instanceof Color) {
 670:                 g2.setPaint(((Color) itemPaint).darker());
 671:             }
 672:             g2.fill(bar3dRight);
 673:         }
 674: 
 675:         bar3dTop = new GeneralPath();
 676:         bar3dTop.moveTo((float) x0, (float) y1);
 677:         bar3dTop.lineTo((float) x1, (float) y0);
 678:         bar3dTop.lineTo((float) x3, (float) y0);
 679:         bar3dTop.lineTo((float) x2, (float) y1);
 680:         bar3dTop.closePath();
 681:         g2.fill(bar3dTop);
 682: 
 683:         if (isDrawBarOutline()
 684:                 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
 685:             g2.setStroke(getItemOutlineStroke(row, column));
 686:             g2.setPaint(getItemOutlinePaint(row, column));
 687:             g2.draw(bar);
 688:             if (bar3dRight != null) {
 689:                 g2.draw(bar3dRight);
 690:             }
 691:             if (bar3dTop != null) {
 692:                 g2.draw(bar3dTop);
 693:             }
 694:         }
 695: 
 696:         CategoryItemLabelGenerator generator
 697:             = getItemLabelGenerator(row, column);
 698:         if (generator != null && isItemLabelVisible(row, column)) {
 699:             drawItemLabel(g2, dataset, row, column, plot, generator, bar,
 700:                     (value < 0.0));
 701:         }
 702: 
 703:         // add an item entity, if this information is being collected
 704:         EntityCollection entities = state.getEntityCollection();
 705:         if (entities != null) {
 706:             GeneralPath barOutline = new GeneralPath();
 707:             barOutline.moveTo((float) x0, (float) y3);
 708:             barOutline.lineTo((float) x0, (float) y1);
 709:             barOutline.lineTo((float) x1, (float) y0);
 710:             barOutline.lineTo((float) x3, (float) y0);
 711:             barOutline.lineTo((float) x3, (float) y2);
 712:             barOutline.lineTo((float) x2, (float) y3);
 713:             barOutline.closePath();
 714:             addItemEntity(entities, dataset, row, column, barOutline);
 715:         }
 716: 
 717:     }
 718: 
 719:     /**
 720:      * Tests this renderer for equality with an arbitrary object.
 721:      *
 722:      * @param obj  the object (<code>null</code> permitted).
 723:      *
 724:      * @return A boolean.
 725:      */
 726:     public boolean equals(Object obj) {
 727:         if (obj == this) {
 728:             return true;
 729:         }
 730:         if (!(obj instanceof BarRenderer3D)) {
 731:             return false;
 732:         }
 733:         BarRenderer3D that = (BarRenderer3D) obj;
 734:         if (this.xOffset != that.xOffset) {
 735:             return false;
 736:         }
 737:         if (this.yOffset != that.yOffset) {
 738:             return false;
 739:         }
 740:         if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) {
 741:             return false;
 742:         }
 743:         return super.equals(obj);
 744:     }
 745: 
 746:     /**
 747:      * Provides serialization support.
 748:      *
 749:      * @param stream  the output stream.
 750:      *
 751:      * @throws IOException  if there is an I/O error.
 752:      */
 753:     private void writeObject(ObjectOutputStream stream) throws IOException {
 754:         stream.defaultWriteObject();
 755:         SerialUtilities.writePaint(this.wallPaint, stream);
 756:     }
 757: 
 758:     /**
 759:      * Provides serialization support.
 760:      *
 761:      * @param stream  the input stream.
 762:      *
 763:      * @throws IOException  if there is an I/O error.
 764:      * @throws ClassNotFoundException  if there is a classpath problem.
 765:      */
 766:     private void readObject(ObjectInputStream stream)
 767:         throws IOException, ClassNotFoundException {
 768:         stream.defaultReadObject();
 769:         this.wallPaint = SerialUtilities.readPaint(stream);
 770:     }
 771: 
 772: }