001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2013, 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 * [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-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
051import java.awt.BasicStroke;
052import java.awt.Color;
053import java.awt.Paint;
054import java.awt.Polygon;
055import java.awt.Shape;
056import java.awt.Stroke;
057import java.awt.geom.Ellipse2D;
058import java.awt.geom.Rectangle2D;
059import java.io.IOException;
060import java.io.ObjectInputStream;
061import java.io.ObjectOutputStream;
062import java.io.Serializable;
063import java.util.Arrays;
064
065import org.jfree.chart.ChartColor;
066import org.jfree.io.SerialUtilities;
067import org.jfree.util.PublicCloneable;
068import 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 */
075public 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    @Override
212    public Paint getNextPaint() {
213        Paint result
214            = this.paintSequence[this.paintIndex % this.paintSequence.length];
215        this.paintIndex++;
216        return result;
217    }
218
219    /**
220     * Returns the next outline paint in the sequence.
221     *
222     * @return The paint.
223     */
224    @Override
225    public Paint getNextOutlinePaint() {
226        Paint result = this.outlinePaintSequence[
227                this.outlinePaintIndex % this.outlinePaintSequence.length];
228        this.outlinePaintIndex++;
229        return result;
230    }
231
232    /**
233     * Returns the next fill paint in the sequence.
234     *
235     * @return The paint.
236     *
237     * @since 1.0.6
238     */
239    @Override
240    public Paint getNextFillPaint() {
241        Paint result = this.fillPaintSequence[this.fillPaintIndex
242                % this.fillPaintSequence.length];
243        this.fillPaintIndex++;
244        return result;
245    }
246
247    /**
248     * Returns the next stroke in the sequence.
249     *
250     * @return The stroke.
251     */
252    @Override
253    public Stroke getNextStroke() {
254        Stroke result = this.strokeSequence[
255                this.strokeIndex % this.strokeSequence.length];
256        this.strokeIndex++;
257        return result;
258    }
259
260    /**
261     * Returns the next outline stroke in the sequence.
262     *
263     * @return The stroke.
264     */
265    @Override
266    public Stroke getNextOutlineStroke() {
267        Stroke result = this.outlineStrokeSequence[
268                this.outlineStrokeIndex % this.outlineStrokeSequence.length];
269        this.outlineStrokeIndex++;
270        return result;
271    }
272
273    /**
274     * Returns the next shape in the sequence.
275     *
276     * @return The shape.
277     */
278    @Override
279    public Shape getNextShape() {
280        Shape result = this.shapeSequence[
281                this.shapeIndex % this.shapeSequence.length];
282        this.shapeIndex++;
283        return result;
284    }
285
286    /**
287     * Creates an array of standard shapes to display for the items in series
288     * on charts.
289     *
290     * @return The array of shapes.
291     */
292    public static Shape[] createStandardSeriesShapes() {
293
294        Shape[] result = new Shape[10];
295
296        double size = 6.0;
297        double delta = size / 2.0;
298        int[] xpoints;
299        int[] ypoints;
300
301        // square
302        result[0] = new Rectangle2D.Double(-delta, -delta, size, size);
303        // circle
304        result[1] = new Ellipse2D.Double(-delta, -delta, size, size);
305
306        // up-pointing triangle
307        xpoints = intArray(0.0, delta, -delta);
308        ypoints = intArray(-delta, delta, delta);
309        result[2] = new Polygon(xpoints, ypoints, 3);
310
311        // diamond
312        xpoints = intArray(0.0, delta, 0.0, -delta);
313        ypoints = intArray(-delta, 0.0, delta, 0.0);
314        result[3] = new Polygon(xpoints, ypoints, 4);
315
316        // horizontal rectangle
317        result[4] = new Rectangle2D.Double(-delta, -delta / 2, size, size / 2);
318
319        // down-pointing triangle
320        xpoints = intArray(-delta, +delta, 0.0);
321        ypoints = intArray(-delta, -delta, delta);
322        result[5] = new Polygon(xpoints, ypoints, 3);
323
324        // horizontal ellipse
325        result[6] = new Ellipse2D.Double(-delta, -delta / 2, size, size / 2);
326
327        // right-pointing triangle
328        xpoints = intArray(-delta, delta, -delta);
329        ypoints = intArray(-delta, 0.0, delta);
330        result[7] = new Polygon(xpoints, ypoints, 3);
331
332        // vertical rectangle
333        result[8] = new Rectangle2D.Double(-delta / 2, -delta, size / 2, size);
334
335        // left-pointing triangle
336        xpoints = intArray(-delta, delta, delta);
337        ypoints = intArray(0.0, -delta, +delta);
338        result[9] = new Polygon(xpoints, ypoints, 3);
339
340        return result;
341
342    }
343
344    /**
345     * Tests this object for equality with another object.
346     *
347     * @param obj  the object (<code>null</code> permitted).
348     *
349     * @return A boolean.
350     */
351    @Override
352    public boolean equals(Object obj) {
353        if (obj == this) {
354            return true;
355        }
356        if (!(obj instanceof DefaultDrawingSupplier)) {
357            return false;
358        }
359        DefaultDrawingSupplier that = (DefaultDrawingSupplier) obj;
360        if (!Arrays.equals(this.paintSequence, that.paintSequence)) {
361            return false;
362        }
363        if (this.paintIndex != that.paintIndex) {
364            return false;
365        }
366        if (!Arrays.equals(this.outlinePaintSequence,
367                that.outlinePaintSequence)) {
368            return false;
369        }
370        if (this.outlinePaintIndex != that.outlinePaintIndex) {
371            return false;
372        }
373        if (!Arrays.equals(this.strokeSequence, that.strokeSequence)) {
374            return false;
375        }
376        if (this.strokeIndex != that.strokeIndex) {
377            return false;
378        }
379        if (!Arrays.equals(this.outlineStrokeSequence,
380                that.outlineStrokeSequence)) {
381            return false;
382        }
383        if (this.outlineStrokeIndex != that.outlineStrokeIndex) {
384            return false;
385        }
386        if (!equalShapes(this.shapeSequence, that.shapeSequence)) {
387            return false;
388        }
389        if (this.shapeIndex != that.shapeIndex) {
390            return false;
391        }
392        return true;
393    }
394
395    /**
396     * A utility method for testing the equality of two arrays of shapes.
397     *
398     * @param s1  the first array (<code>null</code> permitted).
399     * @param s2  the second array (<code>null</code> permitted).
400     *
401     * @return A boolean.
402     */
403    private boolean equalShapes(Shape[] s1, Shape[] s2) {
404        if (s1 == null) {
405            return s2 == null;
406        }
407        if (s2 == null) {
408            return false;
409        }
410        if (s1.length != s2.length) {
411            return false;
412        }
413        for (int i = 0; i < s1.length; i++) {
414            if (!ShapeUtilities.equal(s1[i], s2[i])) {
415                return false;
416            }
417        }
418        return true;
419    }
420
421    /**
422     * Handles serialization.
423     *
424     * @param stream  the output stream.
425     *
426     * @throws IOException if there is an I/O problem.
427     */
428    private void writeObject(ObjectOutputStream stream) throws IOException {
429        stream.defaultWriteObject();
430
431        int paintCount = this.paintSequence.length;
432        stream.writeInt(paintCount);
433        for (int i = 0; i < paintCount; i++) {
434            SerialUtilities.writePaint(this.paintSequence[i], stream);
435        }
436
437        int outlinePaintCount = this.outlinePaintSequence.length;
438        stream.writeInt(outlinePaintCount);
439        for (int i = 0; i < outlinePaintCount; i++) {
440            SerialUtilities.writePaint(this.outlinePaintSequence[i], stream);
441        }
442
443        int strokeCount = this.strokeSequence.length;
444        stream.writeInt(strokeCount);
445        for (int i = 0; i < strokeCount; i++) {
446            SerialUtilities.writeStroke(this.strokeSequence[i], stream);
447        }
448
449        int outlineStrokeCount = this.outlineStrokeSequence.length;
450        stream.writeInt(outlineStrokeCount);
451        for (int i = 0; i < outlineStrokeCount; i++) {
452            SerialUtilities.writeStroke(this.outlineStrokeSequence[i], stream);
453        }
454
455        int shapeCount = this.shapeSequence.length;
456        stream.writeInt(shapeCount);
457        for (int i = 0; i < shapeCount; i++) {
458            SerialUtilities.writeShape(this.shapeSequence[i], stream);
459        }
460
461    }
462
463    /**
464     * Restores a serialized object.
465     *
466     * @param stream  the input stream.
467     *
468     * @throws IOException if there is an I/O problem.
469     * @throws ClassNotFoundException if there is a problem loading a class.
470     */
471    private void readObject(ObjectInputStream stream)
472        throws IOException, ClassNotFoundException {
473        stream.defaultReadObject();
474
475        int paintCount = stream.readInt();
476        this.paintSequence = new Paint[paintCount];
477        for (int i = 0; i < paintCount; i++) {
478            this.paintSequence[i] = SerialUtilities.readPaint(stream);
479        }
480
481        int outlinePaintCount = stream.readInt();
482        this.outlinePaintSequence = new Paint[outlinePaintCount];
483        for (int i = 0; i < outlinePaintCount; i++) {
484            this.outlinePaintSequence[i] = SerialUtilities.readPaint(stream);
485        }
486
487        int strokeCount = stream.readInt();
488        this.strokeSequence = new Stroke[strokeCount];
489        for (int i = 0; i < strokeCount; i++) {
490            this.strokeSequence[i] = SerialUtilities.readStroke(stream);
491        }
492
493        int outlineStrokeCount = stream.readInt();
494        this.outlineStrokeSequence = new Stroke[outlineStrokeCount];
495        for (int i = 0; i < outlineStrokeCount; i++) {
496            this.outlineStrokeSequence[i] = SerialUtilities.readStroke(stream);
497        }
498
499        int shapeCount = stream.readInt();
500        this.shapeSequence = new Shape[shapeCount];
501        for (int i = 0; i < shapeCount; i++) {
502            this.shapeSequence[i] = SerialUtilities.readShape(stream);
503        }
504
505    }
506
507    /**
508     * Helper method to avoid lots of explicit casts in getShape().  Returns
509     * an array containing the provided doubles cast to ints.
510     *
511     * @param a  x
512     * @param b  y
513     * @param c  z
514     *
515     * @return int[3] with converted params.
516     */
517    private static int[] intArray(double a, double b, double c) {
518        return new int[] {(int) a, (int) b, (int) c};
519    }
520
521    /**
522     * Helper method to avoid lots of explicit casts in getShape().  Returns
523     * an array containing the provided doubles cast to ints.
524     *
525     * @param a  x
526     * @param b  y
527     * @param c  z
528     * @param d  t
529     *
530     * @return int[4] with converted params.
531     */
532    private static int[] intArray(double a, double b, double c, double d) {
533        return new int[] {(int) a, (int) b, (int) c, (int) d};
534    }
535
536    /**
537     * Returns a clone.
538     *
539     * @return A clone.
540     *
541     * @throws CloneNotSupportedException if a component of the supplier does
542     *                                    not support cloning.
543     */
544    @Override
545    public Object clone() throws CloneNotSupportedException {
546        DefaultDrawingSupplier clone = (DefaultDrawingSupplier) super.clone();
547        return clone;
548    }
549}