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