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