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

   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:  * LineRenderer3D.java
  29:  * -------------------
  30:  * (C) Copyright 2004-2007, by Tobias Selb and Contributors.
  31:  *
  32:  * Original Author:  Tobias Selb (http://www.uepselon.com);
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *
  35:  * $Id: LineRenderer3D.java,v 1.10.2.7 2007/01/17 14:16:11 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 15-Oct-2004 : Version 1 (TS);
  40:  * 05-Nov-2004 : Modified drawItem() signature (DG);
  41:  * 11-Nov-2004 : Now uses ShapeUtilities class to translate shapes (DG);
  42:  * 26-Jan-2005 : Update for changes in super class (DG);
  43:  * 13-Apr-2005 : Check item visibility in drawItem() method (DG);
  44:  * 09-Jun-2005 : Use addItemEntity() in drawItem() method (DG);
  45:  * 10-Jun-2005 : Fixed capitalisation of setXOffset() and setYOffset() (DG);
  46:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  47:  * 01-Dec-2006 : Fixed equals() and serialization (DG);
  48:  * 17-Jan-2007 : Fixed bug in drawDomainGridline() method and added
  49:  *               argument check to setWallPaint() (DG);
  50:  * 
  51:  */
  52: 
  53: package org.jfree.chart.renderer.category;
  54: 
  55: import java.awt.AlphaComposite;
  56: import java.awt.Color;
  57: import java.awt.Composite;
  58: import java.awt.Graphics2D;
  59: import java.awt.Image;
  60: import java.awt.Paint;
  61: import java.awt.Shape;
  62: import java.awt.Stroke;
  63: import java.awt.geom.GeneralPath;
  64: import java.awt.geom.Line2D;
  65: import java.awt.geom.Rectangle2D;
  66: import java.io.IOException;
  67: import java.io.ObjectInputStream;
  68: import java.io.ObjectOutputStream;
  69: import java.io.Serializable;
  70: 
  71: import org.jfree.chart.Effect3D;
  72: import org.jfree.chart.axis.CategoryAxis;
  73: import org.jfree.chart.axis.ValueAxis;
  74: import org.jfree.chart.entity.EntityCollection;
  75: import org.jfree.chart.event.RendererChangeEvent;
  76: import org.jfree.chart.plot.CategoryPlot;
  77: import org.jfree.chart.plot.Marker;
  78: import org.jfree.chart.plot.PlotOrientation;
  79: import org.jfree.chart.plot.ValueMarker;
  80: import org.jfree.data.Range;
  81: import org.jfree.data.category.CategoryDataset;
  82: import org.jfree.io.SerialUtilities;
  83: import org.jfree.util.PaintUtilities;
  84: import org.jfree.util.ShapeUtilities;
  85: 
  86: /**
  87:  * A line renderer with a 3D effect.
  88:  */
  89: public class LineRenderer3D extends LineAndShapeRenderer 
  90:                             implements Effect3D, Serializable {
  91:    
  92:     /** For serialization. */
  93:     private static final long serialVersionUID = 5467931468380928736L;
  94:     
  95:     /** The default x-offset for the 3D effect. */
  96:     public static final double DEFAULT_X_OFFSET = 12.0;
  97: 
  98:     /** The default y-offset for the 3D effect. */
  99:     public static final double DEFAULT_Y_OFFSET = 8.0;
 100:    
 101:     /** The default wall paint. */
 102:     public static final Paint DEFAULT_WALL_PAINT = new Color(0xDD, 0xDD, 0xDD);
 103:    
 104:     /** The size of x-offset for the 3D effect. */
 105:     private double xOffset;
 106: 
 107:     /** The size of y-offset for the 3D effect. */
 108:     private double yOffset;
 109:    
 110:     /** The paint used to shade the left and lower 3D wall. */
 111:     private transient Paint wallPaint;
 112:    
 113:     /**
 114:      * Creates a new renderer.
 115:      */
 116:     public LineRenderer3D() {
 117:         super(true, false);  //Create a line renderer only
 118:         this.xOffset = DEFAULT_X_OFFSET;
 119:         this.yOffset = DEFAULT_Y_OFFSET;
 120:         this.wallPaint = DEFAULT_WALL_PAINT;
 121:     }
 122:    
 123:     /**
 124:      * Returns the x-offset for the 3D effect.
 125:      *
 126:      * @return The x-offset.
 127:      * 
 128:      * @see #setXOffset(double)
 129:      * @see #getYOffset()
 130:      */
 131:     public double getXOffset() {
 132:         return this.xOffset;
 133:     }
 134: 
 135:     /**
 136:      * Returns the y-offset for the 3D effect.
 137:      *
 138:      * @return The y-offset.
 139:      * 
 140:      * @see #setYOffset(double)
 141:      * @see #getXOffset()
 142:      */
 143:     public double getYOffset() {
 144:         return this.yOffset;
 145:     }
 146:    
 147:     /**
 148:      * Sets the x-offset and sends a {@link RendererChangeEvent} to all 
 149:      * registered listeners.
 150:      * 
 151:      * @param xOffset  the x-offset.
 152:      * 
 153:      * @see #getXOffset()
 154:      */
 155:     public void setXOffset(double xOffset) {
 156:         this.xOffset = xOffset;
 157:         notifyListeners(new RendererChangeEvent(this));
 158:     }
 159: 
 160:     /**
 161:      * Sets the y-offset and sends a {@link RendererChangeEvent} to all 
 162:      * registered listeners.
 163:      * 
 164:      * @param yOffset  the y-offset.
 165:      * 
 166:      * @see #getYOffset()
 167:      */
 168:     public void setYOffset(double yOffset) {
 169:         this.yOffset = yOffset;
 170:         notifyListeners(new RendererChangeEvent(this));
 171:     }
 172: 
 173:     /**
 174:      * Returns the paint used to highlight the left and bottom wall in the plot
 175:      * background.
 176:      *
 177:      * @return The paint.
 178:      * 
 179:      * @see #setWallPaint(Paint)
 180:      */
 181:     public Paint getWallPaint() {
 182:         return this.wallPaint;
 183:     }
 184: 
 185:     /**
 186:      * Sets the paint used to hightlight the left and bottom walls in the plot
 187:      * background, and sends a {@link RendererChangeEvent} to all 
 188:      * registered listeners.
 189:      *
 190:      * @param paint  the paint (<code>null</code> not permitted).
 191:      * 
 192:      * @see #getWallPaint()
 193:      */
 194:     public void setWallPaint(Paint paint) {
 195:         if (paint == null) {
 196:             throw new IllegalArgumentException("Null 'paint' argument.");
 197:         }
 198:         this.wallPaint = paint;
 199:         notifyListeners(new RendererChangeEvent(this));
 200:     }
 201:    
 202:     /**
 203:      * Draws the background for the plot.
 204:      *
 205:      * @param g2  the graphics device.
 206:      * @param plot  the plot.
 207:      * @param dataArea  the area inside the axes.
 208:      */
 209:     public void drawBackground(Graphics2D g2, CategoryPlot plot, 
 210:                                Rectangle2D dataArea) {
 211: 
 212:         float x0 = (float) dataArea.getX();
 213:         float x1 = x0 + (float) Math.abs(this.xOffset);
 214:         float x3 = (float) dataArea.getMaxX();
 215:         float x2 = x3 - (float) Math.abs(this.xOffset);
 216: 
 217:         float y0 = (float) dataArea.getMaxY();
 218:         float y1 = y0 - (float) Math.abs(this.yOffset);
 219:         float y3 = (float) dataArea.getMinY();
 220:         float y2 = y3 + (float) Math.abs(this.yOffset);
 221: 
 222:         GeneralPath clip = new GeneralPath();
 223:         clip.moveTo(x0, y0);
 224:         clip.lineTo(x0, y2);
 225:         clip.lineTo(x1, y3);
 226:         clip.lineTo(x3, y3);
 227:         clip.lineTo(x3, y1);
 228:         clip.lineTo(x2, y0);
 229:         clip.closePath();
 230: 
 231:         // fill background...
 232:         Paint backgroundPaint = plot.getBackgroundPaint();
 233:         if (backgroundPaint != null) {
 234:             g2.setPaint(backgroundPaint);
 235:             g2.fill(clip);
 236:         }
 237: 
 238:         GeneralPath leftWall = new GeneralPath();
 239:         leftWall.moveTo(x0, y0);
 240:         leftWall.lineTo(x0, y2);
 241:         leftWall.lineTo(x1, y3);
 242:         leftWall.lineTo(x1, y1);
 243:         leftWall.closePath();
 244:         g2.setPaint(getWallPaint());
 245:         g2.fill(leftWall);
 246: 
 247:         GeneralPath bottomWall = new GeneralPath();
 248:         bottomWall.moveTo(x0, y0);
 249:         bottomWall.lineTo(x1, y1);
 250:         bottomWall.lineTo(x3, y1);
 251:         bottomWall.lineTo(x2, y0);
 252:         bottomWall.closePath();
 253:         g2.setPaint(getWallPaint());
 254:         g2.fill(bottomWall);
 255: 
 256:         // higlight the background corners...
 257:         g2.setPaint(Color.lightGray);
 258:         Line2D corner = new Line2D.Double(x0, y0, x1, y1);
 259:         g2.draw(corner);
 260:         corner.setLine(x1, y1, x1, y3);
 261:         g2.draw(corner);
 262:         corner.setLine(x1, y1, x3, y1);
 263:         g2.draw(corner);
 264: 
 265:         // draw background image, if there is one...
 266:         Image backgroundImage = plot.getBackgroundImage();
 267:         if (backgroundImage != null) {
 268:             Composite originalComposite = g2.getComposite();
 269:             g2.setComposite(AlphaComposite.getInstance(
 270:                     AlphaComposite.SRC, plot.getBackgroundAlpha()));
 271:             g2.drawImage(backgroundImage, (int) x1, (int) y3, 
 272:                     (int) (x3 - x1 + 1), (int) (y1 - y3 + 1), null);
 273:             g2.setComposite(originalComposite);
 274:         }
 275: 
 276:     }
 277: 
 278:     /**
 279:      * Draws the outline for the plot.
 280:      *
 281:      * @param g2  the graphics device.
 282:      * @param plot  the plot.
 283:      * @param dataArea  the area inside the axes.
 284:      */
 285:     public void drawOutline(Graphics2D g2, CategoryPlot plot, 
 286:                             Rectangle2D dataArea) {
 287: 
 288:         float x0 = (float) dataArea.getX();
 289:         float x1 = x0 + (float) Math.abs(this.xOffset);
 290:         float x3 = (float) dataArea.getMaxX();
 291:         float x2 = x3 - (float) Math.abs(this.xOffset);
 292: 
 293:         float y0 = (float) dataArea.getMaxY();
 294:         float y1 = y0 - (float) Math.abs(this.yOffset);
 295:         float y3 = (float) dataArea.getMinY();
 296:         float y2 = y3 + (float) Math.abs(this.yOffset);
 297: 
 298:         GeneralPath clip = new GeneralPath();
 299:         clip.moveTo(x0, y0);
 300:         clip.lineTo(x0, y2);
 301:         clip.lineTo(x1, y3);
 302:         clip.lineTo(x3, y3);
 303:         clip.lineTo(x3, y1);
 304:         clip.lineTo(x2, y0);
 305:         clip.closePath();
 306: 
 307:         // put an outline around the data area...
 308:         Stroke outlineStroke = plot.getOutlineStroke();
 309:         Paint outlinePaint = plot.getOutlinePaint();
 310:         if ((outlineStroke != null) && (outlinePaint != null)) {
 311:             g2.setStroke(outlineStroke);
 312:             g2.setPaint(outlinePaint);
 313:             g2.draw(clip);
 314:         }
 315: 
 316:     }
 317: 
 318:     /**
 319:      * Draws a grid line against the domain axis.
 320:      *
 321:      * @param g2  the graphics device.
 322:      * @param plot  the plot.
 323:      * @param dataArea  the area for plotting data (not yet adjusted for any 
 324:      *                  3D effect).
 325:      * @param value  the Java2D value at which the grid line should be drawn.
 326:      *
 327:      */
 328:     public void drawDomainGridline(Graphics2D g2,
 329:                                    CategoryPlot plot,
 330:                                    Rectangle2D dataArea,
 331:                                    double value) {
 332: 
 333:         Line2D line1 = null;
 334:         Line2D line2 = null;
 335:         PlotOrientation orientation = plot.getOrientation();
 336:         if (orientation == PlotOrientation.HORIZONTAL) {
 337:             double y0 = value;
 338:             double y1 = value - getYOffset();
 339:             double x0 = dataArea.getMinX();
 340:             double x1 = x0 + getXOffset();
 341:             double x2 = dataArea.getMaxX();
 342:             line1 = new Line2D.Double(x0, y0, x1, y1);
 343:             line2 = new Line2D.Double(x1, y1, x2, y1);
 344:         }
 345:         else if (orientation == PlotOrientation.VERTICAL) {
 346:             double x0 = value;
 347:             double x1 = value + getXOffset();
 348:             double y0 = dataArea.getMaxY();
 349:             double y1 = y0 - getYOffset();
 350:             double y2 = dataArea.getMinY();
 351:             line1 = new Line2D.Double(x0, y0, x1, y1);
 352:             line2 = new Line2D.Double(x1, y1, x1, y2);
 353:         }
 354:         g2.setPaint(plot.getDomainGridlinePaint());
 355:         g2.setStroke(plot.getDomainGridlineStroke());
 356:         g2.draw(line1);
 357:         g2.draw(line2);
 358: 
 359:     }
 360: 
 361:     /**
 362:      * Draws a grid line against the range axis.
 363:      *
 364:      * @param g2  the graphics device.
 365:      * @param plot  the plot.
 366:      * @param axis  the value axis.
 367:      * @param dataArea  the area for plotting data (not yet adjusted for any 
 368:      *                  3D effect).
 369:      * @param value  the value at which the grid line should be drawn.
 370:      *
 371:      */
 372:     public void drawRangeGridline(Graphics2D g2,
 373:                                   CategoryPlot plot,
 374:                                   ValueAxis axis,
 375:                                   Rectangle2D dataArea,
 376:                                   double value) {
 377: 
 378:         Range range = axis.getRange();
 379: 
 380:         if (!range.contains(value)) {
 381:             return;
 382:         }
 383: 
 384:         Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
 385:                 dataArea.getY() + getYOffset(),
 386:                 dataArea.getWidth() - getXOffset(),
 387:                 dataArea.getHeight() - getYOffset());
 388: 
 389:         Line2D line1 = null;
 390:         Line2D line2 = null;
 391:         PlotOrientation orientation = plot.getOrientation();
 392:         if (orientation == PlotOrientation.HORIZONTAL) {
 393:             double x0 = axis.valueToJava2D(value, adjusted, 
 394:                     plot.getRangeAxisEdge());
 395:             double x1 = x0 + getXOffset();
 396:             double y0 = dataArea.getMaxY();
 397:             double y1 = y0 - getYOffset();
 398:             double y2 = dataArea.getMinY();
 399:             line1 = new Line2D.Double(x0, y0, x1, y1);
 400:             line2 = new Line2D.Double(x1, y1, x1, y2);
 401:         }
 402:         else if (orientation == PlotOrientation.VERTICAL) {
 403:             double y0 = axis.valueToJava2D(value, adjusted,
 404:                     plot.getRangeAxisEdge());
 405:             double y1 = y0 - getYOffset();
 406:             double x0 = dataArea.getMinX();
 407:             double x1 = x0 + getXOffset();
 408:             double x2 = dataArea.getMaxX();
 409:             line1 = new Line2D.Double(x0, y0, x1, y1);
 410:             line2 = new Line2D.Double(x1, y1, x2, y1);
 411:         }
 412:         g2.setPaint(plot.getRangeGridlinePaint());
 413:         g2.setStroke(plot.getRangeGridlineStroke());
 414:         g2.draw(line1);
 415:         g2.draw(line2);
 416: 
 417:     }
 418: 
 419:     /**
 420:      * Draws a range marker.
 421:      *
 422:      * @param g2  the graphics device.
 423:      * @param plot  the plot.
 424:      * @param axis  the value axis.
 425:      * @param marker  the marker.
 426:      * @param dataArea  the area for plotting data (not including 3D effect).
 427:      */
 428:     public void drawRangeMarker(Graphics2D g2,
 429:                                 CategoryPlot plot,
 430:                                 ValueAxis axis,
 431:                                 Marker marker,
 432:                                 Rectangle2D dataArea) {
 433: 
 434:         if (marker instanceof ValueMarker) {
 435:             ValueMarker vm = (ValueMarker) marker;
 436:             double value = vm.getValue();
 437:             Range range = axis.getRange();
 438:             if (!range.contains(value)) {
 439:                 return;
 440:             }
 441: 
 442:             Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 
 443:                     dataArea.getY() + getYOffset(), 
 444:                     dataArea.getWidth() - getXOffset(), 
 445:                     dataArea.getHeight() - getYOffset());
 446: 
 447:             GeneralPath path = null;
 448:             PlotOrientation orientation = plot.getOrientation();
 449:             if (orientation == PlotOrientation.HORIZONTAL) {
 450:                 float x = (float) axis.valueToJava2D(value, adjusted, 
 451:                         plot.getRangeAxisEdge());
 452:                 float y = (float) adjusted.getMaxY();
 453:                 path = new GeneralPath();
 454:                 path.moveTo(x, y);
 455:                 path.lineTo((float) (x + getXOffset()), 
 456:                         y - (float) getYOffset());
 457:                 path.lineTo((float) (x + getXOffset()), 
 458:                         (float) (adjusted.getMinY() - getYOffset()));
 459:                 path.lineTo(x, (float) adjusted.getMinY());
 460:                 path.closePath();
 461:             }
 462:             else if (orientation == PlotOrientation.VERTICAL) {
 463:                 float y = (float) axis.valueToJava2D(value, adjusted, 
 464:                         plot.getRangeAxisEdge());
 465:                 float x = (float) dataArea.getX();
 466:                 path = new GeneralPath();
 467:                 path.moveTo(x, y);
 468:                 path.lineTo(x + (float) this.xOffset, y - (float) this.yOffset);
 469:                 path.lineTo((float) (adjusted.getMaxX() + this.xOffset), 
 470:                         y - (float) this.yOffset);
 471:                 path.lineTo((float) (adjusted.getMaxX()), y);
 472:                 path.closePath();
 473:             }
 474:             g2.setPaint(marker.getPaint());
 475:             g2.fill(path);
 476:             g2.setPaint(marker.getOutlinePaint());
 477:             g2.draw(path);
 478:         }
 479:     }
 480:    
 481:    /**
 482:      * Draw a single data item.
 483:      *
 484:      * @param g2  the graphics device.
 485:      * @param state  the renderer state.
 486:      * @param dataArea  the area in which the data is drawn.
 487:      * @param plot  the plot.
 488:      * @param domainAxis  the domain axis.
 489:      * @param rangeAxis  the range axis.
 490:      * @param dataset  the dataset.
 491:      * @param row  the row index (zero-based).
 492:      * @param column  the column index (zero-based).
 493:      * @param pass  the pass index.
 494:      */
 495:     public void drawItem(Graphics2D g2,
 496:                          CategoryItemRendererState state,
 497:                          Rectangle2D dataArea,
 498:                          CategoryPlot plot,
 499:                          CategoryAxis domainAxis,
 500:                          ValueAxis rangeAxis,
 501:                          CategoryDataset dataset,
 502:                          int row,
 503:                          int column,
 504:                          int pass) {
 505: 
 506:         if (!getItemVisible(row, column)) {
 507:             return;   
 508:         }
 509:         
 510:         // nothing is drawn for null...
 511:         Number v = dataset.getValue(row, column);
 512:         if (v == null) {
 513:             return;
 514:         }
 515:        
 516:         Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
 517:                 dataArea.getY() + getYOffset(), 
 518:                 dataArea.getWidth() - getXOffset(),
 519:                 dataArea.getHeight() - getYOffset());
 520:        
 521:         PlotOrientation orientation = plot.getOrientation();
 522: 
 523:         // current data point...
 524:         double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(), 
 525:                 adjusted, plot.getDomainAxisEdge());
 526:         double value = v.doubleValue();
 527:         double y1 = rangeAxis.valueToJava2D(value, adjusted, 
 528:                 plot.getRangeAxisEdge());
 529: 
 530:         Shape shape = getItemShape(row, column);
 531:         if (orientation == PlotOrientation.HORIZONTAL) {
 532:             shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
 533:         }
 534:         else if (orientation == PlotOrientation.VERTICAL) {
 535:             shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
 536:         }
 537:        
 538:         if (getItemLineVisible(row, column)) {
 539:             if (column != 0) {
 540: 
 541:                 Number previousValue = dataset.getValue(row, column - 1);
 542:                 if (previousValue != null) {
 543: 
 544:                     // previous data point...
 545:                     double previous = previousValue.doubleValue();
 546:                     double x0 = domainAxis.getCategoryMiddle(column - 1, 
 547:                             getColumnCount(), adjusted, 
 548:                             plot.getDomainAxisEdge());
 549:                     double y0 = rangeAxis.valueToJava2D(previous, adjusted, 
 550:                             plot.getRangeAxisEdge());
 551: 
 552:                     double x2 = x0 + getXOffset();
 553:                     double y2 = y0 - getYOffset();
 554:                     double x3 = x1 + getXOffset();
 555:                     double y3 = y1 - getYOffset();
 556:                    
 557:                     GeneralPath clip = new GeneralPath();
 558:                    
 559:                     if (orientation == PlotOrientation.HORIZONTAL) {
 560:                         clip.moveTo((float) y0, (float) x0);
 561:                         clip.lineTo((float) y1, (float) x1);
 562:                         clip.lineTo((float) y3, (float) x3);
 563:                         clip.lineTo((float) y2, (float) x2);
 564:                         clip.lineTo((float) y0, (float) x0);
 565:                         clip.closePath();
 566:                     }
 567:                     else if (orientation == PlotOrientation.VERTICAL) {
 568:                         clip.moveTo((float) x0, (float) y0);
 569:                         clip.lineTo((float) x1, (float) y1);
 570:                         clip.lineTo((float) x3, (float) y3);
 571:                         clip.lineTo((float) x2, (float) y2);
 572:                         clip.lineTo((float) x0, (float) y0);
 573:                         clip.closePath();
 574:                     }
 575:                    
 576:                     g2.setPaint(getItemPaint(row, column));
 577:                     g2.fill(clip);
 578:                     g2.setStroke(getItemOutlineStroke(row, column));
 579:                     g2.setPaint(getItemOutlinePaint(row, column));
 580:                     g2.draw(clip);
 581:                 }
 582:             }
 583:         }
 584: 
 585:         // draw the item label if there is one...
 586:         if (isItemLabelVisible(row, column)) {
 587:             drawItemLabel(g2, orientation, dataset, row, column, x1, y1, 
 588:                     (value < 0.0));
 589:         }
 590: 
 591:         // add an item entity, if this information is being collected
 592:         EntityCollection entities = state.getEntityCollection();
 593:         if (entities != null) {
 594:             addItemEntity(entities, dataset, row, column, shape);
 595:         }
 596: 
 597:     }
 598:     
 599:     /**
 600:      * Checks this renderer for equality with an arbitrary object.
 601:      * 
 602:      * @param obj  the object (<code>null</code> permitted).
 603:      * 
 604:      * @return A boolean.
 605:      */
 606:     public boolean equals(Object obj) {
 607:         if (obj == this) {
 608:             return true;
 609:         }
 610:         if (!(obj instanceof LineRenderer3D)) {
 611:             return false;
 612:         }
 613:         LineRenderer3D that = (LineRenderer3D) obj;
 614:         if (this.xOffset != that.xOffset) {
 615:             return false;
 616:         }
 617:         if (this.yOffset != that.yOffset) {
 618:             return false;
 619:         }
 620:         if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) {
 621:             return false;
 622:         }
 623:         return super.equals(obj);
 624:     }
 625:     
 626:     /**
 627:      * Provides serialization support.
 628:      *
 629:      * @param stream  the output stream.
 630:      *
 631:      * @throws IOException  if there is an I/O error.
 632:      */
 633:     private void writeObject(ObjectOutputStream stream) throws IOException {
 634:         stream.defaultWriteObject();
 635:         SerialUtilities.writePaint(this.wallPaint, stream);
 636:     }
 637: 
 638:     /**
 639:      * Provides serialization support.
 640:      *
 641:      * @param stream  the input stream.
 642:      *
 643:      * @throws IOException  if there is an I/O error.
 644:      * @throws ClassNotFoundException  if there is a classpath problem.
 645:      */
 646:     private void readObject(ObjectInputStream stream)
 647:             throws IOException, ClassNotFoundException {
 648:         stream.defaultReadObject();
 649:         this.wallPaint = SerialUtilities.readPaint(stream);
 650:     }
 651: 
 652: }