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