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