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