001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, by Object Refinery Limited 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025 * in the United States and other countries.]
026 *
027 * -------------------
028 * CrosshairState.java
029 * -------------------
030 * (C) Copyright 2002-2007, by Object Refinery Limited.
031 *
032 * Original Author:  David Gilbert (for Object Refinery Limited);
033 * Contributor(s):   -;
034 *
035 * Changes
036 * -------
037 * 24-Jan-2002 : Version 1 (DG);
038 * 05-Mar-2002 : Added Javadoc comments (DG);
039 * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
040 * 19-Sep-2003 : Modified crosshair distance calculation (DG);
041 * 04-Dec-2003 : Crosshair anchor point now stored outside chart since it is
042 *               dependent on the display target (DG);
043 * 25-Feb-2004 : Replaced CrosshairInfo --> CrosshairState (DG);
044 * ------------- JFREECHART 1.0.x ---------------------------------------------
045 * 13-Oct-2006 : Fixed initialisation of CrosshairState - see bug report 
046 *               1565168 (DG);
047 * 06-Feb-2007 : Added new fields and methods to fix bug 1086307 (DG);
048 *
049 */
050
051package org.jfree.chart.plot;
052
053import java.awt.geom.Point2D;
054
055/**
056 * Maintains state information about crosshairs on a plot between successive 
057 * calls to the renderer's draw method.  This class is used internally by 
058 * JFreeChart - it is not intended for external use.
059 */
060public class CrosshairState {
061
062    /** 
063     * A flag that controls whether the distance is calculated in data space 
064     * or Java2D space.
065     */
066    private boolean calculateDistanceInDataSpace = false;
067
068    /** The x-value (in data space) for the anchor point. */
069    private double anchorX;
070
071    /** The y-value (in data space) for the anchor point. */
072    private double anchorY;
073    
074    /** The anchor point in Java2D space - if null, don't update crosshair. */
075    private Point2D anchor;
076    
077    /** The x-value for the current crosshair point. */
078    private double crosshairX;
079
080    /** The y-value for the current crosshair point. */
081    private double crosshairY;
082
083    /**
084     * The index of the domain axis that the crosshair x-value is measured
085     * against.
086     * 
087     * @since 1.0.4
088     */
089    private int domainAxisIndex;
090    
091    /**
092     * The index of the range axis that the crosshair y-value is measured
093     * against.
094     * 
095     * @since 1.0.4
096     */
097    private int rangeAxisIndex;
098    
099    /** 
100     * The smallest distance (so far) between the anchor point and a data 
101     * point. 
102     */
103    private double distance;
104
105    /**
106     * Creates a new <code>CrosshairState</code> instance that calculates
107     * distance in Java2D space.
108     */
109    public CrosshairState() {
110        this(false);
111    }
112
113    /**
114     * Creates a new <code>CrosshairState</code> instance.
115     * 
116     * @param calculateDistanceInDataSpace  a flag that controls whether the 
117     *                                      distance is calculated in data 
118     *                                      space or Java2D space.
119     */
120    public CrosshairState(boolean calculateDistanceInDataSpace) {
121        this.calculateDistanceInDataSpace = calculateDistanceInDataSpace;
122    }
123
124    /**
125     * Returns the distance between the anchor point and the current crosshair
126     * point.
127     * 
128     * @return The distance.
129     * 
130     * @see #setCrosshairDistance(double)
131     * @since 1.0.3
132     */
133    public double getCrosshairDistance() {
134        return this.distance;
135    }
136    
137    /**
138     * Sets the distance between the anchor point and the current crosshair 
139     * point.  As each data point is processed, its distance to the anchor 
140     * point is compared with this value and, if it is closer, the data point 
141     * becomes the new crosshair point.
142     *
143     * @param distance  the distance.
144     * 
145     * @see #getCrosshairDistance()
146     */
147    public void setCrosshairDistance(double distance) {
148        this.distance = distance;
149    }
150
151    /**
152     * Evaluates a data point and if it is the closest to the anchor point it
153     * becomes the new crosshair point.
154     * <P>
155     * To understand this method, you need to know the context in which it will
156     * be called.  An instance of this class is passed to an 
157     * {@link org.jfree.chart.renderer.xy.XYItemRenderer} as
158     * each data point is plotted.  As the point is plotted, it is passed to
159     * this method to see if it should be the new crosshair point.
160     *
161     * @param x  x coordinate (measured against the domain axis).
162     * @param y  y coordinate (measured against the range axis).
163     * @param transX  x translated into Java2D space.
164     * @param transY  y translated into Java2D space.
165     * @param orientation  the plot orientation.
166     * 
167     * @deprecated Use {@link #updateCrosshairPoint(double, double, int, int, 
168     *     double, double, PlotOrientation)}.  See bug report 1086307.
169     */
170    public void updateCrosshairPoint(double x, double y, 
171                                     double transX, double transY, 
172                                     PlotOrientation orientation) {
173        updateCrosshairPoint(x, y, 0, 0, transX, transY, orientation);
174    }
175    
176    /**
177     * Evaluates a data point and if it is the closest to the anchor point it
178     * becomes the new crosshair point.
179     * <P>
180     * To understand this method, you need to know the context in which it will
181     * be called.  An instance of this class is passed to an 
182     * {@link org.jfree.chart.renderer.xy.XYItemRenderer} as
183     * each data point is plotted.  As the point is plotted, it is passed to
184     * this method to see if it should be the new crosshair point.
185     *
186     * @param x  x coordinate (measured against the domain axis).
187     * @param y  y coordinate (measured against the range axis).
188     * @param domainAxisIndex  the index of the domain axis for this point.
189     * @param rangeAxisIndex  the index of the range axis for this point.
190     * @param transX  x translated into Java2D space.
191     * @param transY  y translated into Java2D space.
192     * @param orientation  the plot orientation.
193     * 
194     * @since 1.0.4
195     */
196    public void updateCrosshairPoint(double x, double y, int domainAxisIndex,
197            int rangeAxisIndex, double transX, double transY, 
198            PlotOrientation orientation) {
199
200        if (this.anchor != null) {
201            double d = 0.0;
202            if (this.calculateDistanceInDataSpace) {
203                d = (x - this.anchorX) * (x - this.anchorX)
204                  + (y - this.anchorY) * (y - this.anchorY);
205            }
206            else {
207                double xx = this.anchor.getX();
208                double yy = this.anchor.getY();
209                if (orientation == PlotOrientation.HORIZONTAL) {
210                    double temp = yy;
211                    yy = xx;
212                    xx = temp;
213                }
214                d = (transX - xx) * (transX - xx) 
215                    + (transY - yy) * (transY - yy);            
216            }
217
218            if (d < this.distance) {
219                this.crosshairX = x;
220                this.crosshairY = y;
221                this.domainAxisIndex = domainAxisIndex;
222                this.rangeAxisIndex = rangeAxisIndex;
223                this.distance = d;
224            }
225        }
226
227    }
228
229    /**
230     * Evaluates an x-value and if it is the closest to the anchor x-value it
231     * becomes the new crosshair value.
232     * <P>
233     * Used in cases where only the x-axis is numerical.
234     *
235     * @param candidateX  x position of the candidate for the new crosshair 
236     *                    point.
237     *                    
238     * @deprecated Use {@link #updateCrosshairX(double, int)}.  See bug report 
239     *     1086307.
240     */
241    public void updateCrosshairX(double candidateX) {
242        updateCrosshairX(candidateX, 0);
243    }
244    
245    /**
246     * Evaluates an x-value and if it is the closest to the anchor x-value it
247     * becomes the new crosshair value.
248     * <P>
249     * Used in cases where only the x-axis is numerical.
250     *
251     * @param candidateX  x position of the candidate for the new crosshair 
252     *                    point.
253     * @param domainAxisIndex  the index of the domain axis for this x-value.
254     * 
255     * @since 1.0.4
256     */
257    public void updateCrosshairX(double candidateX, int domainAxisIndex) {
258
259        double d = Math.abs(candidateX - this.anchorX);
260        if (d < this.distance) {
261            this.crosshairX = candidateX;
262            this.domainAxisIndex = domainAxisIndex;
263            this.distance = d;
264        }
265
266    }
267
268    /**
269     * Evaluates a y-value and if it is the closest to the anchor y-value it
270     * becomes the new crosshair value.
271     * <P>
272     * Used in cases where only the y-axis is numerical.
273     *
274     * @param candidateY  y position of the candidate for the new crosshair 
275     *                    point.
276     *                    
277     * @deprecated Use {@link #updateCrosshairY(double, int)}.  See bug report 
278     *     1086307.
279     */
280    public void updateCrosshairY(double candidateY) {
281        updateCrosshairY(candidateY, 0);
282    }
283
284    /**
285     * Evaluates a y-value and if it is the closest to the anchor y-value it
286     * becomes the new crosshair value.
287     * <P>
288     * Used in cases where only the y-axis is numerical.
289     *
290     * @param candidateY  y position of the candidate for the new crosshair 
291     *                    point.
292     * @param rangeAxisIndex  the index of the range axis for this y-value.
293     * 
294     * @since 1.0.4
295     */
296    public void updateCrosshairY(double candidateY, int rangeAxisIndex) {
297        double d = Math.abs(candidateY - this.anchorY);
298        if (d < this.distance) {
299            this.crosshairY = candidateY;
300            this.rangeAxisIndex = rangeAxisIndex;
301            this.distance = d;
302        }
303
304    }
305
306    /**
307     * Returns the anchor point.
308     * 
309     * @return The anchor point.
310     * 
311     * @see #setAnchor(Point2D)
312     * @since 1.0.3
313     */
314    public Point2D getAnchor() {
315        return this.anchor;    
316    }
317    
318    /** 
319     * Sets the anchor point.  This is usually the mouse click point in a chart
320     * panel, and the crosshair point will often be the data item that is 
321     * closest to the anchor point.
322     * <br><br>
323     * Note that the x and y coordinates (in data space) are not updated by 
324     * this method - the caller is responsible for ensuring that this happens
325     * in sync.
326     * 
327     * @param anchor  the anchor point (<code>null</code> permitted).
328     * 
329     * @see #getAnchor()
330     */
331    public void setAnchor(Point2D anchor) {
332        this.anchor = anchor;
333    }
334    
335    /**
336     * Returns the x-coordinate (in data space) for the anchor point.
337     * 
338     * @return The x-coordinate of the anchor point.
339     * 
340     * @since 1.0.3
341     */
342    public double getAnchorX() {
343        return this.anchorX;    
344    }
345    
346    /**
347     * Sets the x-coordinate (in data space) for the anchor point.  Note that
348     * this does NOT update the anchor itself - the caller is responsible for
349     * ensuring this is done in sync.
350     * 
351     * @param x  the x-coordinate.
352     * 
353     * @since 1.0.3
354     */
355    public void setAnchorX(double x) {
356        this.anchorX = x;
357    }
358    
359    /**
360     * Returns the y-coordinate (in data space) for the anchor point.
361     * 
362     * @return The y-coordinate of teh anchor point.
363     * 
364     * @since 1.0.3
365     */
366    public double getAnchorY() {
367        return this.anchorY;    
368    }
369    
370    /**
371     * Sets the y-coordinate (in data space) for the anchor point.  Note that
372     * this does NOT update the anchor itself - the caller is responsible for
373     * ensuring this is done in sync.
374     * 
375     * @param y  the y-coordinate.
376     * 
377     * @since 1.0.3
378     */
379    public void setAnchorY(double y) {
380        this.anchorY = y;
381    }
382    
383    /**
384     * Get the x-value for the crosshair point.
385     *
386     * @return The x position of the crosshair point.
387     * 
388     * @see #setCrosshairX(double)
389     */
390    public double getCrosshairX() {
391        return this.crosshairX;
392    }
393    
394    /**
395     * Sets the x coordinate for the crosshair.  This is the coordinate in data
396     * space measured against the domain axis.
397     * 
398     * @param x the coordinate.
399     * 
400     * @see #getCrosshairX()
401     * @see #setCrosshairY(double)
402     * @see #updateCrosshairPoint(double, double, double, double, 
403     * PlotOrientation)
404     */
405    public void setCrosshairX(double x) {
406        this.crosshairX = x;
407    }
408
409    /**
410     * Get the y-value for the crosshair point.  This is the coordinate in data
411     * space measured against the range axis.
412     *
413     * @return The y position of the crosshair point.
414     * 
415     * @see #setCrosshairY(double)
416     */
417    public double getCrosshairY() {
418        return this.crosshairY;
419    }
420
421    /**
422     * Sets the y coordinate for the crosshair.
423     * 
424     * @param y  the y coordinate.
425     * 
426     * @see #getCrosshairY()
427     * @see #setCrosshairX(double)
428     * @see #updateCrosshairPoint(double, double, double, double, 
429     * PlotOrientation)
430     */
431    public void setCrosshairY(double y) {
432        this.crosshairY = y;
433    }
434    
435    /**
436     * Returns the domain axis index for the crosshair x-value.
437     * 
438     * @return The domain axis index.
439     * 
440     * @since 1.0.4
441     */
442    public int getDomainAxisIndex() {
443        return this.domainAxisIndex;
444    }
445
446    /**
447     * Returns the range axis index for the crosshair y-value.
448     * 
449     * @return The range axis index.
450     * 
451     * @since 1.0.4
452     */
453    public int getRangeAxisIndex() {
454        return this.rangeAxisIndex;
455    }
456}