001/* ===========================================================
002 * Orson Charts : a 3D chart library for the Java(tm) platform
003 * ===========================================================
004 * 
005 * (C)opyright 2013-2022, by David Gilbert.  All rights reserved.
006 * 
007 * https://github.com/jfree/orson-charts
008 * 
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as published by
011 * the Free Software Foundation, either version 3 of the License, or
012 * (at your option) any later version.
013 *
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 *
019 * You should have received a copy of the GNU General Public License
020 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
021 * 
022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
023 * Other names may be trademarks of their respective owners.]
024 * 
025 * If you do not wish to be bound by the terms of the GPL, an alternative
026 * commercial license can be purchased.  For details, please see visit the
027 * Orson Charts home page:
028 * 
029 * http://www.object-refinery.com/orsoncharts/index.html
030 * 
031 */
032
033package org.jfree.chart3d.marker;
034
035import java.awt.Color;
036import java.awt.Font;
037import java.awt.Graphics2D;
038import java.awt.Stroke;
039import java.awt.geom.Line2D;
040import java.awt.geom.Point2D;
041import java.io.IOException;
042import java.io.ObjectInputStream;
043import java.io.ObjectOutputStream;
044import java.io.Serializable;
045
046import org.jfree.chart3d.data.Range;
047import org.jfree.chart3d.graphics2d.Anchor2D;
048import org.jfree.chart3d.internal.Args;
049import org.jfree.chart3d.internal.ObjectUtils;
050import org.jfree.chart3d.internal.SerialUtils;
051
052/**
053 * A marker used to mark one value on an axis.
054 * <br><br>
055 * NOTE: This class is serializable, but the serialization format is subject 
056 * to change in future releases and should not be relied upon for persisting 
057 * instances of this class. 
058 * 
059 * @since 1.2
060 */
061@SuppressWarnings("serial")
062public class NumberMarker extends AbstractMarker implements ValueMarker,
063        Serializable {
064
065    /** The data value to be marked. */
066    private double value;
067    
068    /** The label for the marker (optional). */
069    private String label;
070    
071    /** The font for the label. */
072    private Font font;
073    
074    /** The color for the label. */
075    private Color labelColor;
076    
077    /** The anchor for the label. */
078    private Anchor2D labelAnchor;
079    
080    /** The stroke for the marker line. */
081    private transient Stroke stroke;
082    
083    /** The color for the marker line. */
084    private Color lineColor;
085    
086    /**
087     * Creates a new marker.
088     * 
089     * @param value  the value. 
090     */
091    public NumberMarker(double value) {
092        super();
093        this.value = value;
094        this.label = null;
095        this.font = DEFAULT_MARKER_FONT;
096        this.labelColor = DEFAULT_LABEL_COLOR;
097        this.stroke = DEFAULT_LINE_STROKE;
098        this.lineColor = DEFAULT_LINE_COLOR;
099        this.labelAnchor = Anchor2D.CENTER;
100    }
101    
102    /**
103     * Returns the value for the marker (the initial value comes from the 
104     * constructor).
105     * 
106     * @return The value. 
107     */
108    public double getValue() {
109        return this.value;
110    }
111    
112    /**
113     * Sets the value for the marker and sends a change event to all registered
114     * listeners.
115     * 
116     * @param value  the value. 
117     */
118    public void setValue(double value) {
119        this.value = value;
120        fireChangeEvent();
121    }
122    
123    /**
124     * Returns the range for the marker (in this case, a single value range).
125     * This method is used by the axis to filter out markers that do not touch 
126     * the current axis range.
127     * 
128     * @return The range for the marker (never {@code null}). 
129     */
130    @Override
131    public Range getRange() {
132        return new Range(this.value, this.value);
133    }
134
135    /**
136     * Returns the label for the marker (if this is {@code null} then no
137     * label is displayed).  The default value is {@code null}.
138     * 
139     * @return The label (possibly {@code null}). 
140     */
141    public String getLabel() {
142        return this.label;
143    }
144    
145    /**
146     * Sets the label and sends a change event to all registered listeners.
147     * 
148     * @param label  the label ({@code null} permitted).
149     */
150    public void setLabel(String label) {
151        this.label = label;
152        fireChangeEvent();
153    }
154    
155    /**
156     * Returns the font for the label.  The default value is 
157     * {@link Marker#DEFAULT_MARKER_FONT}.
158     * 
159     * @return The font (never {@code null}). 
160     */
161    public Font getFont() {
162        return this.font;
163    }
164    
165    /**
166     * Sets the font for the marker label and sends a change event to all 
167     * registered listeners.
168     * 
169     * @param font  the font ({@code null} not permitted). 
170     */
171    public void setFont(Font font) {
172        Args.nullNotPermitted(font, "font");
173        this.font = font;
174        fireChangeEvent();
175    }
176    
177    /**
178     * Returns the label color.  The default value is 
179     * {@link Marker#DEFAULT_LABEL_COLOR}.
180     * 
181     * @return The label color (never {@code null}).
182     */
183    public Color getLabelColor() {
184        return this.labelColor;
185    }
186    
187    /**
188     * Sets the label color and sends a change event to all registered
189     * listeners.
190     * 
191     * @param color  the color ({@code null} not permitted). 
192     */
193    public void setLabelColor(Color color) {
194        Args.nullNotPermitted(color, "color");
195        this.labelColor = color;
196        fireChangeEvent();
197    }
198    
199    /**
200     * Returns the anchor for the label.  The default value is 
201     * {@link Anchor2D#CENTER}.
202     * 
203     * @return The anchor for the label. 
204     */
205    public Anchor2D getLabelAnchor() {
206        return this.labelAnchor;
207    }
208    
209    /**
210     * Sets the anchor for the label and sends a change event to all registered
211     * listeners.
212     * 
213     * @param anchor  the anchor ({@code null} not permitted). 
214     */
215    public void setLabelAnchor(Anchor2D anchor) {
216        Args.nullNotPermitted(anchor, "anchor");
217        this.labelAnchor = anchor;
218        fireChangeEvent();
219    }
220     
221    /**
222     * Returns the stroke for the marker line.  The default value is
223     * {@link Marker#DEFAULT_LINE_STROKE}.
224     * 
225     * @return The stroke for the marker line (never {@code null}).
226     */
227    public Stroke getLineStroke() {
228        return this.stroke;    
229    }
230    
231    /**
232     * Sets the stroke for the marker line and sends a change event to all
233     * registered listeners.
234     * 
235     * @param stroke  the stroke ({@code null} not permitted). 
236     */
237    public void setLineStroke(Stroke stroke) {
238        Args.nullNotPermitted(stroke, "stroke");
239        this.stroke = stroke;
240        fireChangeEvent();
241    }
242    
243    /**
244     * Returns the color for the marker line.  The default value is 
245     * {@link Marker#DEFAULT_LINE_COLOR}.
246     * 
247     * @return The color for the marker line (never {@code null}). 
248     */
249    public Color getLineColor() {
250        return this.lineColor;
251    }
252    
253    /**
254     * Sets the color for the marker line and sends a change event to all 
255     * registered listeners.
256     * 
257     * @param color  the color ({@code null} not permitted). 
258     */
259    public void setLineColor(Color color) {
260        Args.nullNotPermitted(color, "color");
261        this.lineColor = color;
262        fireChangeEvent();
263    }
264    
265    /**
266     * Draws the marker.  This method is called by the library, you won't 
267     * normally call it directly.
268     * 
269     * @param g2  the graphics target ({@code null} not permitted).
270     * @param markerData  transient marker data ({@code null} not 
271     *     permitted).
272     */
273    @Override
274    public void draw(Graphics2D g2, MarkerData markerData, boolean reverse) {
275        MarkerLine line = markerData.getValueLine();
276        g2.setPaint(this.lineColor);
277        g2.setStroke(this.stroke);
278        Line2D l = new Line2D.Double(line.getStartPoint(), line.getEndPoint());
279        g2.draw(l);
280        Point2D labelPoint = markerData.getLabelPoint(); 
281        if (labelPoint != null) {
282            g2.setFont(this.font);
283            g2.setColor(this.labelColor);
284            drawMarkerLabel(g2, this.label, labelPoint.getX(), 
285                    labelPoint.getY(), this.labelAnchor, l, reverse);
286        }
287    }
288
289    @Override
290    public int hashCode() {
291        int hash = 7;
292        hash = 19 * hash + (int) (Double.doubleToLongBits(this.value) 
293                ^ (Double.doubleToLongBits(this.value) >>> 32));
294        hash = 19 * hash + ObjectUtils.hashCode(this.label);
295        return hash;
296    }
297
298    @Override
299    public boolean equals(Object obj) {
300        if (obj == null) {
301            return false;
302        }
303        if (getClass() != obj.getClass()) {
304            return false;
305        }
306        final NumberMarker other = (NumberMarker) obj;
307        if (Double.doubleToLongBits(this.value) 
308                != Double.doubleToLongBits(other.value)) {
309            return false;
310        }
311        if (!ObjectUtils.equals(this.label, other.label)) {
312            return false;
313        }
314        if (!ObjectUtils.equals(this.font, other.font)) {
315            return false;
316        }
317        if (!ObjectUtils.equals(this.labelColor, other.labelColor)) {
318            return false;
319        }
320        if (!ObjectUtils.equals(this.labelAnchor, other.labelAnchor)) {
321            return false;
322        }
323        if (!ObjectUtils.equals(this.stroke, other.stroke)) {
324            return false;
325        }
326        if (!ObjectUtils.equals(this.lineColor, other.lineColor)) {
327            return false;
328        }
329        return true;
330    }
331
332    /**
333     * Provides serialization support.
334     *
335     * @param stream  the output stream.
336     *
337     * @throws IOException  if there is an I/O error.
338     */
339    private void writeObject(ObjectOutputStream stream) throws IOException {
340        stream.defaultWriteObject();
341        SerialUtils.writeStroke(this.stroke, stream);
342    }
343
344    /**
345     * Provides serialization support.
346     *
347     * @param stream  the input stream.
348     *
349     * @throws IOException  if there is an I/O error.
350     * @throws ClassNotFoundException  if there is a classpath problem.
351     */
352    private void readObject(ObjectInputStream stream)
353        throws IOException, ClassNotFoundException {
354        stream.defaultReadObject();
355        this.stroke = SerialUtils.readStroke(stream);
356    }
357   
358}