001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2013, 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 * GradientXYBarPainter.java
029 * -------------------------
030 * (C) Copyright 2008, 2009, 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 * 22-Feb-2009 : Fixed bug drawing outlines (DG);
039 *
040 */
041
042package org.jfree.chart.renderer.xy;
043
044import java.awt.Color;
045import java.awt.GradientPaint;
046import java.awt.Graphics2D;
047import java.awt.Paint;
048import java.awt.Stroke;
049import java.awt.geom.Rectangle2D;
050import java.awt.geom.RectangularShape;
051import java.io.Serializable;
052
053import org.jfree.chart.HashUtilities;
054import org.jfree.ui.RectangleEdge;
055
056/**
057 * An implementation of the {@link XYBarPainter} interface that uses several
058 * gradient fills to enrich the appearance of the bars.
059 *
060 * @since 1.0.11
061 */
062public class GradientXYBarPainter implements XYBarPainter, 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 GradientXYBarPainter() {
077        this(0.10, 0.20, 0.80);
078    }
079
080    /**
081     * Creates a new instance.
082     *
083     * @param g1  the division between regions 1 and 2.
084     * @param g2  the division between regions 2 and 3.
085     * @param g3  the division between regions 3 and 4.
086     */
087    public GradientXYBarPainter(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    @Override
105    public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row,
106            int column, RectangularShape bar, RectangleEdge base) {
107
108        Paint itemPaint = renderer.getItemPaint(row, column);
109
110        Color c0, c1;
111        if (itemPaint instanceof Color) {
112            c0 = (Color) itemPaint;
113            c1 = c0.brighter();
114        }
115        else if (itemPaint instanceof GradientPaint) {
116            GradientPaint gp = (GradientPaint) itemPaint;
117            c0 = gp.getColor1();
118            c1 = gp.getColor2();
119        }
120        else {
121            c0 = Color.blue;
122            c1 = Color.blue.brighter();
123        }
124
125        // as a special case, if the bar colour has alpha == 0, we draw
126        // nothing.
127        if (c0.getAlpha() == 0) {
128            return;
129        }
130
131        if (base == RectangleEdge.TOP || base == RectangleEdge.BOTTOM) {
132            Rectangle2D[] regions = splitVerticalBar(bar, this.g1, this.g2,
133                    this.g3);
134            GradientPaint gp = new GradientPaint((float) regions[0].getMinX(),
135                    0.0f, c0, (float) regions[0].getMaxX(), 0.0f, Color.white);
136            g2.setPaint(gp);
137            g2.fill(regions[0]);
138
139            gp = new GradientPaint((float) regions[1].getMinX(), 0.0f,
140                    Color.white, (float) regions[1].getMaxX(), 0.0f, c0);
141            g2.setPaint(gp);
142            g2.fill(regions[1]);
143
144            gp = new GradientPaint((float) regions[2].getMinX(), 0.0f, c0,
145                    (float) regions[2].getMaxX(), 0.0f, c1);
146            g2.setPaint(gp);
147            g2.fill(regions[2]);
148
149            gp = new GradientPaint((float) regions[3].getMinX(), 0.0f, c1,
150                     (float) regions[3].getMaxX(), 0.0f, c0);
151            g2.setPaint(gp);
152            g2.fill(regions[3]);
153        }
154        else if (base == RectangleEdge.LEFT || base == RectangleEdge.RIGHT) {
155            Rectangle2D[] regions = splitHorizontalBar(bar, this.g1, this.g2,
156                    this.g3);
157            GradientPaint gp = new GradientPaint(0.0f,
158                    (float) regions[0].getMinY(), c0, 0.0f,
159                    (float) regions[0].getMaxX(), Color.white);
160            g2.setPaint(gp);
161            g2.fill(regions[0]);
162
163            gp = new GradientPaint(0.0f, (float) regions[1].getMinY(),
164                    Color.white, 0.0f, (float) regions[1].getMaxY(), c0);
165            g2.setPaint(gp);
166            g2.fill(regions[1]);
167
168            gp = new GradientPaint(0.0f, (float) regions[2].getMinY(), c0,
169                    0.0f, (float) regions[2].getMaxY(), c1);
170            g2.setPaint(gp);
171            g2.fill(regions[2]);
172
173            gp = new GradientPaint(0.0f, (float) regions[3].getMinY(), c1,
174                     0.0f, (float) regions[3].getMaxY(), c0);
175            g2.setPaint(gp);
176            g2.fill(regions[3]);
177
178        }
179
180        // draw the outline...
181        if (renderer.isDrawBarOutline()) {
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    @Override
206    public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, int row,
207            int column, RectangularShape bar, RectangleEdge base,
208            boolean pegShadow) {
209
210        // handle a special case - if the bar colour has alpha == 0, it is
211        // invisible so we shouldn't draw any shadow
212        Paint itemPaint = renderer.getItemPaint(row, column);
213        if (itemPaint instanceof Color) {
214            Color c = (Color) itemPaint;
215            if (c.getAlpha() == 0) {
216                return;
217            }
218        }
219
220        RectangularShape shadow = createShadow(bar, renderer.getShadowXOffset(),
221                renderer.getShadowYOffset(), base, pegShadow);
222        g2.setPaint(Color.gray);
223        g2.fill(shadow);
224
225    }
226
227    /**
228     * Creates a shadow for the bar.
229     *
230     * @param bar  the bar shape.
231     * @param xOffset  the x-offset for the shadow.
232     * @param yOffset  the y-offset for the shadow.
233     * @param base  the edge that is the base of the bar.
234     * @param pegShadow  peg the shadow to the base?
235     *
236     * @return A rectangle for the shadow.
237     */
238    private Rectangle2D createShadow(RectangularShape bar, double xOffset,
239            double yOffset, RectangleEdge base, boolean pegShadow) {
240        double x0 = bar.getMinX();
241        double x1 = bar.getMaxX();
242        double y0 = bar.getMinY();
243        double y1 = bar.getMaxY();
244        if (base == RectangleEdge.TOP) {
245            x0 += xOffset;
246            x1 += xOffset;
247            if (!pegShadow) {
248                y0 += yOffset;
249            }
250            y1 += yOffset;
251        }
252        else if (base == RectangleEdge.BOTTOM) {
253            x0 += xOffset;
254            x1 += xOffset;
255            y0 += yOffset;
256            if (!pegShadow) {
257                y1 += yOffset;
258            }
259        }
260        else if (base == RectangleEdge.LEFT) {
261            if (!pegShadow) {
262                x0 += xOffset;
263            }
264            x1 += xOffset;
265            y0 += yOffset;
266            y1 += yOffset;
267        }
268        else if (base == RectangleEdge.RIGHT) {
269            x0 += xOffset;
270            if (!pegShadow) {
271                x1 += xOffset;
272            }
273            y0 += yOffset;
274            y1 += yOffset;
275        }
276        return new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0));
277    }
278
279    /**
280     * Splits a bar into subregions (elsewhere, these subregions will have
281     * different gradients applied to them).
282     *
283     * @param bar  the bar shape.
284     * @param a  the first division.
285     * @param b  the second division.
286     * @param c  the third division.
287     *
288     * @return An array containing four subregions.
289     */
290    private Rectangle2D[] splitVerticalBar(RectangularShape bar, double a,
291            double b, double c) {
292        Rectangle2D[] result = new Rectangle2D[4];
293        double x0 = bar.getMinX();
294        double x1 = Math.rint(x0 + (bar.getWidth() * a));
295        double x2 = Math.rint(x0 + (bar.getWidth() * b));
296        double x3 = Math.rint(x0 + (bar.getWidth() * c));
297        result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(),
298                x1 - x0, bar.getHeight());
299        result[1] = new Rectangle2D.Double(x1, bar.getMinY(), x2 - x1,
300                bar.getHeight());
301        result[2] = new Rectangle2D.Double(x2, bar.getMinY(), x3 - x2,
302                bar.getHeight());
303        result[3] = new Rectangle2D.Double(x3, bar.getMinY(),
304                bar.getMaxX() - x3, bar.getHeight());
305        return result;
306    }
307
308    /**
309     * Splits a bar into subregions (elsewhere, these subregions will have
310     * different gradients applied to them).
311     *
312     * @param bar  the bar shape.
313     * @param a  the first division.
314     * @param b  the second division.
315     * @param c  the third division.
316     *
317     * @return An array containing four subregions.
318     */
319    private Rectangle2D[] splitHorizontalBar(RectangularShape bar, double a,
320            double b, double c) {
321        Rectangle2D[] result = new Rectangle2D[4];
322        double y0 = bar.getMinY();
323        double y1 = Math.rint(y0 + (bar.getHeight() * a));
324        double y2 = Math.rint(y0 + (bar.getHeight() * b));
325        double y3 = Math.rint(y0 + (bar.getHeight() * c));
326        result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(),
327                bar.getWidth(), y1 - y0);
328        result[1] = new Rectangle2D.Double(bar.getMinX(), y1, bar.getWidth(),
329                y2 - y1);
330        result[2] = new Rectangle2D.Double(bar.getMinX(), y2, bar.getWidth(),
331                y3 - y2);
332        result[3] = new Rectangle2D.Double(bar.getMinX(), y3, bar.getWidth(),
333                bar.getMaxY() - y3);
334        return result;
335    }
336
337    /**
338     * Tests this instance for equality with an arbitrary object.
339     *
340     * @param obj  the obj (<code>null</code> permitted).
341     *
342     * @return A boolean.
343     */
344    @Override
345    public boolean equals(Object obj) {
346        if (obj == this) {
347            return true;
348        }
349        if (!(obj instanceof GradientXYBarPainter)) {
350            return false;
351        }
352        GradientXYBarPainter that = (GradientXYBarPainter) obj;
353        if (this.g1 != that.g1) {
354            return false;
355        }
356        if (this.g2 != that.g2) {
357            return false;
358        }
359        if (this.g3 != that.g3) {
360            return false;
361        }
362        return true;
363    }
364
365    /**
366     * Returns a hash code for this instance.
367     *
368     * @return A hash code.
369     */
370    @Override
371    public int hashCode() {
372        int hash = 37;
373        hash = HashUtilities.hashCode(hash, this.g1);
374        hash = HashUtilities.hashCode(hash, this.g2);
375        hash = HashUtilities.hashCode(hash, this.g3);
376        return hash;
377    }
378
379}