001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-present, by David Gilbert and Contributors.
006 *
007 * Project Info:  http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022 * USA.
023 *
024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 
025 * Other names may be trademarks of their respective owners.]
026 *
027 * -------------------
028 * CrosshairState.java
029 * -------------------
030 * (C) Copyright 2002-present, by David Gilbert.
031 *
032 * Original Author:  David Gilbert;
033 * Contributor(s):   -;
034 *
035 */
036
037package org.jfree.chart.plot;
038
039import java.awt.geom.Point2D;
040
041/**
042 * Maintains state information about crosshairs on a plot between successive
043 * calls to the renderer's draw method.  This class is used internally by
044 * JFreeChart - it is not intended for external use.
045 */
046public class CrosshairState {
047
048    /**
049     * A flag that controls whether the distance is calculated in data space
050     * or Java2D space.
051     */
052    private boolean calculateDistanceInDataSpace = false;
053
054    /** The x-value (in data space) for the anchor point. */
055    private double anchorX;
056
057    /** The y-value (in data space) for the anchor point. */
058    private double anchorY;
059
060    /** The anchor point in Java2D space - if null, don't update crosshair. */
061    private Point2D anchor;
062
063    /** The x-value for the current crosshair point. */
064    private double crosshairX;
065
066    /** The y-value for the current crosshair point. */
067    private double crosshairY;
068
069    /**
070     * The dataset index that the crosshair point relates to (this determines
071     * the axes that the crosshairs will be plotted against).
072     */
073    private int datasetIndex;
074
075    /**
076     * The smallest distance (so far) between the anchor point and a data
077     * point.
078     */
079    private double distance;
080
081    /**
082     * Creates a new {@code crosshairState} instance that calculates
083     * distance in Java2D space.
084     */
085    public CrosshairState() {
086        this(false);
087    }
088
089    /**
090     * Creates a new {@code crosshairState} instance.  Determination of the
091     * data point nearest the anchor point can be calculated in either
092     * dataspace or Java2D space.  The former should only be used for charts
093     * with a single set of axes.
094     *
095     * @param calculateDistanceInDataSpace  a flag that controls whether the
096     *                                      distance is calculated in data
097     *                                      space or Java2D space.
098     */
099    public CrosshairState(boolean calculateDistanceInDataSpace) {
100        this.calculateDistanceInDataSpace = calculateDistanceInDataSpace;
101    }
102
103    /**
104     * Returns the distance between the anchor point and the current crosshair
105     * point.
106     *
107     * @return The distance.
108     *
109     * @see #setCrosshairDistance(double)
110     */
111    public double getCrosshairDistance() {
112        return this.distance;
113    }
114
115    /**
116     * Sets the distance between the anchor point and the current crosshair
117     * point.  As each data point is processed, its distance to the anchor
118     * point is compared with this value and, if it is closer, the data point
119     * becomes the new crosshair point.
120     *
121     * @param distance  the distance.
122     *
123     * @see #getCrosshairDistance()
124     */
125    public void setCrosshairDistance(double distance) {
126        this.distance = distance;
127    }
128    
129    /**
130     * Updates the crosshair point.
131     * 
132     * @param x  the x-value.
133     * @param y  the y-value.
134     * @param datasetIndex  the dataset index.
135     * @param transX  the x-value in Java2D space.
136     * @param transY  the y-value in Java2D space.
137     * @param orientation  the plot orientation ({@code null} not permitted).
138     */
139    public void updateCrosshairPoint(double x, double y, int datasetIndex,
140            double transX, double transY, PlotOrientation orientation) {
141
142        if (this.anchor != null) {
143            double d = 0.0;
144            if (this.calculateDistanceInDataSpace) { 
145                d = (x - this.anchorX) * (x - this.anchorX)
146                  + (y - this.anchorY) * (y - this.anchorY);
147            }
148            else {
149                // anchor point is in Java2D coordinates
150                double xx = this.anchor.getX();
151                double yy = this.anchor.getY();
152                if (orientation == PlotOrientation.HORIZONTAL) {
153                    double temp = yy;
154                    yy = xx;
155                    xx = temp;
156                }
157                d = (transX - xx) * (transX - xx)
158                    + (transY - yy) * (transY - yy);
159            }
160
161            if (d < this.distance) {
162                this.crosshairX = x;
163                this.crosshairY = y;
164                this.datasetIndex = datasetIndex;
165                this.distance = d;
166            }
167        }
168
169    }
170    
171    /**
172     * Checks to see if the specified data point is the closest to the
173     * anchor point and, if yes, updates the current state.
174     * 
175     * @param x  the x-value.
176     * @param transX  the x-value in Java2D space.
177     * @param datasetIndex  the dataset index.
178     */
179    public void updateCrosshairX(double x, double transX, int datasetIndex) {
180        if (this.anchor == null) {
181            return;
182        }
183        double d = Math.abs(transX - this.anchor.getX());
184        if (d < this.distance) {
185            this.crosshairX = x;
186            this.datasetIndex = datasetIndex;
187            this.distance = d;
188        }        
189    }
190
191    /**
192     * Evaluates a y-value and if it is the closest to the anchor y-value it
193     * becomes the new crosshair value.
194     * <P>
195     * Used in cases where only the y-axis is numerical.
196     *
197     * @param candidateY  y position of the candidate for the new crosshair
198     *                    point.
199     * @param transY  the y-value in Java2D space.
200     * @param datasetIndex  the index of the range axis for this y-value.
201     */
202    public void updateCrosshairY(double candidateY, double transY, int datasetIndex) {
203        if (this.anchor == null) {
204            return;
205        }
206        double d = Math.abs(transY - this.anchor.getY());
207        if (d < this.distance) {
208            this.crosshairY = candidateY;
209            this.datasetIndex = datasetIndex;
210            this.distance = d;
211        }
212
213    }
214
215    /**
216     * Returns the anchor point.
217     *
218     * @return The anchor point.
219     *
220     * @see #setAnchor(Point2D)
221     */
222    public Point2D getAnchor() {
223        return this.anchor;
224    }
225
226    /**
227     * Sets the anchor point.  This is usually the mouse click point in a chart
228     * panel, and the crosshair point will often be the data item that is
229     * closest to the anchor point.
230     * <br><br>
231     * Note that the x and y coordinates (in data space) are not updated by
232     * this method - the caller is responsible for ensuring that this happens
233     * in sync.
234     *
235     * @param anchor  the anchor point ({@code null} permitted).
236     *
237     * @see #getAnchor()
238     */
239    public void setAnchor(Point2D anchor) {
240        this.anchor = anchor;
241    }
242
243    /**
244     * Returns the x-coordinate (in data space) for the anchor point.
245     *
246     * @return The x-coordinate of the anchor point.
247     */
248    public double getAnchorX() {
249        return this.anchorX;
250    }
251
252    /**
253     * Sets the x-coordinate (in data space) for the anchor point.  Note that
254     * this does NOT update the anchor itself - the caller is responsible for
255     * ensuring this is done in sync.
256     *
257     * @param x  the x-coordinate.
258     */
259    public void setAnchorX(double x) {
260        this.anchorX = x;
261    }
262
263    /**
264     * Returns the y-coordinate (in data space) for the anchor point.
265     *
266     * @return The y-coordinate of teh anchor point.
267     */
268    public double getAnchorY() {
269        return this.anchorY;
270    }
271
272    /**
273     * Sets the y-coordinate (in data space) for the anchor point.  Note that
274     * this does NOT update the anchor itself - the caller is responsible for
275     * ensuring this is done in sync.
276     *
277     * @param y  the y-coordinate.
278     */
279    public void setAnchorY(double y) {
280        this.anchorY = y;
281    }
282
283    /**
284     * Get the x-value for the crosshair point.
285     *
286     * @return The x position of the crosshair point.
287     *
288     * @see #setCrosshairX(double)
289     */
290    public double getCrosshairX() {
291        return this.crosshairX;
292    }
293
294    /**
295     * Sets the x coordinate for the crosshair.  This is the coordinate in data
296     * space measured against the domain axis.
297     *
298     * @param x the coordinate.
299     *
300     * @see #getCrosshairX()
301     * @see #setCrosshairY(double)
302     * @see #updateCrosshairPoint(double, double, int, double, double,
303     * PlotOrientation)
304     */
305    public void setCrosshairX(double x) {
306        this.crosshairX = x;
307    }
308
309    /**
310     * Get the y-value for the crosshair point.  This is the coordinate in data
311     * space measured against the range axis.
312     *
313     * @return The y position of the crosshair point.
314     *
315     * @see #setCrosshairY(double)
316     */
317    public double getCrosshairY() {
318        return this.crosshairY;
319    }
320
321    /**
322     * Sets the y coordinate for the crosshair.
323     *
324     * @param y  the y coordinate.
325     *
326     * @see #getCrosshairY()
327     * @see #setCrosshairX(double)
328     * @see #updateCrosshairPoint(double, double, int, double, double,
329     * PlotOrientation)
330     */
331    public void setCrosshairY(double y) {
332        this.crosshairY = y;
333    }
334
335    /**
336     * Returns the dataset index that the crosshair values relate to.  The
337     * dataset is mapped to specific axes, and this is how the crosshairs are
338     * mapped also.
339     *
340     * @return The dataset index.
341     *
342     * @see #setDatasetIndex(int)
343     */
344    public int getDatasetIndex() {
345        return this.datasetIndex;
346    }
347
348    /**
349     * Sets the dataset index that the current crosshair values relate to.
350     *
351     * @param index  the dataset index.
352     *
353     * @see #getDatasetIndex()
354     */
355    public void setDatasetIndex(int index) {
356        this.datasetIndex = index;
357    }
358}