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