001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2005, 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-2004, 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     * $Id: BarRenderer3D.java,v 1.10.2.3 2005/10/25 20:54:16 mungady Exp $
041     *
042     * Changes
043     * -------
044     * 31-Oct-2001 : First version, contributed by Serge V. Grachov (DG);
045     * 15-Nov-2001 : Modified to allow for null data values (DG);
046     * 13-Dec-2001 : Added tooltips (DG);
047     * 16-Jan-2002 : Added fix for single category or single series datasets, 
048     *               pointed out by Taoufik Romdhane (DG);
049     * 24-May-2002 : Incorporated tooltips into chart entities (DG);
050     * 11-Jun-2002 : Added check for (permitted) null info object, bug and fix 
051     *               reported by David Basten.  Also updated Javadocs. (DG);
052     * 19-Jun-2002 : Added code to draw labels on bars (TL);
053     * 26-Jun-2002 : Added bar clipping to avoid PRExceptions (DG);
054     * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs 
055     *               for HTML image maps (RA);
056     * 06-Aug-2002 : Value labels now use number formatter, thanks to Milo 
057     *               Simpson (DG);
058     * 08-Aug-2002 : Applied fixed in bug id 592218 (DG);
059     * 20-Sep-2002 : Added fix for categoryPaint by Rich Unger, and fixed errors 
060     *               reported by Checkstyle (DG);
061     * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 
062     *               CategoryToolTipGenerator interface (DG);
063     * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
064     * 06-Nov-2002 : Moved to the com.jrefinery.chart.renderer package (DG);
065     * 28-Jan-2003 : Added an attribute to control the shading of the left and 
066     *               bottom walls in the plot background (DG);
067     * 25-Mar-2003 : Implemented Serializable (DG);
068     * 10-Apr-2003 : Removed category paint usage (DG);
069     * 13-May-2003 : Renamed VerticalBarRenderer3D --> BarRenderer3D and merged with
070     *               HorizontalBarRenderer3D (DG);
071     * 30-Jul-2003 : Modified entity constructor (CZ);
072     * 19-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
073     * 07-Oct-2003 : Added renderer state (DG);
074     * 08-Oct-2003 : Removed clipping (replaced with flag in CategoryPlot to 
075     *               control order in which the data items are processed) (DG);
076     * 20-Oct-2003 : Fixed bug (outline stroke not being used for bar 
077     *               outlines) (DG);
078     * 21-Oct-2003 : Bar width moved into CategoryItemRendererState (DG);
079     * 24-Nov-2003 : Fixed bug 846324 (item labels not showing) (DG);
080     * 27-Nov-2003 : Added code to respect maxBarWidth setting (DG);
081     * 02-Feb-2004 : Fixed bug where 'drawBarOutline' flag is not respected (DG);
082     * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste 
083     *               overriding easier (DG);
084     * 04-Oct-2004 : Fixed bug with item label positioning when plot alignment is 
085     *               horizontal (DG);
086     * 05-Nov-2004 : Modified drawItem() signature (DG);
087     * 20-Apr-2005 : Renamed CategoryLabelGenerator 
088     *               --> CategoryItemLabelGenerator (DG);
089     * 25-Apr-2005 : Override initialise() method to fix bug 1189642 (DG);
090     * 09-Jun-2005 : Use addEntityItem from super class (DG);
091     */
092    
093    package org.jfree.chart.renderer.category;
094    
095    import java.awt.AlphaComposite;
096    import java.awt.Color;
097    import java.awt.Composite;
098    import java.awt.Font;
099    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    }