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