001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * --------------------------- 028 * DefaultDrawingSupplier.java 029 * --------------------------- 030 * (C) Copyright 2003-2007, 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 051 import java.awt.BasicStroke; 052 import java.awt.Color; 053 import java.awt.Paint; 054 import java.awt.Polygon; 055 import java.awt.Shape; 056 import java.awt.Stroke; 057 import java.awt.geom.Ellipse2D; 058 import java.awt.geom.Rectangle2D; 059 import java.io.IOException; 060 import java.io.ObjectInputStream; 061 import java.io.ObjectOutputStream; 062 import java.io.Serializable; 063 import java.util.Arrays; 064 065 import org.jfree.chart.ChartColor; 066 import org.jfree.io.SerialUtilities; 067 import org.jfree.util.PublicCloneable; 068 import 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 */ 075 public 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 public Paint getNextPaint() { 212 Paint result 213 = this.paintSequence[this.paintIndex % this.paintSequence.length]; 214 this.paintIndex++; 215 return result; 216 } 217 218 /** 219 * Returns the next outline paint in the sequence. 220 * 221 * @return The paint. 222 */ 223 public Paint getNextOutlinePaint() { 224 Paint result = this.outlinePaintSequence[ 225 this.outlinePaintIndex % this.outlinePaintSequence.length]; 226 this.outlinePaintIndex++; 227 return result; 228 } 229 230 /** 231 * Returns the next fill paint in the sequence. 232 * 233 * @return The paint. 234 * 235 * @since 1.0.6 236 */ 237 public Paint getNextFillPaint() { 238 Paint result = this.fillPaintSequence[this.fillPaintIndex 239 % this.fillPaintSequence.length]; 240 this.fillPaintIndex++; 241 return result; 242 } 243 244 /** 245 * Returns the next stroke in the sequence. 246 * 247 * @return The stroke. 248 */ 249 public Stroke getNextStroke() { 250 Stroke result = this.strokeSequence[ 251 this.strokeIndex % this.strokeSequence.length]; 252 this.strokeIndex++; 253 return result; 254 } 255 256 /** 257 * Returns the next outline stroke in the sequence. 258 * 259 * @return The stroke. 260 */ 261 public Stroke getNextOutlineStroke() { 262 Stroke result = this.outlineStrokeSequence[ 263 this.outlineStrokeIndex % this.outlineStrokeSequence.length]; 264 this.outlineStrokeIndex++; 265 return result; 266 } 267 268 /** 269 * Returns the next shape in the sequence. 270 * 271 * @return The shape. 272 */ 273 public Shape getNextShape() { 274 Shape result = this.shapeSequence[ 275 this.shapeIndex % this.shapeSequence.length]; 276 this.shapeIndex++; 277 return result; 278 } 279 280 /** 281 * Creates an array of standard shapes to display for the items in series 282 * on charts. 283 * 284 * @return The array of shapes. 285 */ 286 public static Shape[] createStandardSeriesShapes() { 287 288 Shape[] result = new Shape[10]; 289 290 double size = 6.0; 291 double delta = size / 2.0; 292 int[] xpoints = null; 293 int[] ypoints = null; 294 295 // square 296 result[0] = new Rectangle2D.Double(-delta, -delta, size, size); 297 // circle 298 result[1] = new Ellipse2D.Double(-delta, -delta, size, size); 299 300 // up-pointing triangle 301 xpoints = intArray(0.0, delta, -delta); 302 ypoints = intArray(-delta, delta, delta); 303 result[2] = new Polygon(xpoints, ypoints, 3); 304 305 // diamond 306 xpoints = intArray(0.0, delta, 0.0, -delta); 307 ypoints = intArray(-delta, 0.0, delta, 0.0); 308 result[3] = new Polygon(xpoints, ypoints, 4); 309 310 // horizontal rectangle 311 result[4] = new Rectangle2D.Double(-delta, -delta / 2, size, size / 2); 312 313 // down-pointing triangle 314 xpoints = intArray(-delta, +delta, 0.0); 315 ypoints = intArray(-delta, -delta, delta); 316 result[5] = new Polygon(xpoints, ypoints, 3); 317 318 // horizontal ellipse 319 result[6] = new Ellipse2D.Double(-delta, -delta / 2, size, size / 2); 320 321 // right-pointing triangle 322 xpoints = intArray(-delta, delta, -delta); 323 ypoints = intArray(-delta, 0.0, delta); 324 result[7] = new Polygon(xpoints, ypoints, 3); 325 326 // vertical rectangle 327 result[8] = new Rectangle2D.Double(-delta / 2, -delta, size / 2, size); 328 329 // left-pointing triangle 330 xpoints = intArray(-delta, delta, delta); 331 ypoints = intArray(0.0, -delta, +delta); 332 result[9] = new Polygon(xpoints, ypoints, 3); 333 334 return result; 335 336 } 337 338 /** 339 * Tests this object for equality with another object. 340 * 341 * @param obj the object (<code>null</code> permitted). 342 * 343 * @return A boolean. 344 */ 345 public boolean equals(Object obj) { 346 347 if (obj == this) { 348 return true; 349 } 350 351 if (!(obj instanceof DefaultDrawingSupplier)) { 352 return false; 353 } 354 355 DefaultDrawingSupplier that = (DefaultDrawingSupplier) obj; 356 357 if (!Arrays.equals(this.paintSequence, that.paintSequence)) { 358 return false; 359 } 360 if (this.paintIndex != that.paintIndex) { 361 return false; 362 } 363 if (!Arrays.equals(this.outlinePaintSequence, 364 that.outlinePaintSequence)) { 365 return false; 366 } 367 if (this.outlinePaintIndex != that.outlinePaintIndex) { 368 return false; 369 } 370 if (!Arrays.equals(this.strokeSequence, that.strokeSequence)) { 371 return false; 372 } 373 if (this.strokeIndex != that.strokeIndex) { 374 return false; 375 } 376 if (!Arrays.equals(this.outlineStrokeSequence, 377 that.outlineStrokeSequence)) { 378 return false; 379 } 380 if (this.outlineStrokeIndex != that.outlineStrokeIndex) { 381 return false; 382 } 383 if (!equalShapes(this.shapeSequence, that.shapeSequence)) { 384 return false; 385 } 386 if (this.shapeIndex != that.shapeIndex) { 387 return false; 388 } 389 return true; 390 391 } 392 393 /** 394 * A utility method for testing the equality of two arrays of shapes. 395 * 396 * @param s1 the first array (<code>null</code> permitted). 397 * @param s2 the second array (<code>null</code> permitted). 398 * 399 * @return A boolean. 400 */ 401 private boolean equalShapes(Shape[] s1, Shape[] s2) { 402 if (s1 == null) { 403 return s2 == null; 404 } 405 if (s2 == null) { 406 return false; 407 } 408 if (s1.length != s2.length) { 409 return false; 410 } 411 for (int i = 0; i < s1.length; i++) { 412 if (!ShapeUtilities.equal(s1[i], s2[i])) { 413 return false; 414 } 415 } 416 return true; 417 } 418 419 /** 420 * Handles serialization. 421 * 422 * @param stream the output stream. 423 * 424 * @throws IOException if there is an I/O problem. 425 */ 426 private void writeObject(ObjectOutputStream stream) throws IOException { 427 stream.defaultWriteObject(); 428 429 int paintCount = this.paintSequence.length; 430 stream.writeInt(paintCount); 431 for (int i = 0; i < paintCount; i++) { 432 SerialUtilities.writePaint(this.paintSequence[i], stream); 433 } 434 435 int outlinePaintCount = this.outlinePaintSequence.length; 436 stream.writeInt(outlinePaintCount); 437 for (int i = 0; i < outlinePaintCount; i++) { 438 SerialUtilities.writePaint(this.outlinePaintSequence[i], stream); 439 } 440 441 int strokeCount = this.strokeSequence.length; 442 stream.writeInt(strokeCount); 443 for (int i = 0; i < strokeCount; i++) { 444 SerialUtilities.writeStroke(this.strokeSequence[i], stream); 445 } 446 447 int outlineStrokeCount = this.outlineStrokeSequence.length; 448 stream.writeInt(outlineStrokeCount); 449 for (int i = 0; i < outlineStrokeCount; i++) { 450 SerialUtilities.writeStroke(this.outlineStrokeSequence[i], stream); 451 } 452 453 int shapeCount = this.shapeSequence.length; 454 stream.writeInt(shapeCount); 455 for (int i = 0; i < shapeCount; i++) { 456 SerialUtilities.writeShape(this.shapeSequence[i], stream); 457 } 458 459 } 460 461 /** 462 * Restores a serialized object. 463 * 464 * @param stream the input stream. 465 * 466 * @throws IOException if there is an I/O problem. 467 * @throws ClassNotFoundException if there is a problem loading a class. 468 */ 469 private void readObject(ObjectInputStream stream) 470 throws IOException, ClassNotFoundException { 471 stream.defaultReadObject(); 472 473 int paintCount = stream.readInt(); 474 this.paintSequence = new Paint[paintCount]; 475 for (int i = 0; i < paintCount; i++) { 476 this.paintSequence[i] = SerialUtilities.readPaint(stream); 477 } 478 479 int outlinePaintCount = stream.readInt(); 480 this.outlinePaintSequence = new Paint[outlinePaintCount]; 481 for (int i = 0; i < outlinePaintCount; i++) { 482 this.outlinePaintSequence[i] = SerialUtilities.readPaint(stream); 483 } 484 485 int strokeCount = stream.readInt(); 486 this.strokeSequence = new Stroke[strokeCount]; 487 for (int i = 0; i < strokeCount; i++) { 488 this.strokeSequence[i] = SerialUtilities.readStroke(stream); 489 } 490 491 int outlineStrokeCount = stream.readInt(); 492 this.outlineStrokeSequence = new Stroke[outlineStrokeCount]; 493 for (int i = 0; i < outlineStrokeCount; i++) { 494 this.outlineStrokeSequence[i] = SerialUtilities.readStroke(stream); 495 } 496 497 int shapeCount = stream.readInt(); 498 this.shapeSequence = new Shape[shapeCount]; 499 for (int i = 0; i < shapeCount; i++) { 500 this.shapeSequence[i] = SerialUtilities.readShape(stream); 501 } 502 503 } 504 505 /** 506 * Helper method to avoid lots of explicit casts in getShape(). Returns 507 * an array containing the provided doubles cast to ints. 508 * 509 * @param a x 510 * @param b y 511 * @param c z 512 * 513 * @return int[3] with converted params. 514 */ 515 private static int[] intArray(double a, double b, double c) { 516 return new int[] {(int) a, (int) b, (int) c}; 517 } 518 519 /** 520 * Helper method to avoid lots of explicit casts in getShape(). Returns 521 * an array containing the provided doubles cast to ints. 522 * 523 * @param a x 524 * @param b y 525 * @param c z 526 * @param d t 527 * 528 * @return int[4] with converted params. 529 */ 530 private static int[] intArray(double a, double b, double c, double d) { 531 return new int[] {(int) a, (int) b, (int) c, (int) d}; 532 } 533 534 /** 535 * Returns a clone. 536 * 537 * @return A clone. 538 * 539 * @throws CloneNotSupportedException if a component of the supplier does 540 * not support cloning. 541 */ 542 public Object clone() throws CloneNotSupportedException { 543 DefaultDrawingSupplier clone = (DefaultDrawingSupplier) super.clone(); 544 return clone; 545 } 546 }