001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2008, 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-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
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 }