    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    }