001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2017, 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 * [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-2017, 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 * 26-Jun-2008 : Now tracks dataset index (DG); 049 * 18-Feb-2017 : Fix for crosshairs with multiple datasets / axes - see 050 * bug #36 (DG); 051 * 052 */ 053 054package org.jfree.chart.plot; 055 056import java.awt.geom.Point2D; 057 058/** 059 * Maintains state information about crosshairs on a plot between successive 060 * calls to the renderer's draw method. This class is used internally by 061 * JFreeChart - it is not intended for external use. 062 */ 063public class CrosshairState { 064 065 /** 066 * A flag that controls whether the distance is calculated in data space 067 * or Java2D space. 068 */ 069 private boolean calculateDistanceInDataSpace = false; 070 071 /** The x-value (in data space) for the anchor point. */ 072 private double anchorX; 073 074 /** The y-value (in data space) for the anchor point. */ 075 private double anchorY; 076 077 /** The anchor point in Java2D space - if null, don't update crosshair. */ 078 private Point2D anchor; 079 080 /** The x-value for the current crosshair point. */ 081 private double crosshairX; 082 083 /** The y-value for the current crosshair point. */ 084 private double crosshairY; 085 086 /** 087 * The dataset index that the crosshair point relates to (this determines 088 * the axes that the crosshairs will be plotted against). 089 * 090 * @since 1.0.11 091 */ 092 private int datasetIndex; 093 094 /** 095 * The smallest distance (so far) between the anchor point and a data 096 * point. 097 */ 098 private double distance; 099 100 /** 101 * Creates a new {@code crosshairState} instance that calculates 102 * distance in Java2D space. 103 */ 104 public CrosshairState() { 105 this(false); 106 } 107 108 /** 109 * Creates a new {@code crosshairState} instance. Determination of the 110 * data point nearest the anchor point can be calculated in either 111 * dataspace or Java2D space. The former should only be used for charts 112 * with a single set of axes. 113 * 114 * @param calculateDistanceInDataSpace a flag that controls whether the 115 * distance is calculated in data 116 * space or Java2D space. 117 */ 118 public CrosshairState(boolean calculateDistanceInDataSpace) { 119 this.calculateDistanceInDataSpace = calculateDistanceInDataSpace; 120 } 121 122 /** 123 * Returns the distance between the anchor point and the current crosshair 124 * point. 125 * 126 * @return The distance. 127 * 128 * @see #setCrosshairDistance(double) 129 * @since 1.0.3 130 */ 131 public double getCrosshairDistance() { 132 return this.distance; 133 } 134 135 /** 136 * Sets the distance between the anchor point and the current crosshair 137 * point. As each data point is processed, its distance to the anchor 138 * point is compared with this value and, if it is closer, the data point 139 * becomes the new crosshair point. 140 * 141 * @param distance the distance. 142 * 143 * @see #getCrosshairDistance() 144 */ 145 public void setCrosshairDistance(double distance) { 146 this.distance = distance; 147 } 148 149 /** 150 * Updates the crosshair point. 151 * 152 * @param x the x-value. 153 * @param y the y-value. 154 * @param datasetIndex the dataset index. 155 * @param transX the x-value in Java2D space. 156 * @param transY the y-value in Java2D space. 157 * @param orientation the plot orientation ({@code null} not permitted). 158 */ 159 public void updateCrosshairPoint(double x, double y, int datasetIndex, 160 double transX, double transY, PlotOrientation orientation) { 161 162 if (this.anchor != null) { 163 double d = 0.0; 164 if (this.calculateDistanceInDataSpace) { 165 d = (x - this.anchorX) * (x - this.anchorX) 166 + (y - this.anchorY) * (y - this.anchorY); 167 } 168 else { 169 // anchor point is in Java2D coordinates 170 double xx = this.anchor.getX(); 171 double yy = this.anchor.getY(); 172 if (orientation == PlotOrientation.HORIZONTAL) { 173 double temp = yy; 174 yy = xx; 175 xx = temp; 176 } 177 d = (transX - xx) * (transX - xx) 178 + (transY - yy) * (transY - yy); 179 } 180 181 if (d < this.distance) { 182 this.crosshairX = x; 183 this.crosshairY = y; 184 this.datasetIndex = datasetIndex; 185 this.distance = d; 186 } 187 } 188 189 } 190 191 /** 192 * Checks to see if the specified data point is the closest to the 193 * anchor point and, if yes, updates the current state. 194 * 195 * @param x the x-value. 196 * @param transX the x-value in Java2D space. 197 * @param datasetIndex the dataset index. 198 * 199 * @since 1.0.20 200 */ 201 public void updateCrosshairX(double x, double transX, int datasetIndex) { 202 if (this.anchor == null) { 203 return; 204 } 205 double d = Math.abs(transX - this.anchor.getX()); 206 if (d < this.distance) { 207 this.crosshairX = x; 208 this.datasetIndex = datasetIndex; 209 this.distance = d; 210 } 211 } 212 213 /** 214 * Evaluates a y-value and if it is the closest to the anchor y-value it 215 * becomes the new crosshair value. 216 * <P> 217 * Used in cases where only the y-axis is numerical. 218 * 219 * @param candidateY y position of the candidate for the new crosshair 220 * point. 221 * @param transY the y-value in Java2D space. 222 * @param datasetIndex the index of the range axis for this y-value. 223 * 224 * @since 1.0.20 225 */ 226 public void updateCrosshairY(double candidateY, double transY, int datasetIndex) { 227 if (this.anchor == null) { 228 return; 229 } 230 double d = Math.abs(transY - this.anchor.getY()); 231 if (d < this.distance) { 232 this.crosshairY = candidateY; 233 this.datasetIndex = datasetIndex; 234 this.distance = d; 235 } 236 237 } 238 239 /** 240 * Returns the anchor point. 241 * 242 * @return The anchor point. 243 * 244 * @see #setAnchor(Point2D) 245 * 246 * @since 1.0.3 247 */ 248 public Point2D getAnchor() { 249 return this.anchor; 250 } 251 252 /** 253 * Sets the anchor point. This is usually the mouse click point in a chart 254 * panel, and the crosshair point will often be the data item that is 255 * closest to the anchor point. 256 * <br><br> 257 * Note that the x and y coordinates (in data space) are not updated by 258 * this method - the caller is responsible for ensuring that this happens 259 * in sync. 260 * 261 * @param anchor the anchor point ({@code null} permitted). 262 * 263 * @see #getAnchor() 264 */ 265 public void setAnchor(Point2D anchor) { 266 this.anchor = anchor; 267 } 268 269 /** 270 * Returns the x-coordinate (in data space) for the anchor point. 271 * 272 * @return The x-coordinate of the anchor point. 273 * 274 * @since 1.0.3 275 */ 276 public double getAnchorX() { 277 return this.anchorX; 278 } 279 280 /** 281 * Sets the x-coordinate (in data space) for the anchor point. Note that 282 * this does NOT update the anchor itself - the caller is responsible for 283 * ensuring this is done in sync. 284 * 285 * @param x the x-coordinate. 286 * 287 * @since 1.0.3 288 */ 289 public void setAnchorX(double x) { 290 this.anchorX = x; 291 } 292 293 /** 294 * Returns the y-coordinate (in data space) for the anchor point. 295 * 296 * @return The y-coordinate of teh anchor point. 297 * 298 * @since 1.0.3 299 */ 300 public double getAnchorY() { 301 return this.anchorY; 302 } 303 304 /** 305 * Sets the y-coordinate (in data space) for the anchor point. Note that 306 * this does NOT update the anchor itself - the caller is responsible for 307 * ensuring this is done in sync. 308 * 309 * @param y the y-coordinate. 310 * 311 * @since 1.0.3 312 */ 313 public void setAnchorY(double y) { 314 this.anchorY = y; 315 } 316 317 /** 318 * Get the x-value for the crosshair point. 319 * 320 * @return The x position of the crosshair point. 321 * 322 * @see #setCrosshairX(double) 323 */ 324 public double getCrosshairX() { 325 return this.crosshairX; 326 } 327 328 /** 329 * Sets the x coordinate for the crosshair. This is the coordinate in data 330 * space measured against the domain axis. 331 * 332 * @param x the coordinate. 333 * 334 * @see #getCrosshairX() 335 * @see #setCrosshairY(double) 336 * @see #updateCrosshairPoint(double, double, int, double, double, 337 * PlotOrientation) 338 */ 339 public void setCrosshairX(double x) { 340 this.crosshairX = x; 341 } 342 343 /** 344 * Get the y-value for the crosshair point. This is the coordinate in data 345 * space measured against the range axis. 346 * 347 * @return The y position of the crosshair point. 348 * 349 * @see #setCrosshairY(double) 350 */ 351 public double getCrosshairY() { 352 return this.crosshairY; 353 } 354 355 /** 356 * Sets the y coordinate for the crosshair. 357 * 358 * @param y the y coordinate. 359 * 360 * @see #getCrosshairY() 361 * @see #setCrosshairX(double) 362 * @see #updateCrosshairPoint(double, double, int, double, double, 363 * PlotOrientation) 364 */ 365 public void setCrosshairY(double y) { 366 this.crosshairY = y; 367 } 368 369 /** 370 * Returns the dataset index that the crosshair values relate to. The 371 * dataset is mapped to specific axes, and this is how the crosshairs are 372 * mapped also. 373 * 374 * @return The dataset index. 375 * 376 * @see #setDatasetIndex(int) 377 * 378 * @since 1.0.11 379 */ 380 public int getDatasetIndex() { 381 return this.datasetIndex; 382 } 383 384 /** 385 * Sets the dataset index that the current crosshair values relate to. 386 * 387 * @param index the dataset index. 388 * 389 * @see #getDatasetIndex() 390 * 391 * @since 1.0.11 392 */ 393 public void setDatasetIndex(int index) { 394 this.datasetIndex = index; 395 } 396}