001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     *
027     * ------------------
028     * BarRenderer3D.java
029     * ------------------
030     * (C) Copyright 2001-2007, by Serge V. Grachov and Contributors.
031     *
032     * Original Author:  Serge V. Grachov;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *                   Tin Luu;
035     *                   Milo Simpson;
036     *                   Richard Atkinson;
037     *                   Rich Unger;
038     *                   Christian W. Zuckschwerdt;
039     *
040     * Changes
041     * -------
042     * 31-Oct-2001 : First version, contributed by Serge V. Grachov (DG);
043     * 15-Nov-2001 : Modified to allow for null data values (DG);
044     * 13-Dec-2001 : Added tooltips (DG);
045     * 16-Jan-2002 : Added fix for single category or single series datasets, 
046     *               pointed out by Taoufik Romdhane (DG);
047     * 24-May-2002 : Incorporated tooltips into chart entities (DG);
048     * 11-Jun-2002 : Added check for (permitted) null info object, bug and fix 
049     *               reported by David Basten.  Also updated Javadocs. (DG);
050     * 19-Jun-2002 : Added code to draw labels on bars (TL);
051     * 26-Jun-2002 : Added bar clipping to avoid PRExceptions (DG);
052     * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs 
053     *               for HTML image maps (RA);
054     * 06-Aug-2002 : Value labels now use number formatter, thanks to Milo 
055     *               Simpson (DG);
056     * 08-Aug-2002 : Applied fixed in bug id 592218 (DG);
057     * 20-Sep-2002 : Added fix for categoryPaint by Rich Unger, and fixed errors 
058     *               reported by Checkstyle (DG);
059     * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 
060     *               CategoryToolTipGenerator interface (DG);
061     * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
062     * 06-Nov-2002 : Moved to the com.jrefinery.chart.renderer package (DG);
063     * 28-Jan-2003 : Added an attribute to control the shading of the left and 
064     *               bottom walls in the plot background (DG);
065     * 25-Mar-2003 : Implemented Serializable (DG);
066     * 10-Apr-2003 : Removed category paint usage (DG);
067     * 13-May-2003 : Renamed VerticalBarRenderer3D --> BarRenderer3D and merged with
068     *               HorizontalBarRenderer3D (DG);
069     * 30-Jul-2003 : Modified entity constructor (CZ);
070     * 19-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
071     * 07-Oct-2003 : Added renderer state (DG);
072     * 08-Oct-2003 : Removed clipping (replaced with flag in CategoryPlot to 
073     *               control order in which the data items are processed) (DG);
074     * 20-Oct-2003 : Fixed bug (outline stroke not being used for bar 
075     *               outlines) (DG);
076     * 21-Oct-2003 : Bar width moved into CategoryItemRendererState (DG);
077     * 24-Nov-2003 : Fixed bug 846324 (item labels not showing) (DG);
078     * 27-Nov-2003 : Added code to respect maxBarWidth setting (DG);
079     * 02-Feb-2004 : Fixed bug where 'drawBarOutline' flag is not respected (DG);
080     * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste 
081     *               overriding easier (DG);
082     * 04-Oct-2004 : Fixed bug with item label positioning when plot alignment is 
083     *               horizontal (DG);
084     * 05-Nov-2004 : Modified drawItem() signature (DG);
085     * 20-Apr-2005 : Renamed CategoryLabelGenerator 
086     *               --> CategoryItemLabelGenerator (DG);
087     * 25-Apr-2005 : Override initialise() method to fix bug 1189642 (DG);
088     * 09-Jun-2005 : Use addEntityItem from super class (DG);
089     * ------------- JFREECHART 1.0.x ---------------------------------------------
090     * 07-Dec-2006 : Implemented equals() override (DG);
091     * 17-Jan-2007 : Fixed bug in drawDomainGridline() method (DG);
092     * 03-Apr-2007 : Fixed bugs in drawBackground() method (DG);
093     * 16-Oct-2007 : Fixed bug in range marker drawing (DG);
094     * 
095     */
096    
097    package org.jfree.chart.renderer.category;
098    
099    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            setBasePositiveItemLabelPosition(p1);
193            ItemLabelPosition p2 = new ItemLabelPosition(ItemLabelAnchor.INSIDE12, 
194                    TextAnchor.TOP_CENTER);
195            setBaseNegativeItemLabelPosition(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            fireChangeEvent();
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            Composite originalComposite = g2.getComposite();
307            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
308                    plot.getBackgroundAlpha()));
309            
310            // fill background...
311            Paint backgroundPaint = plot.getBackgroundPaint();
312            if (backgroundPaint != null) {
313                g2.setPaint(backgroundPaint);
314                g2.fill(clip);
315            }
316    
317            GeneralPath leftWall = new GeneralPath();
318            leftWall.moveTo(x0, y0);
319            leftWall.lineTo(x0, y2);
320            leftWall.lineTo(x1, y3);
321            leftWall.lineTo(x1, y1);
322            leftWall.closePath();
323            g2.setPaint(getWallPaint());
324            g2.fill(leftWall);
325    
326            GeneralPath bottomWall = new GeneralPath();
327            bottomWall.moveTo(x0, y0);
328            bottomWall.lineTo(x1, y1);
329            bottomWall.lineTo(x3, y1);
330            bottomWall.lineTo(x2, y0);
331            bottomWall.closePath();
332            g2.setPaint(getWallPaint());
333            g2.fill(bottomWall);
334    
335            // highlight the background corners...
336            g2.setPaint(Color.lightGray);
337            Line2D corner = new Line2D.Double(x0, y0, x1, y1);
338            g2.draw(corner);
339            corner.setLine(x1, y1, x1, y3);
340            g2.draw(corner);
341            corner.setLine(x1, y1, x3, y1);
342            g2.draw(corner);
343                    
344            // draw background image, if there is one...
345            Image backgroundImage = plot.getBackgroundImage();
346            if (backgroundImage != null) {
347                Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX() 
348                        + getXOffset(), dataArea.getY(), 
349                        dataArea.getWidth() - getXOffset(), 
350                        dataArea.getHeight() - getYOffset());
351                plot.drawBackgroundImage(g2, adjusted);
352            }
353            
354            g2.setComposite(originalComposite);
355    
356        }
357    
358        /**
359         * Draws the outline for the plot.
360         *
361         * @param g2  the graphics device.
362         * @param plot  the plot.
363         * @param dataArea  the area inside the axes.
364         */
365        public void drawOutline(Graphics2D g2, CategoryPlot plot, 
366                                Rectangle2D dataArea) {
367    
368            float x0 = (float) dataArea.getX();
369            float x1 = x0 + (float) Math.abs(this.xOffset);
370            float x3 = (float) dataArea.getMaxX();
371            float x2 = x3 - (float) Math.abs(this.xOffset);
372    
373            float y0 = (float) dataArea.getMaxY();
374            float y1 = y0 - (float) Math.abs(this.yOffset);
375            float y3 = (float) dataArea.getMinY();
376            float y2 = y3 + (float) Math.abs(this.yOffset);
377    
378            GeneralPath clip = new GeneralPath();
379            clip.moveTo(x0, y0);
380            clip.lineTo(x0, y2);
381            clip.lineTo(x1, y3);
382            clip.lineTo(x3, y3);
383            clip.lineTo(x3, y1);
384            clip.lineTo(x2, y0);
385            clip.closePath();
386    
387            // put an outline around the data area...
388            Stroke outlineStroke = plot.getOutlineStroke();
389            Paint outlinePaint = plot.getOutlinePaint();
390            if ((outlineStroke != null) && (outlinePaint != null)) {
391                g2.setStroke(outlineStroke);
392                g2.setPaint(outlinePaint);
393                g2.draw(clip);
394            }
395    
396        }
397    
398        /**
399         * Draws a grid line against the domain axis.
400         *
401         * @param g2  the graphics device.
402         * @param plot  the plot.
403         * @param dataArea  the area for plotting data (not yet adjusted for any 
404         *                  3D effect).
405         * @param value  the Java2D value at which the grid line should be drawn.
406         *
407         */
408        public void drawDomainGridline(Graphics2D g2,
409                                       CategoryPlot plot,
410                                       Rectangle2D dataArea,
411                                       double value) {
412    
413            Line2D line1 = null;
414            Line2D line2 = null;
415            PlotOrientation orientation = plot.getOrientation();
416            if (orientation == PlotOrientation.HORIZONTAL) {
417                double y0 = value;
418                double y1 = value - getYOffset();
419                double x0 = dataArea.getMinX();
420                double x1 = x0 + getXOffset();
421                double x2 = dataArea.getMaxX();
422                line1 = new Line2D.Double(x0, y0, x1, y1);
423                line2 = new Line2D.Double(x1, y1, x2, y1);
424            }
425            else if (orientation == PlotOrientation.VERTICAL) {
426                double x0 = value;
427                double x1 = value + getXOffset();
428                double y0 = dataArea.getMaxY();
429                double y1 = y0 - getYOffset();
430                double y2 = dataArea.getMinY();
431                line1 = new Line2D.Double(x0, y0, x1, y1);
432                line2 = new Line2D.Double(x1, y1, x1, y2);
433            }
434            Paint paint = plot.getDomainGridlinePaint();
435            Stroke stroke = plot.getDomainGridlineStroke();
436            g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
437            g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
438            g2.draw(line1);
439            g2.draw(line2);
440    
441        }
442    
443        /**
444         * Draws a grid line against the range axis.
445         *
446         * @param g2  the graphics device.
447         * @param plot  the plot.
448         * @param axis  the value axis.
449         * @param dataArea  the area for plotting data (not yet adjusted for any 
450         *                  3D effect).
451         * @param value  the value at which the grid line should be drawn.
452         *
453         */
454        public void drawRangeGridline(Graphics2D g2,
455                                      CategoryPlot plot,
456                                      ValueAxis axis,
457                                      Rectangle2D dataArea,
458                                      double value) {
459    
460            Range range = axis.getRange();
461    
462            if (!range.contains(value)) {
463                return;
464            }
465    
466            Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
467                    dataArea.getY() + getYOffset(), dataArea.getWidth() 
468                    - getXOffset(), dataArea.getHeight() - getYOffset());
469    
470            Line2D line1 = null;
471            Line2D line2 = null;
472            PlotOrientation orientation = plot.getOrientation();
473            if (orientation == PlotOrientation.HORIZONTAL) {
474                double x0 = axis.valueToJava2D(value, adjusted, 
475                        plot.getRangeAxisEdge());
476                double x1 = x0 + getXOffset();
477                double y0 = dataArea.getMaxY();
478                double y1 = y0 - getYOffset();
479                double y2 = dataArea.getMinY();
480                line1 = new Line2D.Double(x0, y0, x1, y1);
481                line2 = new Line2D.Double(x1, y1, x1, y2);
482            }
483            else if (orientation == PlotOrientation.VERTICAL) {
484                double y0 = axis.valueToJava2D(value, adjusted, 
485                        plot.getRangeAxisEdge());
486                double y1 = y0 - getYOffset();
487                double x0 = dataArea.getMinX();
488                double x1 = x0 + getXOffset();
489                double x2 = dataArea.getMaxX();
490                line1 = new Line2D.Double(x0, y0, x1, y1);
491                line2 = new Line2D.Double(x1, y1, x2, y1);
492            }
493            Paint paint = plot.getRangeGridlinePaint();
494            Stroke stroke = plot.getRangeGridlineStroke();
495            g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
496            g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
497            g2.draw(line1);
498            g2.draw(line2);
499    
500        }
501    
502        /**
503         * Draws a range marker.
504         *
505         * @param g2  the graphics device.
506         * @param plot  the plot.
507         * @param axis  the value axis.
508         * @param marker  the marker.
509         * @param dataArea  the area for plotting data (not including 3D effect).
510         */
511        public void drawRangeMarker(Graphics2D g2,
512                                    CategoryPlot plot,
513                                    ValueAxis axis,
514                                    Marker marker,
515                                    Rectangle2D dataArea) {
516    
517    
518            Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(), 
519                    dataArea.getY() + getYOffset(), dataArea.getWidth() 
520                    - getXOffset(), dataArea.getHeight() - getYOffset());
521            if (marker instanceof ValueMarker) {
522                ValueMarker vm = (ValueMarker) marker;
523                double value = vm.getValue();
524                Range range = axis.getRange();
525                if (!range.contains(value)) {
526                    return;
527                }
528    
529                GeneralPath path = null;
530                PlotOrientation orientation = plot.getOrientation();
531                if (orientation == PlotOrientation.HORIZONTAL) {
532                    float x = (float) axis.valueToJava2D(value, adjusted, 
533                            plot.getRangeAxisEdge());
534                    float y = (float) adjusted.getMaxY();
535                    path = new GeneralPath();
536                    path.moveTo(x, y);
537                    path.lineTo((float) (x + getXOffset()), 
538                            y - (float) getYOffset());
539                    path.lineTo((float) (x + getXOffset()), 
540                            (float) (adjusted.getMinY() - getYOffset()));
541                    path.lineTo(x, (float) adjusted.getMinY());
542                    path.closePath();
543                }
544                else if (orientation == PlotOrientation.VERTICAL) {
545                    float y = (float) axis.valueToJava2D(value, adjusted, 
546                            plot.getRangeAxisEdge());
547                    float x = (float) dataArea.getX();
548                    path = new GeneralPath();
549                    path.moveTo(x, y);
550                    path.lineTo(x + (float) this.xOffset, y - (float) this.yOffset);
551                    path.lineTo((float) (adjusted.getMaxX() + this.xOffset), 
552                            y - (float) this.yOffset);
553                    path.lineTo((float) (adjusted.getMaxX()), y);
554                    path.closePath();
555                }
556                g2.setPaint(marker.getPaint());
557                g2.fill(path);
558                g2.setPaint(marker.getOutlinePaint());
559                g2.draw(path);
560            
561                String label = marker.getLabel();
562                RectangleAnchor anchor = marker.getLabelAnchor();
563                if (label != null) {
564                    Font labelFont = marker.getLabelFont();
565                    g2.setFont(labelFont);
566                    g2.setPaint(marker.getLabelPaint());
567                    Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
568                            g2, orientation, dataArea, path.getBounds2D(), 
569                            marker.getLabelOffset(), LengthAdjustmentType.EXPAND, 
570                            anchor);
571                    TextUtilities.drawAlignedString(label, g2, 
572                            (float) coordinates.getX(), (float) coordinates.getY(), 
573                            marker.getLabelTextAnchor());
574                }
575            
576            }
577            else {
578                super.drawRangeMarker(g2, plot, axis, marker, adjusted);
579                // TODO: draw the interval marker with a 3D effect
580            }
581        }
582    
583        /**
584         * Draws a 3D bar to represent one data item.
585         *
586         * @param g2  the graphics device.
587         * @param state  the renderer state.
588         * @param dataArea  the area for plotting the data.
589         * @param plot  the plot.
590         * @param domainAxis  the domain axis.
591         * @param rangeAxis  the range axis.
592         * @param dataset  the dataset.
593         * @param row  the row index (zero-based).
594         * @param column  the column index (zero-based).
595         * @param pass  the pass index.
596         */
597        public void drawItem(Graphics2D g2,
598                             CategoryItemRendererState state,
599                             Rectangle2D dataArea,
600                             CategoryPlot plot,
601                             CategoryAxis domainAxis,
602                             ValueAxis rangeAxis,
603                             CategoryDataset dataset,
604                             int row,
605                             int column,
606                             int pass) {
607        
608            // check the value we are plotting...
609            Number dataValue = dataset.getValue(row, column);
610            if (dataValue == null) {
611                return;
612            }
613            
614            double value = dataValue.doubleValue();
615            
616            Rectangle2D adjusted = new Rectangle2D.Double(dataArea.getX(),
617                    dataArea.getY() + getYOffset(), 
618                    dataArea.getWidth() - getXOffset(), 
619                    dataArea.getHeight() - getYOffset());
620    
621            PlotOrientation orientation = plot.getOrientation();
622            
623            double barW0 = calculateBarW0(plot, orientation, adjusted, domainAxis, 
624                    state, row, column);
625            double[] barL0L1 = calculateBarL0L1(value);
626            if (barL0L1 == null) {
627                return;  // the bar is not visible
628            }
629    
630            RectangleEdge edge = plot.getRangeAxisEdge();
631            double transL0 = rangeAxis.valueToJava2D(barL0L1[0], adjusted, edge);
632            double transL1 = rangeAxis.valueToJava2D(barL0L1[1], adjusted, edge);
633            double barL0 = Math.min(transL0, transL1);
634            double barLength = Math.abs(transL1 - transL0);
635            
636            // draw the bar...
637            Rectangle2D bar = null;
638            if (orientation == PlotOrientation.HORIZONTAL) {
639                bar = new Rectangle2D.Double(barL0, barW0, barLength, 
640                        state.getBarWidth());
641            }
642            else {
643                bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(), 
644                        barLength);
645            }
646            Paint itemPaint = getItemPaint(row, column);
647            g2.setPaint(itemPaint);
648            g2.fill(bar);
649    
650            double x0 = bar.getMinX();
651            double x1 = x0 + getXOffset();
652            double x2 = bar.getMaxX();
653            double x3 = x2 + getXOffset();
654            
655            double y0 = bar.getMinY() - getYOffset();
656            double y1 = bar.getMinY();
657            double y2 = bar.getMaxY() - getYOffset();
658            double y3 = bar.getMaxY();
659            
660            GeneralPath bar3dRight = null;
661            GeneralPath bar3dTop = null;
662            if (barLength > 0.0) {
663                bar3dRight = new GeneralPath();
664                bar3dRight.moveTo((float) x2, (float) y3);
665                bar3dRight.lineTo((float) x2, (float) y1);
666                bar3dRight.lineTo((float) x3, (float) y0);
667                bar3dRight.lineTo((float) x3, (float) y2);
668                bar3dRight.closePath();
669    
670                if (itemPaint instanceof Color) {
671                    g2.setPaint(((Color) itemPaint).darker());
672                }
673                g2.fill(bar3dRight);
674            }
675    
676            bar3dTop = new GeneralPath();
677            bar3dTop.moveTo((float) x0, (float) y1);
678            bar3dTop.lineTo((float) x1, (float) y0);
679            bar3dTop.lineTo((float) x3, (float) y0);
680            bar3dTop.lineTo((float) x2, (float) y1);
681            bar3dTop.closePath();
682            g2.fill(bar3dTop);
683    
684            if (isDrawBarOutline() 
685                    && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
686                g2.setStroke(getItemOutlineStroke(row, column));
687                g2.setPaint(getItemOutlinePaint(row, column));
688                g2.draw(bar);
689                if (bar3dRight != null) {
690                    g2.draw(bar3dRight);
691                }
692                if (bar3dTop != null) {
693                    g2.draw(bar3dTop);
694                }
695            }
696    
697            CategoryItemLabelGenerator generator 
698                = getItemLabelGenerator(row, column);
699            if (generator != null && isItemLabelVisible(row, column)) {
700                drawItemLabel(g2, dataset, row, column, plot, generator, bar, 
701                        (value < 0.0));
702            }        
703    
704            // add an item entity, if this information is being collected
705            EntityCollection entities = state.getEntityCollection();
706            if (entities != null) {
707                GeneralPath barOutline = new GeneralPath();
708                barOutline.moveTo((float) x0, (float) y3);
709                barOutline.lineTo((float) x0, (float) y1);
710                barOutline.lineTo((float) x1, (float) y0);
711                barOutline.lineTo((float) x3, (float) y0);
712                barOutline.lineTo((float) x3, (float) y2);
713                barOutline.lineTo((float) x2, (float) y3);
714                barOutline.closePath();
715                addItemEntity(entities, dataset, row, column, barOutline);
716            }
717    
718        }
719        
720        /**
721         * Tests this renderer for equality with an arbitrary object.
722         * 
723         * @param obj  the object (<code>null</code> permitted).
724         * 
725         * @return A boolean.
726         */
727        public boolean equals(Object obj) {
728            if (obj == this) {
729                return true;
730            }
731            if (!(obj instanceof BarRenderer3D)) {
732                return false;
733            }
734            BarRenderer3D that = (BarRenderer3D) obj;
735            if (this.xOffset != that.xOffset) {
736                return false;
737            }
738            if (this.yOffset != that.yOffset) {
739                return false;
740            }
741            if (!PaintUtilities.equal(this.wallPaint, that.wallPaint)) {
742                return false;
743            }
744            return super.equals(obj);
745        }
746    
747        /**
748         * Provides serialization support.
749         *
750         * @param stream  the output stream.
751         *
752         * @throws IOException  if there is an I/O error.
753         */
754        private void writeObject(ObjectOutputStream stream) throws IOException {
755            stream.defaultWriteObject();
756            SerialUtilities.writePaint(this.wallPaint, stream);
757        }
758    
759        /**
760         * Provides serialization support.
761         *
762         * @param stream  the input stream.
763         *
764         * @throws IOException  if there is an I/O error.
765         * @throws ClassNotFoundException  if there is a classpath problem.
766         */
767        private void readObject(ObjectInputStream stream) 
768            throws IOException, ClassNotFoundException {
769            stream.defaultReadObject();
770            this.wallPaint = SerialUtilities.readPaint(stream);
771        }
772    
773    }