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