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