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}