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.geom.Line2D;
039import java.awt.geom.Path2D;
040import java.awt.geom.Point2D;
041import java.io.Serializable;
042
043import org.jfree.chart3d.data.Range;
044import org.jfree.chart3d.graphics2d.Anchor2D;
045import org.jfree.chart3d.internal.Args;
046import org.jfree.chart3d.internal.ObjectUtils;
047
048/**
049 * A marker that marks a range of values on an axis.
050 * <br><br>
051 * For an example, please refer to the demo {@code RangeMarkerDemo1.java}.
052 * <br><br>
053 * NOTE: This class is serializable, but the serialization format is subject 
054 * to change in future releases and should not be relied upon for persisting 
055 * instances of this class. 
056 * 
057 * @since 1.2
058 */
059@SuppressWarnings("serial")
060public class RangeMarker extends AbstractMarker implements ValueMarker, 
061        MarkerChangeListener, Serializable {
062    
063    /** The start of the range. */
064    private NumberMarker start;
065    
066    /** The end of the range. */
067    private NumberMarker end;
068
069    /** The label for the marker (optional). */
070    private String label;
071    
072    /** The font for the label. */
073    private Font font;
074    
075    /** The color for the label. */
076    private Color labelColor;
077    
078    /** The anchor for the label. */
079    private Anchor2D labelAnchor;
080
081    /** The color used to fill the band representing the marker range. */
082    Color fillColor;
083    
084    /**
085     * Creates a new range marker for the given bounds.
086     * 
087     * @param lowerBound  the lower bound.
088     * @param upperBound  the upper bound.
089     */
090    public RangeMarker(double lowerBound, double upperBound) {
091        this(lowerBound, upperBound, null);
092    }
093    
094    /**
095     * Creates a new range marker for the given bounds.
096     * 
097     * @param lowerBound  the lower bound.
098     * @param upperBound  the upper bound.
099     * @param label  the label ({@code null} permitted).
100     */
101    public RangeMarker(double lowerBound, double upperBound, String label) {
102             super();
103        this.start = new NumberMarker(lowerBound);
104        this.start.addChangeListener(this);
105        this.end = new NumberMarker(upperBound);
106        this.end.addChangeListener(this);
107        this.label = label;
108        this.font = DEFAULT_MARKER_FONT;
109        this.labelColor = DEFAULT_LABEL_COLOR;
110        this.labelAnchor = Anchor2D.CENTER;
111        this.fillColor = DEFAULT_FILL_COLOR;   
112    }
113    
114    /**
115     * Returns the starting point for the range marker.
116     * 
117     * @return The starting point.
118     */
119    public NumberMarker getStart() {
120        return this.start;
121    }
122    
123    /**
124     * Returns the ending point for the range marker.
125     * 
126     * @return The ending point. 
127     */
128    public NumberMarker getEnd() {
129        return this.end;
130    }
131    
132    /**
133     * Returns the range of values for the marker.
134     * 
135     * @return The range of values for the marker.
136     */
137    @Override
138    public Range getRange() {
139        return new Range(this.start.getValue(), this.end.getValue());
140    }
141
142    /**
143     * Returns the label for the marker (if this is {@code null} then no
144     * label is displayed).
145     * 
146     * @return The label (possibly {@code null}). 
147     */
148    public String getLabel() {
149        return this.label;
150    }
151    
152    /**
153     * Sets the label and sends a change event to all registered listeners.  If
154     * the label is set to {@code null} then no label is displayed for 
155     * the marker.
156     * 
157     * @param label  the label ({@code null} permitted).
158     */
159    public void setLabel(String label) {
160        this.label = label;
161        fireChangeEvent();
162    }
163    
164    /**
165     * Returns the font for the label.  The default value is 
166     * {@link Marker#DEFAULT_MARKER_FONT}.
167     * 
168     * @return The font (never {@code null}). 
169     */
170    public Font getFont() {
171        return this.font;
172    }
173    
174    /**
175     * Sets the font for the marker label and sends a change event to all 
176     * registered listeners.
177     * 
178     * @param font  the font ({@code null} not permitted). 
179     */
180    public void setFont(Font font) {
181        Args.nullNotPermitted(font, "font");
182        this.font = font;
183        fireChangeEvent();
184    }
185    
186    /**
187     * Returns the label color.  The default value is 
188     * {@link Marker#DEFAULT_LABEL_COLOR}.
189     * 
190     * @return The label color (never {@code null}).
191     */
192    public Color getLabelColor() {
193        return this.labelColor;
194    }
195    
196    /**
197     * Sets the label color and sends a change event to all registered
198     * listeners.
199     * 
200     * @param color  the color ({@code null} not permitted). 
201     */
202    public void setLabelColor(Color color) {
203        Args.nullNotPermitted(color, "color");
204        this.labelColor = color;
205        fireChangeEvent();
206    }
207    
208    /**
209     * Returns the anchor for the label.  The default value is 
210     * {@link Anchor2D#CENTER}.
211     * 
212     * @return The anchor for the label. 
213     */
214    public Anchor2D getLabelAnchor() {
215        return this.labelAnchor;
216    }
217    
218    /**
219     * Sets the anchor for the label and sends a change event to all registered
220     * listeners.
221     * 
222     * @param anchor  the anchor ({@code null} not permitted). 
223     */
224    public void setLabelAnchor(Anchor2D anchor) {
225        Args.nullNotPermitted(anchor, "anchor");
226        this.labelAnchor = anchor;
227        fireChangeEvent();
228    }
229
230    /**
231     * Returns the color used to fill the band representing the range for 
232     * the marker.  The default value is {@link Marker#DEFAULT_FILL_COLOR}.
233     * 
234     * @return The fill color (never {@code null}). 
235     */
236    public Color getFillColor() {
237        return this.fillColor;
238    }
239    
240    /**
241     * Sets the color used to fill the band representing the range for the
242     * marker and sends a change event to all registered listeners.
243     * 
244     * @param color  the color ({@code null} not permitted).
245     */
246    public void setFillColor(Color color) {
247        Args.nullNotPermitted(color, "color");
248        this.fillColor = color;
249        fireChangeEvent();
250    }
251    
252    @Override
253    public void draw(Graphics2D g2, MarkerData markerData, boolean reverse) {
254        MarkerLine startLine = markerData.getStartLine();
255        Line2D l1 = new Line2D.Double(startLine.getStartPoint(), 
256                startLine.getEndPoint());
257        MarkerLine endLine = markerData.getEndLine();
258        Line2D l2 = new Line2D.Double(endLine.getStartPoint(), 
259                endLine.getEndPoint());
260
261        Path2D path = new Path2D.Double();
262        path.moveTo(l1.getX1(), l1.getY1());
263        path.lineTo(l1.getX2(), l1.getY2());
264        path.lineTo(l2.getX2(), l2.getY2());
265        path.lineTo(l2.getX1(), l2.getY1());
266        path.closePath();
267        g2.setPaint(this.fillColor);
268        g2.fill(path);
269
270        if (!startLine.isPegged()) {
271            g2.setPaint(this.start.getLineColor());
272            g2.setStroke(this.start.getLineStroke());
273            g2.draw(l1);
274        }
275        if (!endLine.isPegged()) {
276            g2.setPaint(this.end.getLineColor());
277            g2.setStroke(this.end.getLineStroke());
278            g2.draw(l2);
279        }
280        
281        Point2D labelPoint = markerData.getLabelPoint(); 
282        if (labelPoint != null) {
283            g2.setFont(this.font);
284            g2.setColor(this.labelColor);
285            drawMarkerLabel(g2, this.label, labelPoint.getX(), 
286                    labelPoint.getY(), markerData.getLabelAnchor(), l1, l2,
287                    reverse);
288        }
289    }
290
291    /**
292     * Receives notification of a change to the start or end marker for the
293     * range.
294     * 
295     * @param event  the event ({@code null} not permitted). 
296     */
297    @Override
298    public void markerChanged(MarkerChangeEvent event) {
299        fireChangeEvent();
300    }
301
302    @Override
303    public int hashCode() {
304        int hash = 7;
305        hash = 97 * hash + ObjectUtils.hashCode(this.start);
306        hash = 97 * hash + ObjectUtils.hashCode(this.end);
307        hash = 97 * hash + ObjectUtils.hashCode(this.label);
308        return hash;
309    }
310
311    /**
312     * Tests this marker for equality with an arbitrary object.
313     * 
314     * @param obj  the object to test against ({@code null} permitted).
315     * 
316     * @return A boolean. 
317     */
318    @Override
319    public boolean equals(Object obj) {
320        if (obj == null) {
321            return false;
322        }
323        if (getClass() != obj.getClass()) {
324            return false;
325        }
326        final RangeMarker other = (RangeMarker) obj;
327        if (!ObjectUtils.equals(this.start, other.start)) {
328            return false;
329        }
330        if (!ObjectUtils.equals(this.end, other.end)) {
331            return false;
332        }
333        if (!ObjectUtils.equals(this.label, other.label)) {
334            return false;
335        }
336        if (!ObjectUtils.equals(this.font, other.font)) {
337            return false;
338        }
339        if (!ObjectUtils.equals(this.labelColor, other.labelColor)) {
340            return false;
341        }
342        if (!ObjectUtils.equals(this.labelAnchor, other.labelAnchor)) {
343            return false;
344        }
345        if (!ObjectUtils.equals(this.fillColor, other.fillColor)) {
346            return false;
347        }
348        return true;
349    }
350    
351}