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 * DefaultDrawingSupplier.java 029 * --------------------------- 030 * (C) Copyright 2003-2008, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Jeremy Bowman; 034 * 035 * Changes 036 * ------- 037 * 16-Jan-2003 : Version 1 (DG); 038 * 17-Jan-2003 : Added stroke method, renamed DefaultPaintSupplier 039 * --> DefaultDrawingSupplier (DG) 040 * 27-Jan-2003 : Incorporated code from SeriesShapeFactory, originally 041 * contributed by Jeremy Bowman (DG); 042 * 25-Mar-2003 : Implemented Serializable (DG); 043 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 044 * ------------- JFREECHART 1.0.x --------------------------------------------- 045 * 13-Jun-2007 : Added fillPaintSequence (DG); 046 * 047 */ 048 049 package org.jfree.chart.plot; 050 051import java.awt.BasicStroke; 052import java.awt.Color; 053import java.awt.Paint; 054import java.awt.Polygon; 055import java.awt.Shape; 056import java.awt.Stroke; 057import java.awt.geom.Ellipse2D; 058import java.awt.geom.Rectangle2D; 059import java.io.IOException; 060import java.io.ObjectInputStream; 061import java.io.ObjectOutputStream; 062import java.io.Serializable; 063import java.util.Arrays; 064 065import org.jfree.chart.ChartColor; 066import org.jfree.io.SerialUtilities; 067import org.jfree.util.PublicCloneable; 068import org.jfree.util.ShapeUtilities; 069 070/** 071 * A default implementation of the {@link DrawingSupplier} interface. All 072 * {@link Plot} instances have a new instance of this class installed by 073 * default. 074 */ 075public class DefaultDrawingSupplier implements DrawingSupplier, Cloneable, 076 PublicCloneable, Serializable { 077 078 /** For serialization. */ 079 private static final long serialVersionUID = -7339847061039422538L; 080 081 /** The default fill paint sequence. */ 082 public static final Paint[] DEFAULT_PAINT_SEQUENCE 083 = ChartColor.createDefaultPaintArray(); 084 085 /** The default outline paint sequence. */ 086 public static final Paint[] DEFAULT_OUTLINE_PAINT_SEQUENCE = new Paint[] { 087 Color.lightGray}; 088 089 /** The default fill paint sequence. */ 090 public static final Paint[] DEFAULT_FILL_PAINT_SEQUENCE = new Paint[] { 091 Color.white}; 092 093 /** The default stroke sequence. */ 094 public static final Stroke[] DEFAULT_STROKE_SEQUENCE = new Stroke[] { 095 new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, 096 BasicStroke.JOIN_BEVEL)}; 097 098 /** The default outline stroke sequence. */ 099 public static final Stroke[] DEFAULT_OUTLINE_STROKE_SEQUENCE 100 = new Stroke[] {new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, 101 BasicStroke.JOIN_BEVEL)}; 102 103 /** The default shape sequence. */ 104 public static final Shape[] DEFAULT_SHAPE_SEQUENCE 105 = createStandardSeriesShapes(); 106 107 /** The paint sequence. */ 108 private transient Paint[] paintSequence; 109 110 /** The current paint index. */ 111 private int paintIndex; 112 113 /** The outline paint sequence. */ 114 private transient Paint[] outlinePaintSequence; 115 116 /** The current outline paint index. */ 117 private int outlinePaintIndex; 118 119 /** The fill paint sequence. */ 120 private transient Paint[] fillPaintSequence; 121 122 /** The current fill paint index. */ 123 private int fillPaintIndex; 124 125 /** The stroke sequence. */ 126 private transient Stroke[] strokeSequence; 127 128 /** The current stroke index. */ 129 private int strokeIndex; 130 131 /** The outline stroke sequence. */ 132 private transient Stroke[] outlineStrokeSequence; 133 134 /** The current outline stroke index. */ 135 private int outlineStrokeIndex; 136 137 /** The shape sequence. */ 138 private transient Shape[] shapeSequence; 139 140 /** The current shape index. */ 141 private int shapeIndex; 142 143 /** 144 * Creates a new supplier, with default sequences for fill paint, outline 145 * paint, stroke and shapes. 146 */ 147 public DefaultDrawingSupplier() { 148 149 this(DEFAULT_PAINT_SEQUENCE, DEFAULT_FILL_PAINT_SEQUENCE, 150 DEFAULT_OUTLINE_PAINT_SEQUENCE, 151 DEFAULT_STROKE_SEQUENCE, 152 DEFAULT_OUTLINE_STROKE_SEQUENCE, 153 DEFAULT_SHAPE_SEQUENCE); 154 155 } 156 157 /** 158 * Creates a new supplier. 159 * 160 * @param paintSequence the fill paint sequence. 161 * @param outlinePaintSequence the outline paint sequence. 162 * @param strokeSequence the stroke sequence. 163 * @param outlineStrokeSequence the outline stroke sequence. 164 * @param shapeSequence the shape sequence. 165 */ 166 public DefaultDrawingSupplier(Paint[] paintSequence, 167 Paint[] outlinePaintSequence, 168 Stroke[] strokeSequence, 169 Stroke[] outlineStrokeSequence, 170 Shape[] shapeSequence) { 171 172 this.paintSequence = paintSequence; 173 this.fillPaintSequence = DEFAULT_FILL_PAINT_SEQUENCE; 174 this.outlinePaintSequence = outlinePaintSequence; 175 this.strokeSequence = strokeSequence; 176 this.outlineStrokeSequence = outlineStrokeSequence; 177 this.shapeSequence = shapeSequence; 178 179 } 180 181 /** 182 * Creates a new supplier. 183 * 184 * @param paintSequence the paint sequence. 185 * @param fillPaintSequence the fill paint sequence. 186 * @param outlinePaintSequence the outline paint sequence. 187 * @param strokeSequence the stroke sequence. 188 * @param outlineStrokeSequence the outline stroke sequence. 189 * @param shapeSequence the shape sequence. 190 * 191 * @since 1.0.6 192 */ 193 public DefaultDrawingSupplier(Paint[] paintSequence, 194 Paint[] fillPaintSequence, Paint[] outlinePaintSequence, 195 Stroke[] strokeSequence, Stroke[] outlineStrokeSequence, 196 Shape[] shapeSequence) { 197 198 this.paintSequence = paintSequence; 199 this.fillPaintSequence = fillPaintSequence; 200 this.outlinePaintSequence = outlinePaintSequence; 201 this.strokeSequence = strokeSequence; 202 this.outlineStrokeSequence = outlineStrokeSequence; 203 this.shapeSequence = shapeSequence; 204 } 205 206 /** 207 * Returns the next paint in the sequence. 208 * 209 * @return The paint. 210 */ 211 @Override 212 public Paint getNextPaint() { 213 Paint result 214 = this.paintSequence[this.paintIndex % this.paintSequence.length]; 215 this.paintIndex++; 216 return result; 217 } 218 219 /** 220 * Returns the next outline paint in the sequence. 221 * 222 * @return The paint. 223 */ 224 @Override 225 public Paint getNextOutlinePaint() { 226 Paint result = this.outlinePaintSequence[ 227 this.outlinePaintIndex % this.outlinePaintSequence.length]; 228 this.outlinePaintIndex++; 229 return result; 230 } 231 232 /** 233 * Returns the next fill paint in the sequence. 234 * 235 * @return The paint. 236 * 237 * @since 1.0.6 238 */ 239 @Override 240 public Paint getNextFillPaint() { 241 Paint result = this.fillPaintSequence[this.fillPaintIndex 242 % this.fillPaintSequence.length]; 243 this.fillPaintIndex++; 244 return result; 245 } 246 247 /** 248 * Returns the next stroke in the sequence. 249 * 250 * @return The stroke. 251 */ 252 @Override 253 public Stroke getNextStroke() { 254 Stroke result = this.strokeSequence[ 255 this.strokeIndex % this.strokeSequence.length]; 256 this.strokeIndex++; 257 return result; 258 } 259 260 /** 261 * Returns the next outline stroke in the sequence. 262 * 263 * @return The stroke. 264 */ 265 @Override 266 public Stroke getNextOutlineStroke() { 267 Stroke result = this.outlineStrokeSequence[ 268 this.outlineStrokeIndex % this.outlineStrokeSequence.length]; 269 this.outlineStrokeIndex++; 270 return result; 271 } 272 273 /** 274 * Returns the next shape in the sequence. 275 * 276 * @return The shape. 277 */ 278 @Override 279 public Shape getNextShape() { 280 Shape result = this.shapeSequence[ 281 this.shapeIndex % this.shapeSequence.length]; 282 this.shapeIndex++; 283 return result; 284 } 285 286 /** 287 * Creates an array of standard shapes to display for the items in series 288 * on charts. 289 * 290 * @return The array of shapes. 291 */ 292 public static Shape[] createStandardSeriesShapes() { 293 294 Shape[] result = new Shape[10]; 295 296 double size = 6.0; 297 double delta = size / 2.0; 298 int[] xpoints; 299 int[] ypoints; 300 301 // square 302 result[0] = new Rectangle2D.Double(-delta, -delta, size, size); 303 // circle 304 result[1] = new Ellipse2D.Double(-delta, -delta, size, size); 305 306 // up-pointing triangle 307 xpoints = intArray(0.0, delta, -delta); 308 ypoints = intArray(-delta, delta, delta); 309 result[2] = new Polygon(xpoints, ypoints, 3); 310 311 // diamond 312 xpoints = intArray(0.0, delta, 0.0, -delta); 313 ypoints = intArray(-delta, 0.0, delta, 0.0); 314 result[3] = new Polygon(xpoints, ypoints, 4); 315 316 // horizontal rectangle 317 result[4] = new Rectangle2D.Double(-delta, -delta / 2, size, size / 2); 318 319 // down-pointing triangle 320 xpoints = intArray(-delta, +delta, 0.0); 321 ypoints = intArray(-delta, -delta, delta); 322 result[5] = new Polygon(xpoints, ypoints, 3); 323 324 // horizontal ellipse 325 result[6] = new Ellipse2D.Double(-delta, -delta / 2, size, size / 2); 326 327 // right-pointing triangle 328 xpoints = intArray(-delta, delta, -delta); 329 ypoints = intArray(-delta, 0.0, delta); 330 result[7] = new Polygon(xpoints, ypoints, 3); 331 332 // vertical rectangle 333 result[8] = new Rectangle2D.Double(-delta / 2, -delta, size / 2, size); 334 335 // left-pointing triangle 336 xpoints = intArray(-delta, delta, delta); 337 ypoints = intArray(0.0, -delta, +delta); 338 result[9] = new Polygon(xpoints, ypoints, 3); 339 340 return result; 341 342 } 343 344 /** 345 * Tests this object for equality with another object. 346 * 347 * @param obj the object (<code>null</code> permitted). 348 * 349 * @return A boolean. 350 */ 351 @Override 352 public boolean equals(Object obj) { 353 if (obj == this) { 354 return true; 355 } 356 if (!(obj instanceof DefaultDrawingSupplier)) { 357 return false; 358 } 359 DefaultDrawingSupplier that = (DefaultDrawingSupplier) obj; 360 if (!Arrays.equals(this.paintSequence, that.paintSequence)) { 361 return false; 362 } 363 if (this.paintIndex != that.paintIndex) { 364 return false; 365 } 366 if (!Arrays.equals(this.outlinePaintSequence, 367 that.outlinePaintSequence)) { 368 return false; 369 } 370 if (this.outlinePaintIndex != that.outlinePaintIndex) { 371 return false; 372 } 373 if (!Arrays.equals(this.strokeSequence, that.strokeSequence)) { 374 return false; 375 } 376 if (this.strokeIndex != that.strokeIndex) { 377 return false; 378 } 379 if (!Arrays.equals(this.outlineStrokeSequence, 380 that.outlineStrokeSequence)) { 381 return false; 382 } 383 if (this.outlineStrokeIndex != that.outlineStrokeIndex) { 384 return false; 385 } 386 if (!equalShapes(this.shapeSequence, that.shapeSequence)) { 387 return false; 388 } 389 if (this.shapeIndex != that.shapeIndex) { 390 return false; 391 } 392 return true; 393 } 394 395 /** 396 * A utility method for testing the equality of two arrays of shapes. 397 * 398 * @param s1 the first array (<code>null</code> permitted). 399 * @param s2 the second array (<code>null</code> permitted). 400 * 401 * @return A boolean. 402 */ 403 private boolean equalShapes(Shape[] s1, Shape[] s2) { 404 if (s1 == null) { 405 return s2 == null; 406 } 407 if (s2 == null) { 408 return false; 409 } 410 if (s1.length != s2.length) { 411 return false; 412 } 413 for (int i = 0; i < s1.length; i++) { 414 if (!ShapeUtilities.equal(s1[i], s2[i])) { 415 return false; 416 } 417 } 418 return true; 419 } 420 421 /** 422 * Handles serialization. 423 * 424 * @param stream the output stream. 425 * 426 * @throws IOException if there is an I/O problem. 427 */ 428 private void writeObject(ObjectOutputStream stream) throws IOException { 429 stream.defaultWriteObject(); 430 431 int paintCount = this.paintSequence.length; 432 stream.writeInt(paintCount); 433 for (int i = 0; i < paintCount; i++) { 434 SerialUtilities.writePaint(this.paintSequence[i], stream); 435 } 436 437 int outlinePaintCount = this.outlinePaintSequence.length; 438 stream.writeInt(outlinePaintCount); 439 for (int i = 0; i < outlinePaintCount; i++) { 440 SerialUtilities.writePaint(this.outlinePaintSequence[i], stream); 441 } 442 443 int strokeCount = this.strokeSequence.length; 444 stream.writeInt(strokeCount); 445 for (int i = 0; i < strokeCount; i++) { 446 SerialUtilities.writeStroke(this.strokeSequence[i], stream); 447 } 448 449 int outlineStrokeCount = this.outlineStrokeSequence.length; 450 stream.writeInt(outlineStrokeCount); 451 for (int i = 0; i < outlineStrokeCount; i++) { 452 SerialUtilities.writeStroke(this.outlineStrokeSequence[i], stream); 453 } 454 455 int shapeCount = this.shapeSequence.length; 456 stream.writeInt(shapeCount); 457 for (int i = 0; i < shapeCount; i++) { 458 SerialUtilities.writeShape(this.shapeSequence[i], stream); 459 } 460 461 } 462 463 /** 464 * Restores a serialized object. 465 * 466 * @param stream the input stream. 467 * 468 * @throws IOException if there is an I/O problem. 469 * @throws ClassNotFoundException if there is a problem loading a class. 470 */ 471 private void readObject(ObjectInputStream stream) 472 throws IOException, ClassNotFoundException { 473 stream.defaultReadObject(); 474 475 int paintCount = stream.readInt(); 476 this.paintSequence = new Paint[paintCount]; 477 for (int i = 0; i < paintCount; i++) { 478 this.paintSequence[i] = SerialUtilities.readPaint(stream); 479 } 480 481 int outlinePaintCount = stream.readInt(); 482 this.outlinePaintSequence = new Paint[outlinePaintCount]; 483 for (int i = 0; i < outlinePaintCount; i++) { 484 this.outlinePaintSequence[i] = SerialUtilities.readPaint(stream); 485 } 486 487 int strokeCount = stream.readInt(); 488 this.strokeSequence = new Stroke[strokeCount]; 489 for (int i = 0; i < strokeCount; i++) { 490 this.strokeSequence[i] = SerialUtilities.readStroke(stream); 491 } 492 493 int outlineStrokeCount = stream.readInt(); 494 this.outlineStrokeSequence = new Stroke[outlineStrokeCount]; 495 for (int i = 0; i < outlineStrokeCount; i++) { 496 this.outlineStrokeSequence[i] = SerialUtilities.readStroke(stream); 497 } 498 499 int shapeCount = stream.readInt(); 500 this.shapeSequence = new Shape[shapeCount]; 501 for (int i = 0; i < shapeCount; i++) { 502 this.shapeSequence[i] = SerialUtilities.readShape(stream); 503 } 504 505 } 506 507 /** 508 * Helper method to avoid lots of explicit casts in getShape(). Returns 509 * an array containing the provided doubles cast to ints. 510 * 511 * @param a x 512 * @param b y 513 * @param c z 514 * 515 * @return int[3] with converted params. 516 */ 517 private static int[] intArray(double a, double b, double c) { 518 return new int[] {(int) a, (int) b, (int) c}; 519 } 520 521 /** 522 * Helper method to avoid lots of explicit casts in getShape(). Returns 523 * an array containing the provided doubles cast to ints. 524 * 525 * @param a x 526 * @param b y 527 * @param c z 528 * @param d t 529 * 530 * @return int[4] with converted params. 531 */ 532 private static int[] intArray(double a, double b, double c, double d) { 533 return new int[] {(int) a, (int) b, (int) c, (int) d}; 534 } 535 536 /** 537 * Returns a clone. 538 * 539 * @return A clone. 540 * 541 * @throws CloneNotSupportedException if a component of the supplier does 542 * not support cloning. 543 */ 544 @Override 545 public Object clone() throws CloneNotSupportedException { 546 DefaultDrawingSupplier clone = (DefaultDrawingSupplier) super.clone(); 547 return clone; 548 } 549}