    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.geom.Point2D;
    036import java.util.Map;
    037import java.util.HashMap;
    038import com.orsoncharts.util.ArgChecks;
    039import com.orsoncharts.util.Anchor2D;
    040
    041/**
    042 * A record holder for data relating to markers that needs to be passed
    043 * to the 3D engine.
    044 *
    045 * @since 1.2
    046 */
    047public class MarkerData {
    048
    049    /**
    050     * The key used to store the marker on the axis.  We can use this key
    051     * to retrieve the actual marker to get the label, font, color etc.
    052     */
    053    private String markerKey;
    054
    055    /**
    056     * The type of marker data (value or range).  A value marker will have
    057     * a 'valueLine' stored in the data map.  A range marker will have
    058     * a 'startLine' and an 'endLine' stored in the data map.
    059     */
    060    private MarkerDataType type;
    061
    062    /** Storage for data values (using a map for future expansion). */
    063    private Map<String, Object> data;
    064
    065    /**
    066     * Creates marker data for the case where there is a single line
    067     * (for example, the {@link NumberMarker} class).
    068     *
    069     * @param key  the key for the marker ({@code null} not permitted).
    070     * @param pos  the relative position along the axis (in the range 0.0 to
    071     *     1.0).
    072     */
    073    public MarkerData(String key, double pos) {
    074        ArgChecks.nullNotPermitted(key, "key");
    075        this.markerKey = key;
    076        this.type = MarkerDataType.VALUE;
    077        this.data = new HashMap<String, Object>();
    078        this.data.put("valueLine", new MarkerLine(pos, false));
    079    }
    080
    081    /**
    082     * Creates marker data for the case where there are two lines.
    083     * (for example, the {@link RangeMarker} class).
    084     *
    085     * @param key  the key ({@code null} not permitted).
    086     * @param startPos  the relative start position.
    087     * @param startPegged  is the start position pegged?
    088     * @param endPos  the relative end position.
    089     * @param endPegged  is the end position pegged?
    090     */
    091    public MarkerData(String key, double startPos, boolean startPegged,
    092            double endPos, boolean endPegged) {
    093        ArgChecks.nullNotPermitted(key, "key");
    094        this.markerKey = key;
    095        this.type = MarkerDataType.RANGE;
    096        this.data = new HashMap<String, Object>();
    097        this.data.put("startLine", new MarkerLine(startPos, startPegged));
    098        this.data.put("endLine", new MarkerLine(endPos, endPegged));
    099    }
    100
    101    /**
    102     * Creates a new instance based on an existing source that has type
    103     * {@code MarkerDataType.VALUE}.
    104     *
    105     * @param source  the source ({@code null} not permitted).
    106     * @param v0  the vertex index for the start of the line.
    107     * @param v1  the vertex index for the end of the line.
    108     */
    109    public MarkerData(MarkerData source, int v0, int v1) {
    110        ArgChecks.nullNotPermitted(source, "source");
    111        if (!source.getType().equals(MarkerDataType.VALUE)) {
    112            throw new IllegalArgumentException("Must be MarkerDataType.VALUE");
    113        }
    114        this.markerKey = source.markerKey;
    115        this.type = source.type;
    116        this.data = new HashMap<String, Object>(source.data);
    117        double pos = source.getValueLine().getPos();
    118        MarkerLine valueLine = new MarkerLine(pos, false, v0, v1);
    119        this.data.put("valueLine", valueLine);
    120    }
    121
    122    /**
    123     * Creates a new instance based on an existing source that has type
    124     * {@code MarkerDataType.Range}.
    125     *
    126     * @param source  the source ({@code null} not permitted).
    127     * @param v0  the vertex index for the start of the first line.
    128     * @param v1  the vertex index for the end of the first line.
    129     * @param v2  the vertex index for the start of the second line.
    130     * @param v3  the vertex index for the end of the second line.
    131     */
    132    public MarkerData(MarkerData source, int v0, int v1, int v2, int v3) {
    133        ArgChecks.nullNotPermitted(source, "source");
    134        if (!source.getType().equals(MarkerDataType.RANGE)) {
    135            throw new IllegalArgumentException("Must be MarkerDataType.RANGE");
    136        }
    137        this.markerKey = source.markerKey;
    138        this.type = MarkerDataType.RANGE;
    139        this.data = new HashMap<String, Object>(source.data);
    140        double startPos = source.getStartLine().getPos();
    141        boolean startPegged = source.getStartLine().isPegged();
    142        MarkerLine startLine = new MarkerLine(startPos, startPegged, v0, v1);
    143        this.data.put("startLine", startLine);
    144        double endPos = source.getEndLine().getPos();
    145        boolean endPegged = source.getEndLine().isPegged();
    146        MarkerLine endLine = new MarkerLine(endPos, endPegged, v2, v3);
    147        this.data.put("endLine", endLine);
    148    }
    149
    150    /**
    151     * Returns the marker key (allows retrieval of the original marker object
    152     * when required).
    153     *
    154     * @return The marker key (never {@code null}).
    155     */
    156    public String getMarkerKey() {
    157        return this.markerKey;
    158    }
    159
    160    /**
    161     * Returns the type of marker data (value or range).
    162     *
    163     * @return The type (never {@code null}).
    164     */
    165    public MarkerDataType getType() {
    166        return this.type;
    167    }
    168
    169    /**
    170     * A convenience method that returns the value line data for a value marker.
    171     *
    172     * @return The value line (or {@code null}).
    173     */
    174    public MarkerLine getValueLine() {
    175        return (MarkerLine) this.data.get("valueLine");
    176    }
    177
    178    /**
    179     * A convenience method that returns the start line data for a range marker.
    180     *
    181     * @return The start line (or {@code null}).
    182     */
    183    public MarkerLine getStartLine() {
    184        return (MarkerLine) this.data.get("startLine");
    185    }
    186
    187    /**
    188     * A convenience method that returns the end line data for a range marker.
    189     *
    190     * @return The end line (or {@code null}).
    191     */
    192    public MarkerLine getEndLine() {
    193        return (MarkerLine) this.data.get("endLine");
    194    }
    195
    196    /**
    197     * Returns the label anchor.
    198     *
    199     * @return The label anchor.
    200     */
    201    public Anchor2D getLabelAnchor() {
    202        return (Anchor2D) this.data.get("labelAnchor");
    203    }
    204
    205    /**
    206     * Sets the label anchor.
    207     *
    208     * @param anchor  the label anchor.
    209     */
    210    public void setLabelAnchor(Anchor2D anchor) {
    211        this.data.put("labelAnchor", anchor);
    212    }
    213
    214    /**
    215     * Returns the label vertex index.
    216     *
    217     * @return The label vertex index.
    218     */
    219    public int getLabelVertexIndex() {
    220        Integer i = (Integer) this.data.get("labelVertexIndex");
    221        return (i != null ? i.intValue() : -1);
    222    }
    223
    224    /**
    225     * Sets the label vertex index.
    226     *
    227     * @param labelVertexIndex  the label vertex index.
    228     */
    229    public void setLabelVertexIndex(int labelVertexIndex) {
    230        this.data.put("labelVertexIndex", Integer.valueOf(labelVertexIndex));
    231    }
    232
    233    /**
    234     * Returns the label projection point.
    235     *
    236     * @return The label projection point (possibly {@code null}).
    237     */
    238    public Point2D getLabelPoint() {
    239        return (Point2D) this.data.get("labelPoint");
    240    }
    241
    242    /**
    243     * Updates the projected points for this marker.  This needs to be done
    244     * before the markers can be drawn.
    245     *
    246     * @param pts  the projected points for the world.
    247     */
    248    public void updateProjection(Point2D[] pts) {
    249        if (this.type.equals(MarkerDataType.VALUE)) {
    250            MarkerLine line = getValueLine();
    251            line.setStartPoint(pts[line.getV0()]);
    252            line.setEndPoint(pts[line.getV1()]);
    253        } else if (this.type.equals(MarkerDataType.RANGE)) {
    254            MarkerLine startLine = getStartLine();
    255            startLine.setStartPoint(pts[startLine.getV0()]);
    256            startLine.setEndPoint(pts[startLine.getV1()]);
    257            MarkerLine endLine = getEndLine();
    258            endLine.setStartPoint(pts[endLine.getV0()]);
    259            endLine.setEndPoint(pts[endLine.getV1()]);
    260        }
    261        int labelVertex = getLabelVertexIndex();
    262        if (labelVertex >= 0) {
    263            this.data.put("labelPoint", pts[labelVertex]);
    264        } else {
    265            this.data.put("labelPoint", null);
    266        }
    267    }
    268
    269    @Override
    270    public String toString() {
    271        StringBuilder sb = new StringBuilder();
    272        sb.append("MarkerData[key=");
    273        sb.append(this.markerKey);
    274        sb.append("]");
    275        return sb.toString();
    276    }
    277}