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 }