    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2011, 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     * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025     * Other names may be trademarks of their respective owners.]
    026     *
    027     * -----------------------
    028     * GradientBarPainter.java
    029     * -----------------------
    030     * (C) Copyright 2008, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * Changes:
    036     * --------
    037     * 19-Jun-2008 : Version 1 (DG);
    038     * 15-Aug-2008 : Use outline paint and shadow paint (DG);
    039     *
    040     */
    041
    042    package org.jfree.chart.renderer.category;
    043
    044    import java.awt.Color;
    045    import java.awt.GradientPaint;
    046    import java.awt.Graphics2D;
    047    import java.awt.Paint;
    048    import java.awt.Stroke;
    049    import java.awt.geom.Rectangle2D;
    050    import java.awt.geom.RectangularShape;
    051    import java.io.Serializable;
    052
    053    import org.jfree.chart.HashUtilities;
    054    import org.jfree.ui.RectangleEdge;
    055
    056    /**
    057     * An implementation of the {@link BarPainter} interface that uses several
    058     * gradient fills to enrich the appearance of the bars.
    059     *
    060     * @since 1.0.11
    061     */
    062    public class GradientBarPainter implements BarPainter, Serializable {
    063
    064        /** The division point between the first and second gradient regions. */
    065        private double g1;
    066
    067        /** The division point between the second and third gradient regions. */
    068        private double g2;
    069
    070        /** The division point between the third and fourth gradient regions. */
    071        private double g3;
    072
    073        /**
    074         * Creates a new instance.
    075         */
    076        public GradientBarPainter() {
    077            this(0.10, 0.20, 0.80);
    078        }
    079
    080        /**
    081         * Creates a new instance.
    082         *
    083         * @param g1
    084         * @param g2
    085         * @param g3
    086         */
    087        public GradientBarPainter(double g1, double g2, double g3) {
    088            this.g1 = g1;
    089            this.g2 = g2;
    090            this.g3 = g3;
    091        }
    092
    093        /**
    094         * Paints a single bar instance.
    095         *
    096         * @param g2  the graphics target.
    097         * @param renderer  the renderer.
    098         * @param row  the row index.
    099         * @param column  the column index.
    100         * @param bar  the bar
    101         * @param base  indicates which side of the rectangle is the base of the
    102         *              bar.
    103         */
    104        public void paintBar(Graphics2D g2, BarRenderer renderer, int row,
    105                int column, RectangularShape bar, RectangleEdge base) {
    106
    107            Paint itemPaint = renderer.getItemPaint(row, column);
    108
    109            Color c0, c1;
    110            if (itemPaint instanceof Color) {
    111                c0 = (Color) itemPaint;
    112                c1 = c0.brighter();
    113            }
    114            else if (itemPaint instanceof GradientPaint) {
    115                GradientPaint gp = (GradientPaint) itemPaint;
    116                c0 = gp.getColor1();
    117                c1 = gp.getColor2();
    118            }
    119            else {
    120                c0 = Color.blue;
    121                c1 = Color.blue.brighter();
    122            }
    123
    124            // as a special case, if the bar colour has alpha == 0, we draw
    125            // nothing.
    126            if (c0.getAlpha() == 0) {
    127                return;
    128            }
    129
    130            if (base == RectangleEdge.TOP || base == RectangleEdge.BOTTOM) {
    131                Rectangle2D[] regions = splitVerticalBar(bar, this.g1, this.g2,
    132                        this.g3);
    133                GradientPaint gp = new GradientPaint((float) regions[0].getMinX(),
    134                        0.0f, c0, (float) regions[0].getMaxX(), 0.0f, Color.white);
    135                g2.setPaint(gp);
    136                g2.fill(regions[0]);
    137
    138                gp = new GradientPaint((float) regions[1].getMinX(), 0.0f,
    139                        Color.white, (float) regions[1].getMaxX(), 0.0f, c0);
    140                g2.setPaint(gp);
    141                g2.fill(regions[1]);
    142
    143                gp = new GradientPaint((float) regions[2].getMinX(), 0.0f, c0,
    144                        (float) regions[2].getMaxX(), 0.0f, c1);
    145                g2.setPaint(gp);
    146                g2.fill(regions[2]);
    147
    148                gp = new GradientPaint((float) regions[3].getMinX(), 0.0f, c1,
    149                         (float) regions[3].getMaxX(), 0.0f, c0);
    150                g2.setPaint(gp);
    151                g2.fill(regions[3]);
    152            }
    153            else if (base == RectangleEdge.LEFT || base == RectangleEdge.RIGHT) {
    154                Rectangle2D[] regions = splitHorizontalBar(bar, this.g1, this.g2,
    155                        this.g3);
    156                GradientPaint gp = new GradientPaint(0.0f,
    157                        (float) regions[0].getMinY(), c0, 0.0f,
    158                        (float) regions[0].getMaxX(), Color.white);
    159                g2.setPaint(gp);
    160                g2.fill(regions[0]);
    161
    162                gp = new GradientPaint(0.0f, (float) regions[1].getMinY(),
    163                        Color.white, 0.0f, (float) regions[1].getMaxY(), c0);
    164                g2.setPaint(gp);
    165                g2.fill(regions[1]);
    166
    167                gp = new GradientPaint(0.0f, (float) regions[2].getMinY(), c0,
    168                        0.0f, (float) regions[2].getMaxY(), c1);
    169                g2.setPaint(gp);
    170                g2.fill(regions[2]);
    171
    172                gp = new GradientPaint(0.0f, (float) regions[3].getMinY(), c1,
    173                         0.0f, (float) regions[3].getMaxY(), c0);
    174                g2.setPaint(gp);
    175                g2.fill(regions[3]);
    176
    177            }
    178
    179            // draw the outline...
    180            if (renderer.isDrawBarOutline()
    181                /*&& state.getBarWidth() > renderer.BAR_OUTLINE_WIDTH_THRESHOLD*/) {
    182                Stroke stroke = renderer.getItemOutlineStroke(row, column);
    183                Paint paint = renderer.getItemOutlinePaint(row, column);
    184                if (stroke != null && paint != null) {
    185                    g2.setStroke(stroke);
    186                    g2.setPaint(paint);
    187                    g2.draw(bar);
    188                }
    189            }
    190
    191        }
    192
    193        /**
    194         * Paints a single bar instance.
    195         *
    196         * @param g2  the graphics target.
    197         * @param renderer  the renderer.
    198         * @param row  the row index.
    199         * @param column  the column index.
    200         * @param bar  the bar
    201         * @param base  indicates which side of the rectangle is the base of the
    202         *              bar.
    203         * @param pegShadow  peg the shadow to the base of the bar?
    204         */
    205        public void paintBarShadow(Graphics2D g2, BarRenderer renderer, int row,
    206                int column, RectangularShape bar, RectangleEdge base,
    207                boolean pegShadow) {
    208
    209            // handle a special case - if the bar colour has alpha == 0, it is
    210            // invisible so we shouldn't draw any shadow
    211            Paint itemPaint = renderer.getItemPaint(row, column);
    212            if (itemPaint instanceof Color) {
    213                Color c = (Color) itemPaint;
    214                if (c.getAlpha() == 0) {
    215                    return;
    216                }
    217            }
    218
    219            RectangularShape shadow = createShadow(bar, renderer.getShadowXOffset(),
    220                    renderer.getShadowYOffset(), base, pegShadow);
    221            g2.setPaint(renderer.getShadowPaint());
    222            g2.fill(shadow);
    223
    224        }
    225
    226        /**
    227         * Creates a shadow for the bar.
    228         *
    229         * @param bar  the bar shape.
    230         * @param xOffset  the x-offset for the shadow.
    231         * @param yOffset  the y-offset for the shadow.
    232         * @param base  the edge that is the base of the bar.
    233         * @param pegShadow  peg the shadow to the base?
    234         *
    235         * @return A rectangle for the shadow.
    236         */
    237        private Rectangle2D createShadow(RectangularShape bar, double xOffset,
    238                double yOffset, RectangleEdge base, boolean pegShadow) {
    239            double x0 = bar.getMinX();
    240            double x1 = bar.getMaxX();
    241            double y0 = bar.getMinY();
    242            double y1 = bar.getMaxY();
    243            if (base == RectangleEdge.TOP) {
    244                x0 += xOffset;
    245                x1 += xOffset;
    246                if (!pegShadow) {
    247                    y0 += yOffset;
    248                }
    249                y1 += yOffset;
    250            }
    251            else if (base == RectangleEdge.BOTTOM) {
    252                x0 += xOffset;
    253                x1 += xOffset;
    254                y0 += yOffset;
    255                if (!pegShadow) {
    256                    y1 += yOffset;
    257                }
    258            }
    259            else if (base == RectangleEdge.LEFT) {
    260                if (!pegShadow) {
    261                    x0 += xOffset;
    262                }
    263                x1 += xOffset;
    264                y0 += yOffset;
    265                y1 += yOffset;
    266            }
    267            else if (base == RectangleEdge.RIGHT) {
    268                x0 += xOffset;
    269                if (!pegShadow) {
    270                    x1 += xOffset;
    271                }
    272                y0 += yOffset;
    273                y1 += yOffset;
    274            }
    275            return new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0));
    276        }
    277
    278        /**
    279         * Splits a bar into subregions (elsewhere, these subregions will have
    280         * different gradients applied to them).
    281         *
    282         * @param bar  the bar shape.
    283         * @param a  the first division.
    284         * @param b  the second division.
    285         * @param c  the third division.
    286         *
    287         * @return An array containing four subregions.
    288         */
    289        private Rectangle2D[] splitVerticalBar(RectangularShape bar, double a,
    290                double b, double c) {
    291            Rectangle2D[] result = new Rectangle2D[4];
    292            double x0 = bar.getMinX();
    293            double x1 = Math.rint(x0 + (bar.getWidth() * a));
    294            double x2 = Math.rint(x0 + (bar.getWidth() * b));
    295            double x3 = Math.rint(x0 + (bar.getWidth() * c));
    296            result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(),
    297                    x1 - x0, bar.getHeight());
    298            result[1] = new Rectangle2D.Double(x1, bar.getMinY(), x2 - x1,
    299                    bar.getHeight());
    300            result[2] = new Rectangle2D.Double(x2, bar.getMinY(), x3 - x2,
    301                    bar.getHeight());
    302            result[3] = new Rectangle2D.Double(x3, bar.getMinY(),
    303                    bar.getMaxX() - x3, bar.getHeight());
    304            return result;
    305        }
    306
    307        /**
    308         * Splits a bar into subregions (elsewhere, these subregions will have
    309         * different gradients applied to them).
    310         *
    311         * @param bar  the bar shape.
    312         * @param a  the first division.
    313         * @param b  the second division.
    314         * @param c  the third division.
    315         *
    316         * @return An array containing four subregions.
    317         */
    318        private Rectangle2D[] splitHorizontalBar(RectangularShape bar, double a,
    319                double b, double c) {
    320            Rectangle2D[] result = new Rectangle2D[4];
    321            double y0 = bar.getMinY();
    322            double y1 = Math.rint(y0 + (bar.getHeight() * a));
    323            double y2 = Math.rint(y0 + (bar.getHeight() * b));
    324            double y3 = Math.rint(y0 + (bar.getHeight() * c));
    325            result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(),
    326                    bar.getWidth(), y1 - y0);
    327            result[1] = new Rectangle2D.Double(bar.getMinX(), y1, bar.getWidth(),
    328                    y2 - y1);
    329            result[2] = new Rectangle2D.Double(bar.getMinX(), y2, bar.getWidth(),
    330                    y3 - y2);
    331            result[3] = new Rectangle2D.Double(bar.getMinX(), y3, bar.getWidth(),
    332                    bar.getMaxY() - y3);
    333            return result;
    334        }
    335
    336        /**
    337         * Tests this instance for equality with an arbitrary object.
    338         *
    339         * @param obj  the obj (<code>null</code> permitted).
    340         *
    341         * @return A boolean.
    342         */
    343        public boolean equals(Object obj) {
    344            if (obj == this) {
    345                return true;
    346            }
    347            if (!(obj instanceof GradientBarPainter)) {
    348                return false;
    349            }
    350            GradientBarPainter that = (GradientBarPainter) obj;
    351            if (this.g1 != that.g1) {
    352                return false;
    353            }
    354            if (this.g2 != that.g2) {
    355                return false;
    356            }
    357            if (this.g3 != that.g3) {
    358                return false;
    359            }
    360            return true;
    361        }
    362
    363        /**
    364         * Returns a hash code for this instance.
    365         *
    366         * @return A hash code.
    367         */
    368        public int hashCode() {
    369            int hash = 37;
    370            hash = HashUtilities.hashCode(hash, this.g1);
    371            hash = HashUtilities.hashCode(hash, this.g2);
    372            hash = HashUtilities.hashCode(hash, this.g3);
    373            return hash;
    374        }
    375
    376    }