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 * MeterNeedle.java
029 * ----------------
030 * (C) Copyright 2002-2021, by the Australian Antarctic Division and
031 *                          Contributors.
032 *
033 * Original Author:  Bryan Scott (for the Australian Antarctic Division);
034 * Contributor(s):   David Gilbert (for Object Refinery Limited);
035 *                   Nicolas Brodu (for Astrium and EADS Corporate Research
036 *                   Center);
037 */
038
039package org.jfree.chart.needle;
040
041import java.awt.BasicStroke;
042import java.awt.Color;
043import java.awt.Graphics2D;
044import java.awt.Paint;
045import java.awt.Shape;
046import java.awt.Stroke;
047import java.awt.geom.AffineTransform;
048import java.awt.geom.Point2D;
049import java.awt.geom.Rectangle2D;
050import java.io.IOException;
051import java.io.ObjectInputStream;
052import java.io.ObjectOutputStream;
053import java.io.Serializable;
054import java.util.Objects;
055
056import org.jfree.chart.HashUtils;
057import org.jfree.chart.util.PaintUtils;
058import org.jfree.chart.util.SerialUtils;
059
060/**
061 * The base class used to represent the needle on a
062 * {@link org.jfree.chart.plot.CompassPlot}.
063 */
064public abstract class MeterNeedle implements Serializable {
065
066    /** For serialization. */
067    private static final long serialVersionUID = 5203064851510951052L;
068
069    /** The outline paint. */
070    private transient Paint outlinePaint = Color.BLACK;
071
072    /** The outline stroke. */
073    private transient Stroke outlineStroke = new BasicStroke(2);
074
075    /** The fill paint. */
076    private transient Paint fillPaint = null;
077
078    /** The highlight paint. */
079    private transient Paint highlightPaint = null;
080
081    /** The size. */
082    private int size = 5;
083
084    /** Scalar to apply to locate the rotation x point. */
085    private double rotateX = 0.5;
086
087    /** Scalar to apply to locate the rotation y point. */
088    private double rotateY = 0.5;
089
090    /** A transform. */
091    protected static AffineTransform transform = new AffineTransform();
092
093    /**
094     * Creates a new needle.
095     */
096    public MeterNeedle() {
097        this(null, null, null);
098    }
099
100    /**
101     * Creates a new needle.
102     *
103     * @param outline  the outline paint ({@code null} permitted).
104     * @param fill  the fill paint ({@code null} permitted).
105     * @param highlight  the highlight paint ({@code null} permitted).
106     */
107    public MeterNeedle(Paint outline, Paint fill, Paint highlight) {
108        this.fillPaint = fill;
109        this.highlightPaint = highlight;
110        this.outlinePaint = outline;
111    }
112
113    /**
114     * Returns the outline paint.
115     *
116     * @return The outline paint.
117     */
118    public Paint getOutlinePaint() {
119        return this.outlinePaint;
120    }
121
122    /**
123     * Sets the outline paint.
124     *
125     * @param p  the new paint.
126     */
127    public void setOutlinePaint(Paint p) {
128        if (p != null) {
129            this.outlinePaint = p;
130        }
131    }
132
133    /**
134     * Returns the outline stroke.
135     *
136     * @return The outline stroke.
137     */
138    public Stroke getOutlineStroke() {
139        return this.outlineStroke;
140    }
141
142    /**
143     * Sets the outline stroke.
144     *
145     * @param s  the new stroke.
146     */
147    public void setOutlineStroke(Stroke s) {
148        if (s != null) {
149            this.outlineStroke = s;
150        }
151    }
152
153    /**
154     * Returns the fill paint.
155     *
156     * @return The fill paint.
157     */
158    public Paint getFillPaint() {
159        return this.fillPaint;
160    }
161
162    /**
163     * Sets the fill paint.
164     *
165     * @param p  the fill paint.
166     */
167    public void setFillPaint(Paint p) {
168        if (p != null) {
169            this.fillPaint = p;
170        }
171    }
172
173    /**
174     * Returns the highlight paint.
175     *
176     * @return The highlight paint.
177     */
178    public Paint getHighlightPaint() {
179        return this.highlightPaint;
180    }
181
182    /**
183     * Sets the highlight paint.
184     *
185     * @param p  the highlight paint.
186     */
187    public void setHighlightPaint(Paint p) {
188        if (p != null) {
189            this.highlightPaint = p;
190        }
191    }
192
193    /**
194     * Returns the scalar used for determining the rotation x value.
195     *
196     * @return The x rotate scalar.
197     */
198    public double getRotateX() {
199        return this.rotateX;
200    }
201
202    /**
203     * Sets the rotateX value.
204     *
205     * @param x  the new value.
206     */
207    public void setRotateX(double x) {
208        this.rotateX = x;
209    }
210
211    /**
212     * Sets the rotateY value.
213     *
214     * @param y  the new value.
215     */
216    public void setRotateY(double y) {
217        this.rotateY = y;
218    }
219
220    /**
221     * Returns the scalar used for determining the rotation y value.
222     *
223     * @return The y rotate scalar.
224     */
225    public double getRotateY() {
226        return this.rotateY;
227    }
228
229    /**
230     * Draws the needle.
231     *
232     * @param g2  the graphics device.
233     * @param plotArea  the plot area.
234     */
235    public void draw(Graphics2D g2, Rectangle2D plotArea) {
236        draw(g2, plotArea, 0);
237    }
238
239    /**
240     * Draws the needle.
241     *
242     * @param g2  the graphics device.
243     * @param plotArea  the plot area.
244     * @param angle  the angle.
245     */
246    public void draw(Graphics2D g2, Rectangle2D plotArea, double angle) {
247        Point2D.Double pt = new Point2D.Double();
248        pt.setLocation(plotArea.getMinX() + this.rotateX * plotArea.getWidth(),
249                plotArea.getMinY() + this.rotateY * plotArea.getHeight());
250        draw(g2, plotArea, pt, angle);
251    }
252
253    /**
254     * Draws the needle.
255     *
256     * @param g2  the graphics device.
257     * @param plotArea  the plot area.
258     * @param rotate  the rotation point.
259     * @param angle  the angle.
260     */
261    public void draw(Graphics2D g2, Rectangle2D plotArea, Point2D rotate,
262            double angle) {
263
264        Paint savePaint = g2.getColor();
265        Stroke saveStroke = g2.getStroke();
266
267        drawNeedle(g2, plotArea, rotate, Math.toRadians(angle));
268
269        g2.setStroke(saveStroke);
270        g2.setPaint(savePaint);
271    }
272
273    /**
274     * Draws the needle.
275     *
276     * @param g2  the graphics device.
277     * @param plotArea  the plot area.
278     * @param rotate  the rotation point.
279     * @param angle  the angle.
280     */
281    protected abstract void drawNeedle(Graphics2D g2, Rectangle2D plotArea, 
282            Point2D rotate, double angle);
283
284    /**
285     * Displays a shape.
286     *
287     * @param g2  the graphics device.
288     * @param shape  the shape.
289     */
290    protected void defaultDisplay(Graphics2D g2, Shape shape) {
291        if (this.fillPaint != null) {
292            g2.setPaint(this.fillPaint);
293            g2.fill(shape);
294        }
295
296        if (this.outlinePaint != null) {
297            g2.setStroke(this.outlineStroke);
298            g2.setPaint(this.outlinePaint);
299            g2.draw(shape);
300        }
301    }
302
303    /**
304     * Returns the size.
305     *
306     * @return The size.
307     */
308    public int getSize() {
309        return this.size;
310    }
311
312    /**
313     * Sets the size.
314     *
315     * @param pixels  the new size.
316     */
317    public void setSize(int pixels) {
318        this.size = pixels;
319    }
320
321    /**
322     * Returns the transform.
323     *
324     * @return The transform.
325     */
326    public AffineTransform getTransform() {
327        return MeterNeedle.transform;
328    }
329
330    /**
331     * Tests another object for equality with this object.
332     *
333     * @param obj the object to test ({@code null} permitted).
334     *
335     * @return A boolean.
336     */
337    @Override
338    public boolean equals(Object obj) {
339        if (obj == this) {
340            return true;
341        }
342        if (!(obj instanceof MeterNeedle)) {
343            return false;
344        }
345        MeterNeedle that = (MeterNeedle) obj;
346        if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) {
347            return false;
348        }
349        if (!Objects.equals(this.outlineStroke, that.outlineStroke)) {
350            return false;
351        }
352        if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) {
353            return false;
354        }
355        if (!PaintUtils.equal(this.highlightPaint, that.highlightPaint)) {
356            return false;
357        }
358        if (this.size != that.size) {
359            return false;
360        }
361        if (this.rotateX != that.rotateX) {
362            return false;
363        }
364        if (this.rotateY != that.rotateY) {
365            return false;
366        }
367        return true;
368    }
369
370    /**
371     * Returns a hash code for this instance.
372     *
373     * @return A hash code.
374     */
375    @Override
376    public int hashCode() {
377        int result = HashUtils.hashCode(193, this.fillPaint);
378        result = HashUtils.hashCode(result, this.highlightPaint);
379        result = HashUtils.hashCode(result, this.outlinePaint);
380        result = HashUtils.hashCode(result, this.outlineStroke);
381        result = HashUtils.hashCode(result, this.rotateX);
382        result = HashUtils.hashCode(result, this.rotateY);
383        result = HashUtils.hashCode(result, this.size);
384        return result;
385    }
386
387    /**
388     * Provides serialization support.
389     *
390     * @param stream  the output stream.
391     *
392     * @throws IOException  if there is an I/O error.
393     */
394    private void writeObject(ObjectOutputStream stream) throws IOException {
395        stream.defaultWriteObject();
396        SerialUtils.writeStroke(this.outlineStroke, stream);
397        SerialUtils.writePaint(this.outlinePaint, stream);
398        SerialUtils.writePaint(this.fillPaint, stream);
399        SerialUtils.writePaint(this.highlightPaint, stream);
400    }
401
402    /**
403     * Provides serialization support.
404     *
405     * @param stream  the input stream.
406     *
407     * @throws IOException  if there is an I/O error.
408     * @throws ClassNotFoundException  if there is a classpath problem.
409     */
410    private void readObject(ObjectInputStream stream)
411        throws IOException, ClassNotFoundException {
412        stream.defaultReadObject();
413        this.outlineStroke = SerialUtils.readStroke(stream);
414        this.outlinePaint = SerialUtils.readPaint(stream);
415        this.fillPaint = SerialUtils.readPaint(stream);
416        this.highlightPaint = SerialUtils.readPaint(stream);
417    }
418
419}