    001/* ===========================================================
    002 * Orson Charts : a 3D chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C)opyright 2013-2016, by Object Refinery Limited.  All rights reserved.
    006 *
    007 * http://www.object-refinery.com/orsoncharts/index.html
    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 com.orsoncharts.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 com.orsoncharts.Range;
    044import com.orsoncharts.util.Anchor2D;
    045import com.orsoncharts.util.ArgChecks;
    046import com.orsoncharts.util.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        ArgChecks.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        ArgChecks.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        ArgChecks.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        ArgChecks.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}