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.geom.Point2D;
036import java.util.Map;
037import java.util.HashMap;
038import org.jfree.chart3d.graphics2d.Anchor2D;
039import org.jfree.chart3d.internal.Args;
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        Args.nullNotPermitted(key, "key");
075        this.markerKey = key;
076        this.type = MarkerDataType.VALUE;
077        this.data = new HashMap<>();
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        Args.nullNotPermitted(key, "key");
094        this.markerKey = key;
095        this.type = MarkerDataType.RANGE;
096        this.data = new HashMap<>();
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        Args.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<>(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        Args.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<>(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 : -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", 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        return "MarkerData[key=" + this.markerKey + "]";
272    }
273}