001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2014, 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 * ContourPlot.java 029 * ---------------- 030 * (C) Copyright 2002-2014, by David M. O'Donnell and Contributors. 031 * 032 * Original Author: David M. O'Donnell; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Arnaud Lelievre; 035 * Nicolas Brodu; 036 * 037 * Changes 038 * ------- 039 * 26-Nov-2002 : Version 1 contributed by David M. O'Donnell (DG); 040 * 14-Jan-2003 : Added crosshair attributes (DG); 041 * 23-Jan-2003 : Removed two constructors (DG); 042 * 21-Mar-2003 : Bug fix 701744 (DG); 043 * 26-Mar-2003 : Implemented Serializable (DG); 044 * 09-Jul-2003 : Changed ColorBar from extending axis classes to enclosing 045 * them (DG); 046 * 05-Aug-2003 : Applied changes in bug report 780298 (DG); 047 * 08-Sep-2003 : Added internationalization via use of properties 048 * resourceBundle (RFE 690236) (AL); 049 * 11-Sep-2003 : Cloning support (NB); 050 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 051 * 17-Jan-2004 : Removed references to DefaultContourDataset class, replaced 052 * with ContourDataset interface (with changes to the interface). 053 * See bug 741048 (DG); 054 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG); 055 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG); 056 * 06-Oct-2004 : Updated for changes in DatasetUtilities class (DG); 057 * 11-Nov-2004 : Renamed zoom methods to match ValueAxisPlot interface (DG); 058 * 25-Nov-2004 : Small update to clone() implementation (DG); 059 * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG); 060 * 05-May-2005 : Updated draw() method parameters (DG); 061 * 16-Jun-2005 : Added default constructor (DG); 062 * 01-Sep-2005 : Moved dataAreaRatio from Plot to here (DG); 063 * ------------- JFREECHART 1.0.x --------------------------------------------- 064 * 31-Jan-2007 : Deprecated (DG); 065 * 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by 066 * Jess Thrysoee (DG); 067 * 19-May-2009 : Fixed FindBugs warnings, patch by Michal Wozniak (DG); 068 * 02-Jul-2013 : Fix NB warnings (DG); 069 * 070 */ 071 072package org.jfree.chart.plot; 073 074import java.awt.AlphaComposite; 075import java.awt.Composite; 076import java.awt.Graphics2D; 077import java.awt.Paint; 078import java.awt.RenderingHints; 079import java.awt.Shape; 080import java.awt.Stroke; 081import java.awt.geom.Ellipse2D; 082import java.awt.geom.GeneralPath; 083import java.awt.geom.Line2D; 084import java.awt.geom.Point2D; 085import java.awt.geom.Rectangle2D; 086import java.awt.geom.RectangularShape; 087import java.beans.PropertyChangeEvent; 088import java.beans.PropertyChangeListener; 089import java.io.Serializable; 090import java.util.Iterator; 091import java.util.List; 092import java.util.ResourceBundle; 093 094import org.jfree.chart.ClipPath; 095import org.jfree.chart.annotations.XYAnnotation; 096import org.jfree.chart.axis.AxisSpace; 097import org.jfree.chart.axis.ColorBar; 098import org.jfree.chart.axis.NumberAxis; 099import org.jfree.chart.axis.ValueAxis; 100import org.jfree.chart.entity.ContourEntity; 101import org.jfree.chart.entity.EntityCollection; 102import org.jfree.chart.event.AxisChangeEvent; 103import org.jfree.chart.event.PlotChangeEvent; 104import org.jfree.chart.labels.ContourToolTipGenerator; 105import org.jfree.chart.labels.StandardContourToolTipGenerator; 106import org.jfree.chart.renderer.xy.XYBlockRenderer; 107import org.jfree.chart.urls.XYURLGenerator; 108import org.jfree.chart.util.ResourceBundleWrapper; 109import org.jfree.data.Range; 110import org.jfree.data.contour.ContourDataset; 111import org.jfree.data.general.DatasetChangeEvent; 112import org.jfree.data.general.DatasetUtilities; 113import org.jfree.ui.RectangleEdge; 114import org.jfree.ui.RectangleInsets; 115import org.jfree.util.ObjectUtilities; 116 117/** 118 * A class for creating shaded contours. 119 * 120 * @deprecated This plot is no longer supported, please use {@link XYPlot} with 121 * an {@link XYBlockRenderer}. 122 */ 123public class ContourPlot extends Plot implements ContourValuePlot, 124 ValueAxisPlot, PropertyChangeListener, Serializable, Cloneable { 125 126 /** For serialization. */ 127 private static final long serialVersionUID = 7861072556590502247L; 128 129 /** The default insets. */ 130 protected static final RectangleInsets DEFAULT_INSETS 131 = new RectangleInsets(2.0, 2.0, 100.0, 10.0); 132 133 /** The domain axis (used for the x-values). */ 134 private ValueAxis domainAxis; 135 136 /** The range axis (used for the y-values). */ 137 private ValueAxis rangeAxis; 138 139 /** The dataset. */ 140 private ContourDataset dataset; 141 142 /** The colorbar axis (used for the z-values). */ 143 private ColorBar colorBar = null; 144 145 /** The color bar location. */ 146 private RectangleEdge colorBarLocation; 147 148 /** A flag that controls whether or not a domain crosshair is drawn..*/ 149 private boolean domainCrosshairVisible; 150 151 /** The domain crosshair value. */ 152 private double domainCrosshairValue; 153 154 /** The pen/brush used to draw the crosshair (if any). */ 155 private transient Stroke domainCrosshairStroke; 156 157 /** The color used to draw the crosshair (if any). */ 158 private transient Paint domainCrosshairPaint; 159 160 /** 161 * A flag that controls whether or not the crosshair locks onto actual data 162 * points. 163 */ 164 private boolean domainCrosshairLockedOnData = true; 165 166 /** A flag that controls whether or not a range crosshair is drawn..*/ 167 private boolean rangeCrosshairVisible; 168 169 /** The range crosshair value. */ 170 private double rangeCrosshairValue; 171 172 /** The pen/brush used to draw the crosshair (if any). */ 173 private transient Stroke rangeCrosshairStroke; 174 175 /** The color used to draw the crosshair (if any). */ 176 private transient Paint rangeCrosshairPaint; 177 178 /** 179 * A flag that controls whether or not the crosshair locks onto actual data 180 * points. 181 */ 182 private boolean rangeCrosshairLockedOnData = true; 183 184 /** 185 * Defines dataArea rectangle as the ratio formed from dividing height by 186 * width (of the dataArea). Modifies plot area calculations. 187 * ratio > 0 will attempt to layout the plot so that the 188 * dataArea.height/dataArea.width = ratio. 189 * ratio < 0 will attempt to layout the plot so that the 190 * dataArea.height/dataArea.width in plot units (not java2D units as when 191 * ratio > 0) = -1.*ratio. 192 */ //dmo 193 private double dataAreaRatio = 0.0; //zero when the parameter is not set 194 195 /** A list of markers (optional) for the domain axis. */ 196 private List domainMarkers; 197 198 /** A list of markers (optional) for the range axis. */ 199 private List rangeMarkers; 200 201 /** A list of annotations (optional) for the plot. */ 202 private List annotations; 203 204 /** The tool tip generator. */ 205 private ContourToolTipGenerator toolTipGenerator; 206 207 /** The URL text generator. */ 208 private XYURLGenerator urlGenerator; 209 210 /** 211 * Controls whether data are render as filled rectangles or rendered as 212 * points 213 */ 214 private boolean renderAsPoints = false; 215 216 /** 217 * Size of points rendered when renderAsPoints = true. Size is relative to 218 * dataArea 219 */ 220 private double ptSizePct = 0.05; 221 222 /** Contains the a ClipPath to "trim" the contours. */ 223 private transient ClipPath clipPath = null; 224 225 /** Set to Paint to represent missing values. */ 226 private transient Paint missingPaint = null; 227 228 /** The resourceBundle for the localization. */ 229 protected static ResourceBundle localizationResources 230 = ResourceBundleWrapper.getBundle( 231 "org.jfree.chart.plot.LocalizationBundle"); 232 233 /** 234 * Creates a new plot with no dataset or axes. 235 */ 236 public ContourPlot() { 237 this(null, null, null, null); 238 } 239 240 /** 241 * Constructs a contour plot with the specified axes (other attributes take 242 * default values). 243 * 244 * @param dataset The dataset. 245 * @param domainAxis The domain axis. 246 * @param rangeAxis The range axis. 247 * @param colorBar The z-axis axis. 248 */ 249 public ContourPlot(ContourDataset dataset, 250 ValueAxis domainAxis, ValueAxis rangeAxis, 251 ColorBar colorBar) { 252 253 super(); 254 255 this.dataset = dataset; 256 if (dataset != null) { 257 dataset.addChangeListener(this); 258 } 259 260 this.domainAxis = domainAxis; 261 if (domainAxis != null) { 262 domainAxis.setPlot(this); 263 domainAxis.addChangeListener(this); 264 } 265 266 this.rangeAxis = rangeAxis; 267 if (rangeAxis != null) { 268 rangeAxis.setPlot(this); 269 rangeAxis.addChangeListener(this); 270 } 271 272 this.colorBar = colorBar; 273 if (colorBar != null) { 274 colorBar.getAxis().setPlot(this); 275 colorBar.getAxis().addChangeListener(this); 276 colorBar.configure(this); 277 } 278 this.colorBarLocation = RectangleEdge.LEFT; 279 280 this.toolTipGenerator = new StandardContourToolTipGenerator(); 281 282 } 283 284 /** 285 * Returns the color bar location. 286 * 287 * @return The color bar location. 288 */ 289 public RectangleEdge getColorBarLocation() { 290 return this.colorBarLocation; 291 } 292 293 /** 294 * Sets the color bar location and sends a {@link PlotChangeEvent} to all 295 * registered listeners. 296 * 297 * @param edge the location. 298 */ 299 public void setColorBarLocation(RectangleEdge edge) { 300 this.colorBarLocation = edge; 301 fireChangeEvent(); 302 } 303 304 /** 305 * Returns the primary dataset for the plot. 306 * 307 * @return The primary dataset (possibly <code>null</code>). 308 */ 309 public ContourDataset getDataset() { 310 return this.dataset; 311 } 312 313 /** 314 * Sets the dataset for the plot, replacing the existing dataset if there 315 * is one. 316 * 317 * @param dataset the dataset (<code>null</code> permitted). 318 */ 319 public void setDataset(ContourDataset dataset) { 320 321 // if there is an existing dataset, remove the plot from the list of 322 // change listeners... 323 ContourDataset existing = this.dataset; 324 if (existing != null) { 325 existing.removeChangeListener(this); 326 } 327 328 // set the new dataset, and register the chart as a change listener... 329 this.dataset = dataset; 330 if (dataset != null) { 331 setDatasetGroup(dataset.getGroup()); 332 dataset.addChangeListener(this); 333 } 334 335 // send a dataset change event to self... 336 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); 337 datasetChanged(event); 338 339 } 340 341 /** 342 * Returns the domain axis for the plot. 343 * 344 * @return The domain axis. 345 */ 346 public ValueAxis getDomainAxis() { 347 348 ValueAxis result = this.domainAxis; 349 350 return result; 351 352 } 353 354 /** 355 * Sets the domain axis for the plot (this must be compatible with the plot 356 * type or an exception is thrown). 357 * 358 * @param axis The new axis. 359 */ 360 public void setDomainAxis(ValueAxis axis) { 361 362 if (isCompatibleDomainAxis(axis)) { 363 364 if (axis != null) { 365 axis.setPlot(this); 366 axis.addChangeListener(this); 367 } 368 369 // plot is likely registered as a listener with the existing axis... 370 if (this.domainAxis != null) { 371 this.domainAxis.removeChangeListener(this); 372 } 373 374 this.domainAxis = axis; 375 fireChangeEvent(); 376 377 } 378 379 } 380 381 /** 382 * Returns the range axis for the plot. 383 * 384 * @return The range axis. 385 */ 386 public ValueAxis getRangeAxis() { 387 388 ValueAxis result = this.rangeAxis; 389 390 return result; 391 392 } 393 394 /** 395 * Sets the range axis for the plot. 396 * <P> 397 * An exception is thrown if the new axis and the plot are not mutually 398 * compatible. 399 * 400 * @param axis The new axis (null permitted). 401 */ 402 public void setRangeAxis(ValueAxis axis) { 403 404 if (axis != null) { 405 axis.setPlot(this); 406 axis.addChangeListener(this); 407 } 408 409 // plot is likely registered as a listener with the existing axis... 410 if (this.rangeAxis != null) { 411 this.rangeAxis.removeChangeListener(this); 412 } 413 414 this.rangeAxis = axis; 415 fireChangeEvent(); 416 417 } 418 419 /** 420 * Sets the colorbar for the plot. 421 * 422 * @param axis The new axis (null permitted). 423 */ 424 public void setColorBarAxis(ColorBar axis) { 425 426 this.colorBar = axis; 427 fireChangeEvent(); 428 429 } 430 431 /** 432 * Returns the data area ratio. 433 * 434 * @return The ratio. 435 */ 436 public double getDataAreaRatio() { 437 return this.dataAreaRatio; 438 } 439 440 /** 441 * Sets the data area ratio. 442 * 443 * @param ratio the ratio. 444 */ 445 public void setDataAreaRatio(double ratio) { 446 this.dataAreaRatio = ratio; 447 } 448 449 /** 450 * Adds a marker for the domain axis. 451 * <P> 452 * Typically a marker will be drawn by the renderer as a line perpendicular 453 * to the range axis, however this is entirely up to the renderer. 454 * 455 * @param marker the marker. 456 */ 457 public void addDomainMarker(Marker marker) { 458 459 if (this.domainMarkers == null) { 460 this.domainMarkers = new java.util.ArrayList(); 461 } 462 this.domainMarkers.add(marker); 463 fireChangeEvent(); 464 465 } 466 467 /** 468 * Clears all the domain markers. 469 */ 470 public void clearDomainMarkers() { 471 if (this.domainMarkers != null) { 472 this.domainMarkers.clear(); 473 fireChangeEvent(); 474 } 475 } 476 477 /** 478 * Adds a marker for the range axis. 479 * <P> 480 * Typically a marker will be drawn by the renderer as a line perpendicular 481 * to the range axis, however this is entirely up to the renderer. 482 * 483 * @param marker The marker. 484 */ 485 public void addRangeMarker(Marker marker) { 486 487 if (this.rangeMarkers == null) { 488 this.rangeMarkers = new java.util.ArrayList(); 489 } 490 this.rangeMarkers.add(marker); 491 fireChangeEvent(); 492 493 } 494 495 /** 496 * Clears all the range markers. 497 */ 498 public void clearRangeMarkers() { 499 if (this.rangeMarkers != null) { 500 this.rangeMarkers.clear(); 501 fireChangeEvent(); 502 } 503 } 504 505 /** 506 * Adds an annotation to the plot. 507 * 508 * @param annotation the annotation. 509 */ 510 public void addAnnotation(XYAnnotation annotation) { 511 512 if (this.annotations == null) { 513 this.annotations = new java.util.ArrayList(); 514 } 515 this.annotations.add(annotation); 516 fireChangeEvent(); 517 518 } 519 520 /** 521 * Clears all the annotations. 522 */ 523 public void clearAnnotations() { 524 if (this.annotations != null) { 525 this.annotations.clear(); 526 fireChangeEvent(); 527 } 528 } 529 530 /** 531 * Checks the compatibility of a domain axis, returning true if the axis is 532 * compatible with the plot, and false otherwise. 533 * 534 * @param axis The proposed axis. 535 * 536 * @return <code>true</code> if the axis is compatible with the plot. 537 */ 538 public boolean isCompatibleDomainAxis(ValueAxis axis) { 539 540 return true; 541 542 } 543 544 /** 545 * Draws the plot on a Java 2D graphics device (such as the screen or a 546 * printer). 547 * <P> 548 * The optional <code>info</code> argument collects information about the 549 * rendering of the plot (dimensions, tooltip information etc). Just pass 550 * in <code>null</code> if you do not need this information. 551 * 552 * @param g2 the graphics device. 553 * @param area the area within which the plot (including axis labels) 554 * should be drawn. 555 * @param anchor the anchor point (<code>null</code> permitted). 556 * @param parentState the state from the parent plot, if there is one. 557 * @param info collects chart drawing information (<code>null</code> 558 * permitted). 559 */ 560 @Override 561 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 562 PlotState parentState, PlotRenderingInfo info) { 563 564 // if the plot area is too small, just return... 565 boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); 566 boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); 567 if (b1 || b2) { 568 return; 569 } 570 571 // record the plot area... 572 if (info != null) { 573 info.setPlotArea(area); 574 } 575 576 // adjust the drawing area for plot insets (if any)... 577 RectangleInsets insets = getInsets(); 578 insets.trim(area); 579 580 AxisSpace space = new AxisSpace(); 581 582 space = this.domainAxis.reserveSpace(g2, this, area, 583 RectangleEdge.BOTTOM, space); 584 space = this.rangeAxis.reserveSpace(g2, this, area, 585 RectangleEdge.LEFT, space); 586 587 Rectangle2D estimatedDataArea = space.shrink(area, null); 588 589 AxisSpace space2 = new AxisSpace(); 590 space2 = this.colorBar.reserveSpace(g2, this, area, estimatedDataArea, 591 this.colorBarLocation, space2); 592 Rectangle2D adjustedPlotArea = space2.shrink(area, null); 593 594 Rectangle2D dataArea = space.shrink(adjustedPlotArea, null); 595 596 Rectangle2D colorBarArea = space2.reserved(area, this.colorBarLocation); 597 598 // additional dataArea modifications 599 if (getDataAreaRatio() != 0.0) { //check whether modification is 600 double ratio = getDataAreaRatio(); 601 Rectangle2D tmpDataArea = (Rectangle2D) dataArea.clone(); 602 double h = tmpDataArea.getHeight(); 603 double w = tmpDataArea.getWidth(); 604 605 if (ratio > 0) { // ratio represents pixels 606 if (w * ratio <= h) { 607 h = ratio * w; 608 } 609 else { 610 w = h / ratio; 611 } 612 } 613 else { // ratio represents axis units 614 ratio *= -1.0; 615 double xLength = getDomainAxis().getRange().getLength(); 616 double yLength = getRangeAxis().getRange().getLength(); 617 double unitRatio = yLength / xLength; 618 619 ratio = unitRatio * ratio; 620 621 if (w * ratio <= h) { 622 h = ratio * w; 623 } 624 else { 625 w = h / ratio; 626 } 627 } 628 629 dataArea.setRect(tmpDataArea.getX() + tmpDataArea.getWidth() / 2 630 - w / 2, tmpDataArea.getY(), w, h); 631 } 632 633 if (info != null) { 634 info.setDataArea(dataArea); 635 } 636 637 CrosshairState crosshairState = new CrosshairState(); 638 crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY); 639 640 // draw the plot background... 641 drawBackground(g2, dataArea); 642 643 double cursor = dataArea.getMaxY(); 644 if (this.domainAxis != null) { 645 this.domainAxis.draw(g2, cursor, adjustedPlotArea, dataArea, 646 RectangleEdge.BOTTOM, info); 647 } 648 649 if (this.rangeAxis != null) { 650 cursor = dataArea.getMinX(); 651 this.rangeAxis.draw(g2, cursor, adjustedPlotArea, dataArea, 652 RectangleEdge.LEFT, info); 653 } 654 655 if (this.colorBar != null) { 656 cursor = 0.0; 657 this.colorBar.draw(g2, cursor, adjustedPlotArea, dataArea, 658 colorBarArea, this.colorBarLocation); 659 } 660 Shape originalClip = g2.getClip(); 661 Composite originalComposite = g2.getComposite(); 662 663 g2.clip(dataArea); 664 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 665 getForegroundAlpha())); 666 render(g2, dataArea, info, crosshairState); 667 668 if (this.domainMarkers != null) { 669 Iterator iterator = this.domainMarkers.iterator(); 670 while (iterator.hasNext()) { 671 Marker marker = (Marker) iterator.next(); 672 drawDomainMarker(g2, this, getDomainAxis(), marker, dataArea); 673 } 674 } 675 676 if (this.rangeMarkers != null) { 677 Iterator iterator = this.rangeMarkers.iterator(); 678 while (iterator.hasNext()) { 679 Marker marker = (Marker) iterator.next(); 680 drawRangeMarker(g2, this, getRangeAxis(), marker, dataArea); 681 } 682 } 683 684// TO DO: these annotations only work with XYPlot, see if it is possible to 685// make ContourPlot a subclass of XYPlot (DG); 686 687// // draw the annotations... 688// if (this.annotations != null) { 689// Iterator iterator = this.annotations.iterator(); 690// while (iterator.hasNext()) { 691// Annotation annotation = (Annotation) iterator.next(); 692// if (annotation instanceof XYAnnotation) { 693// XYAnnotation xya = (XYAnnotation) annotation; 694// // get the annotation to draw itself... 695// xya.draw(g2, this, dataArea, getDomainAxis(), 696// getRangeAxis()); 697// } 698// } 699// } 700 701 g2.setClip(originalClip); 702 g2.setComposite(originalComposite); 703 drawOutline(g2, dataArea); 704 705 } 706 707 /** 708 * Draws a representation of the data within the dataArea region, using the 709 * current renderer. 710 * <P> 711 * The <code>info</code> and <code>crosshairState</code> arguments may be 712 * <code>null</code>. 713 * 714 * @param g2 the graphics device. 715 * @param dataArea the region in which the data is to be drawn. 716 * @param info an optional object for collection dimension information. 717 * @param crosshairState an optional object for collecting crosshair info. 718 */ 719 public void render(Graphics2D g2, Rectangle2D dataArea, 720 PlotRenderingInfo info, CrosshairState crosshairState) { 721 722 // now get the data and plot it (the visual representation will depend 723 // on the renderer that has been set)... 724 ContourDataset data = getDataset(); 725 if (data != null) { 726 727 ColorBar zAxis = getColorBar(); 728 729 if (this.clipPath != null) { 730 GeneralPath clipper = getClipPath().draw(g2, dataArea, 731 this.domainAxis, this.rangeAxis); 732 if (this.clipPath.isClip()) { 733 g2.clip(clipper); 734 } 735 } 736 737 if (this.renderAsPoints) { 738 pointRenderer(g2, dataArea, info, this, this.domainAxis, 739 this.rangeAxis, zAxis, data, crosshairState); 740 } 741 else { 742 contourRenderer(g2, dataArea, info, this, this.domainAxis, 743 this.rangeAxis, zAxis, data, crosshairState); 744 } 745 746 // draw vertical crosshair if required... 747 setDomainCrosshairValue(crosshairState.getCrosshairX(), false); 748 if (isDomainCrosshairVisible()) { 749 drawVerticalLine(g2, dataArea, 750 getDomainCrosshairValue(), 751 getDomainCrosshairStroke(), 752 getDomainCrosshairPaint()); 753 } 754 755 // draw horizontal crosshair if required... 756 setRangeCrosshairValue(crosshairState.getCrosshairY(), false); 757 if (isRangeCrosshairVisible()) { 758 drawHorizontalLine(g2, dataArea, 759 getRangeCrosshairValue(), 760 getRangeCrosshairStroke(), 761 getRangeCrosshairPaint()); 762 } 763 764 } 765 else if (this.clipPath != null) { 766 getClipPath().draw(g2, dataArea, this.domainAxis, this.rangeAxis); 767 } 768 769 } 770 771 /** 772 * Fills the plot. 773 * 774 * @param g2 the graphics device. 775 * @param dataArea the area within which the data is being drawn. 776 * @param info collects information about the drawing. 777 * @param plot the plot (can be used to obtain standard color 778 * information etc). 779 * @param horizontalAxis the domain (horizontal) axis. 780 * @param verticalAxis the range (vertical) axis. 781 * @param colorBar the color bar axis. 782 * @param data the dataset. 783 * @param crosshairState information about crosshairs on a plot. 784 */ 785 public void contourRenderer(Graphics2D g2, 786 Rectangle2D dataArea, 787 PlotRenderingInfo info, 788 ContourPlot plot, 789 ValueAxis horizontalAxis, 790 ValueAxis verticalAxis, 791 ColorBar colorBar, 792 ContourDataset data, 793 CrosshairState crosshairState) { 794 795 // setup for collecting optional entity info... 796 Rectangle2D.Double entityArea; 797 EntityCollection entities = null; 798 if (info != null) { 799 entities = info.getOwner().getEntityCollection(); 800 } 801 802 Rectangle2D.Double rect; 803 rect = new Rectangle2D.Double(); 804 805 //turn off anti-aliasing when filling rectangles 806 Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 807 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 808 RenderingHints.VALUE_ANTIALIAS_OFF); 809 810 // get the data points 811 Number[] xNumber = data.getXValues(); 812 Number[] yNumber = data.getYValues(); 813 Number[] zNumber = data.getZValues(); 814 815 double[] x = new double[xNumber.length]; 816 double[] y = new double[yNumber.length]; 817 818 for (int i = 0; i < x.length; i++) { 819 x[i] = xNumber[i].doubleValue(); 820 y[i] = yNumber[i].doubleValue(); 821 } 822 823 int[] xIndex = data.indexX(); 824 int[] indexX = data.getXIndices(); 825 boolean vertInverted = ((NumberAxis) verticalAxis).isInverted(); 826 boolean horizInverted = false; 827 if (horizontalAxis instanceof NumberAxis) { 828 horizInverted = ((NumberAxis) horizontalAxis).isInverted(); 829 } 830 double transX = 0.0; 831 double transXm1; 832 double transXp1; 833 double transDXm1; 834 double transDXp1 = 0.0; 835 double transDX = 0.0; 836 double transY; 837 double transYm1; 838 double transYp1; 839 double transDYm1; 840 double transDYp1 = 0.0; 841 double transDY; 842 int iMax = xIndex[xIndex.length - 1]; 843 for (int k = 0; k < x.length; k++) { 844 int i = xIndex[k]; 845 if (indexX[i] == k) { // this is a new column 846 if (i == 0) { 847 transX = horizontalAxis.valueToJava2D(x[k], dataArea, 848 RectangleEdge.BOTTOM); 849 transXm1 = transX; 850 transXp1 = horizontalAxis.valueToJava2D( 851 x[indexX[i + 1]], dataArea, RectangleEdge.BOTTOM); 852 transDXm1 = Math.abs(0.5 * (transX - transXm1)); 853 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 854 } 855 else if (i == iMax) { 856 transX = horizontalAxis.valueToJava2D(x[k], dataArea, 857 RectangleEdge.BOTTOM); 858 transXm1 = horizontalAxis.valueToJava2D(x[indexX[i - 1]], 859 dataArea, RectangleEdge.BOTTOM); 860 transXp1 = transX; 861 transDXm1 = Math.abs(0.5 * (transX - transXm1)); 862 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 863 } 864 else { 865 transX = horizontalAxis.valueToJava2D(x[k], dataArea, 866 RectangleEdge.BOTTOM); 867 transXp1 = horizontalAxis.valueToJava2D(x[indexX[i + 1]], 868 dataArea, RectangleEdge.BOTTOM); 869 transDXm1 = transDXp1; 870 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 871 } 872 873 if (horizInverted) { 874 transX -= transDXp1; 875 } 876 else { 877 transX -= transDXm1; 878 } 879 880 transDX = transDXm1 + transDXp1; 881 882 transY = verticalAxis.valueToJava2D(y[k], dataArea, 883 RectangleEdge.LEFT); 884 transYm1 = transY; 885 if (k + 1 == y.length) { 886 continue; 887 } 888 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea, 889 RectangleEdge.LEFT); 890 transDYm1 = Math.abs(0.5 * (transY - transYm1)); 891 transDYp1 = Math.abs(0.5 * (transY - transYp1)); 892 } 893 else if ((i < indexX.length - 1 894 && indexX[i + 1] - 1 == k) || k == x.length - 1) { 895 // end of column 896 transY = verticalAxis.valueToJava2D(y[k], dataArea, 897 RectangleEdge.LEFT); 898 transYm1 = verticalAxis.valueToJava2D(y[k - 1], dataArea, 899 RectangleEdge.LEFT); 900 transYp1 = transY; 901 transDYm1 = Math.abs(0.5 * (transY - transYm1)); 902 transDYp1 = Math.abs(0.5 * (transY - transYp1)); 903 } 904 else { 905 transY = verticalAxis.valueToJava2D(y[k], dataArea, 906 RectangleEdge.LEFT); 907 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea, 908 RectangleEdge.LEFT); 909 transDYm1 = transDYp1; 910 transDYp1 = Math.abs(0.5 * (transY - transYp1)); 911 } 912 if (vertInverted) { 913 transY -= transDYm1; 914 } 915 else { 916 transY -= transDYp1; 917 } 918 919 transDY = transDYm1 + transDYp1; 920 921 rect.setRect(transX, transY, transDX, transDY); 922 if (zNumber[k] != null) { 923 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue())); 924 g2.fill(rect); 925 } 926 else if (this.missingPaint != null) { 927 g2.setPaint(this.missingPaint); 928 g2.fill(rect); 929 } 930 931 entityArea = rect; 932 933 // add an entity for the item... 934 if (entities != null) { 935 String tip = ""; 936 if (getToolTipGenerator() != null) { 937 tip = this.toolTipGenerator.generateToolTip(data, k); 938 } 939// Shape s = g2.getClip(); 940// if (s.contains(rect) || s.intersects(rect)) { 941 String url = null; 942 // if (getURLGenerator() != null) { //dmo: look at this later 943 // url = getURLGenerator().generateURL(data, series, item); 944 // } 945 // Unlike XYItemRenderer, we need to clone entityArea since it 946 // reused. 947 ContourEntity entity = new ContourEntity( 948 (Rectangle2D.Double) entityArea.clone(), tip, url); 949 entity.setIndex(k); 950 entities.add(entity); 951// } 952 } 953 954 // do we need to update the crosshair values? 955 if (plot.isDomainCrosshairLockedOnData()) { 956 if (plot.isRangeCrosshairLockedOnData()) { 957 // both axes 958 crosshairState.updateCrosshairPoint(x[k], y[k], transX, 959 transY, PlotOrientation.VERTICAL); 960 } 961 else { 962 // just the horizontal axis... 963 crosshairState.updateCrosshairX(transX); 964 } 965 } 966 else { 967 if (plot.isRangeCrosshairLockedOnData()) { 968 // just the vertical axis... 969 crosshairState.updateCrosshairY(transY); 970 } 971 } 972 } 973 974 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias); 975 976 } 977 978 /** 979 * Draws the visual representation of a single data item. 980 * 981 * @param g2 the graphics device. 982 * @param dataArea the area within which the data is being drawn. 983 * @param info collects information about the drawing. 984 * @param plot the plot (can be used to obtain standard color 985 * information etc). 986 * @param domainAxis the domain (horizontal) axis. 987 * @param rangeAxis the range (vertical) axis. 988 * @param colorBar the color bar axis. 989 * @param data the dataset. 990 * @param crosshairState information about crosshairs on a plot. 991 */ 992 public void pointRenderer(Graphics2D g2, 993 Rectangle2D dataArea, 994 PlotRenderingInfo info, 995 ContourPlot plot, 996 ValueAxis domainAxis, 997 ValueAxis rangeAxis, 998 ColorBar colorBar, 999 ContourDataset data, 1000 CrosshairState crosshairState) { 1001 1002 // setup for collecting optional entity info... 1003 RectangularShape entityArea; 1004 EntityCollection entities = null; 1005 if (info != null) { 1006 entities = info.getOwner().getEntityCollection(); 1007 } 1008 1009// Rectangle2D.Double rect = null; 1010// rect = new Rectangle2D.Double(); 1011 RectangularShape rect = new Ellipse2D.Double(); 1012 1013 1014 //turn off anti-aliasing when filling rectangles 1015 Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 1016 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 1017 RenderingHints.VALUE_ANTIALIAS_OFF); 1018 1019 // if (tooltips!=null) tooltips.clearToolTips(); // reset collection 1020 // get the data points 1021 Number[] xNumber = data.getXValues(); 1022 Number[] yNumber = data.getYValues(); 1023 Number[] zNumber = data.getZValues(); 1024 1025 double[] x = new double[xNumber.length]; 1026 double[] y = new double[yNumber.length]; 1027 1028 for (int i = 0; i < x.length; i++) { 1029 x[i] = xNumber[i].doubleValue(); 1030 y[i] = yNumber[i].doubleValue(); 1031 } 1032 1033 double transX; 1034 double transDX; 1035 double transY; 1036 double transDY; 1037 double size = dataArea.getWidth() * this.ptSizePct; 1038 for (int k = 0; k < x.length; k++) { 1039 1040 transX = domainAxis.valueToJava2D(x[k], dataArea, 1041 RectangleEdge.BOTTOM) - 0.5 * size; 1042 transY = rangeAxis.valueToJava2D(y[k], dataArea, RectangleEdge.LEFT) 1043 - 0.5 * size; 1044 transDX = size; 1045 transDY = size; 1046 1047 rect.setFrame(transX, transY, transDX, transDY); 1048 1049 if (zNumber[k] != null) { 1050 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue())); 1051 g2.fill(rect); 1052 } 1053 else if (this.missingPaint != null) { 1054 g2.setPaint(this.missingPaint); 1055 g2.fill(rect); 1056 } 1057 1058 1059 entityArea = rect; 1060 1061 // add an entity for the item... 1062 if (entities != null) { 1063 String tip = null; 1064 if (getToolTipGenerator() != null) { 1065 tip = this.toolTipGenerator.generateToolTip(data, k); 1066 } 1067 String url = null; 1068 // if (getURLGenerator() != null) { //dmo: look at this later 1069 // url = getURLGenerator().generateURL(data, series, item); 1070 // } 1071 // Unlike XYItemRenderer, we need to clone entityArea since it 1072 // reused. 1073 ContourEntity entity = new ContourEntity( 1074 (RectangularShape) entityArea.clone(), tip, url); 1075 entity.setIndex(k); 1076 entities.add(entity); 1077 } 1078 1079 // do we need to update the crosshair values? 1080 if (plot.isDomainCrosshairLockedOnData()) { 1081 if (plot.isRangeCrosshairLockedOnData()) { 1082 // both axes 1083 crosshairState.updateCrosshairPoint(x[k], y[k], transX, 1084 transY, PlotOrientation.VERTICAL); 1085 } 1086 else { 1087 // just the horizontal axis... 1088 crosshairState.updateCrosshairX(transX); 1089 } 1090 } 1091 else { 1092 if (plot.isRangeCrosshairLockedOnData()) { 1093 // just the vertical axis... 1094 crosshairState.updateCrosshairY(transY); 1095 } 1096 } 1097 } 1098 1099 1100 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias); 1101 1102 } 1103 1104 /** 1105 * Utility method for drawing a crosshair on the chart (if required). 1106 * 1107 * @param g2 The graphics device. 1108 * @param dataArea The data area. 1109 * @param value The coordinate, where to draw the line. 1110 * @param stroke The stroke to use. 1111 * @param paint The paint to use. 1112 */ 1113 protected void drawVerticalLine(Graphics2D g2, Rectangle2D dataArea, 1114 double value, Stroke stroke, Paint paint) { 1115 1116 double xx = getDomainAxis().valueToJava2D(value, dataArea, 1117 RectangleEdge.BOTTOM); 1118 Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx, 1119 dataArea.getMaxY()); 1120 g2.setStroke(stroke); 1121 g2.setPaint(paint); 1122 g2.draw(line); 1123 1124 } 1125 1126 /** 1127 * Utility method for drawing a crosshair on the chart (if required). 1128 * 1129 * @param g2 The graphics device. 1130 * @param dataArea The data area. 1131 * @param value The coordinate, where to draw the line. 1132 * @param stroke The stroke to use. 1133 * @param paint The paint to use. 1134 */ 1135 protected void drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea, 1136 double value, Stroke stroke, 1137 Paint paint) { 1138 1139 double yy = getRangeAxis().valueToJava2D(value, dataArea, 1140 RectangleEdge.LEFT); 1141 Line2D line = new Line2D.Double(dataArea.getMinX(), yy, 1142 dataArea.getMaxX(), yy); 1143 g2.setStroke(stroke); 1144 g2.setPaint(paint); 1145 g2.draw(line); 1146 1147 } 1148 1149 /** 1150 * Handles a 'click' on the plot by updating the anchor values... 1151 * 1152 * @param x x-coordinate, where the click occured. 1153 * @param y y-coordinate, where the click occured. 1154 * @param info An object for collection dimension information. 1155 */ 1156 @Override 1157 public void handleClick(int x, int y, PlotRenderingInfo info) { 1158 1159/* // set the anchor value for the horizontal axis... 1160 ValueAxis hva = getDomainAxis(); 1161 if (hva != null) { 1162 double hvalue = hva.translateJava2DtoValue( 1163 (float) x, info.getDataArea() 1164 ); 1165 1166 hva.setAnchorValue(hvalue); 1167 setDomainCrosshairValue(hvalue); 1168 } 1169 1170 // set the anchor value for the vertical axis... 1171 ValueAxis vva = getRangeAxis(); 1172 if (vva != null) { 1173 double vvalue = vva.translateJava2DtoValue( 1174 (float) y, info.getDataArea() 1175 ); 1176 vva.setAnchorValue(vvalue); 1177 setRangeCrosshairValue(vvalue); 1178 } 1179*/ 1180 } 1181 1182 /** 1183 * Zooms the axis ranges by the specified percentage about the anchor point. 1184 * 1185 * @param percent The amount of the zoom. 1186 */ 1187 @Override 1188 public void zoom(double percent) { 1189 1190 if (percent > 0) { 1191 // double range = this.domainAxis.getRange().getLength(); 1192 // double scaledRange = range * percent; 1193 // domainAxis.setAnchoredRange(scaledRange); 1194 1195 // range = this.rangeAxis.getRange().getLength(); 1196 // scaledRange = range * percent; 1197 // rangeAxis.setAnchoredRange(scaledRange); 1198 } 1199 else { 1200 getRangeAxis().setAutoRange(true); 1201 getDomainAxis().setAutoRange(true); 1202 } 1203 1204 } 1205 1206 /** 1207 * Returns the plot type as a string. 1208 * 1209 * @return A short string describing the type of plot. 1210 */ 1211 @Override 1212 public String getPlotType() { 1213 return localizationResources.getString("Contour_Plot"); 1214 } 1215 1216 /** 1217 * Returns the range for an axis. 1218 * 1219 * @param axis the axis. 1220 * 1221 * @return The range for an axis. 1222 */ 1223 @Override 1224 public Range getDataRange(ValueAxis axis) { 1225 1226 if (this.dataset == null) { 1227 return null; 1228 } 1229 1230 Range result = null; 1231 1232 if (axis == getDomainAxis()) { 1233 result = DatasetUtilities.findDomainBounds(this.dataset); 1234 } 1235 else if (axis == getRangeAxis()) { 1236 result = DatasetUtilities.findRangeBounds(this.dataset); 1237 } 1238 return result; 1239 } 1240 1241 /** 1242 * Returns the range for the Contours. 1243 * 1244 * @return The range for the Contours (z-axis). 1245 */ 1246 @Override 1247 public Range getContourDataRange() { 1248 Range result = null; 1249 ContourDataset data = getDataset(); 1250 if (data != null) { 1251 Range h = getDomainAxis().getRange(); 1252 Range v = getRangeAxis().getRange(); 1253 result = this.visibleRange(data, h, v); 1254 } 1255 return result; 1256 } 1257 1258 /** 1259 * Notifies all registered listeners of a property change. 1260 * <P> 1261 * One source of property change events is the plot's renderer. 1262 * 1263 * @param event Information about the property change. 1264 */ 1265 @Override 1266 public void propertyChange(PropertyChangeEvent event) { 1267 fireChangeEvent(); 1268 } 1269 1270 /** 1271 * Receives notification of a change to the plot's dataset. 1272 * <P> 1273 * The chart reacts by passing on a chart change event to all registered 1274 * listeners. 1275 * 1276 * @param event Information about the event (not used here). 1277 */ 1278 @Override 1279 public void datasetChanged(DatasetChangeEvent event) { 1280 if (this.domainAxis != null) { 1281 this.domainAxis.configure(); 1282 } 1283 if (this.rangeAxis != null) { 1284 this.rangeAxis.configure(); 1285 } 1286 if (this.colorBar != null) { 1287 this.colorBar.configure(this); 1288 } 1289 super.datasetChanged(event); 1290 } 1291 1292 /** 1293 * Returns the colorbar. 1294 * 1295 * @return The colorbar. 1296 */ 1297 public ColorBar getColorBar() { 1298 return this.colorBar; 1299 } 1300 1301 /** 1302 * Returns a flag indicating whether or not the domain crosshair is visible. 1303 * 1304 * @return The flag. 1305 */ 1306 public boolean isDomainCrosshairVisible() { 1307 return this.domainCrosshairVisible; 1308 } 1309 1310 /** 1311 * Sets the flag indicating whether or not the domain crosshair is visible. 1312 * 1313 * @param flag the new value of the flag. 1314 */ 1315 public void setDomainCrosshairVisible(boolean flag) { 1316 1317 if (this.domainCrosshairVisible != flag) { 1318 this.domainCrosshairVisible = flag; 1319 fireChangeEvent(); 1320 } 1321 1322 } 1323 1324 /** 1325 * Returns a flag indicating whether or not the crosshair should "lock-on" 1326 * to actual data values. 1327 * 1328 * @return The flag. 1329 */ 1330 public boolean isDomainCrosshairLockedOnData() { 1331 return this.domainCrosshairLockedOnData; 1332 } 1333 1334 /** 1335 * Sets the flag indicating whether or not the domain crosshair should 1336 * "lock-on" to actual data values. 1337 * 1338 * @param flag the flag. 1339 */ 1340 public void setDomainCrosshairLockedOnData(boolean flag) { 1341 if (this.domainCrosshairLockedOnData != flag) { 1342 this.domainCrosshairLockedOnData = flag; 1343 fireChangeEvent(); 1344 } 1345 } 1346 1347 /** 1348 * Returns the domain crosshair value. 1349 * 1350 * @return The value. 1351 */ 1352 public double getDomainCrosshairValue() { 1353 return this.domainCrosshairValue; 1354 } 1355 1356 /** 1357 * Sets the domain crosshair value. 1358 * <P> 1359 * Registered listeners are notified that the plot has been modified, but 1360 * only if the crosshair is visible. 1361 * 1362 * @param value the new value. 1363 */ 1364 public void setDomainCrosshairValue(double value) { 1365 setDomainCrosshairValue(value, true); 1366 } 1367 1368 /** 1369 * Sets the domain crosshair value. 1370 * <P> 1371 * Registered listeners are notified that the axis has been modified, but 1372 * only if the crosshair is visible. 1373 * 1374 * @param value the new value. 1375 * @param notify a flag that controls whether or not listeners are 1376 * notified. 1377 */ 1378 public void setDomainCrosshairValue(double value, boolean notify) { 1379 this.domainCrosshairValue = value; 1380 if (isDomainCrosshairVisible() && notify) { 1381 fireChangeEvent(); 1382 } 1383 } 1384 1385 /** 1386 * Returns the Stroke used to draw the crosshair (if visible). 1387 * 1388 * @return The crosshair stroke. 1389 */ 1390 public Stroke getDomainCrosshairStroke() { 1391 return this.domainCrosshairStroke; 1392 } 1393 1394 /** 1395 * Sets the Stroke used to draw the crosshairs (if visible) and notifies 1396 * registered listeners that the axis has been modified. 1397 * 1398 * @param stroke the new crosshair stroke. 1399 */ 1400 public void setDomainCrosshairStroke(Stroke stroke) { 1401 this.domainCrosshairStroke = stroke; 1402 fireChangeEvent(); 1403 } 1404 1405 /** 1406 * Returns the domain crosshair color. 1407 * 1408 * @return The crosshair color. 1409 */ 1410 public Paint getDomainCrosshairPaint() { 1411 return this.domainCrosshairPaint; 1412 } 1413 1414 /** 1415 * Sets the Paint used to color the crosshairs (if visible) and notifies 1416 * registered listeners that the axis has been modified. 1417 * 1418 * @param paint the new crosshair paint. 1419 */ 1420 public void setDomainCrosshairPaint(Paint paint) { 1421 this.domainCrosshairPaint = paint; 1422 fireChangeEvent(); 1423 } 1424 1425 /** 1426 * Returns a flag indicating whether or not the range crosshair is visible. 1427 * 1428 * @return The flag. 1429 */ 1430 public boolean isRangeCrosshairVisible() { 1431 return this.rangeCrosshairVisible; 1432 } 1433 1434 /** 1435 * Sets the flag indicating whether or not the range crosshair is visible. 1436 * 1437 * @param flag the new value of the flag. 1438 */ 1439 public void setRangeCrosshairVisible(boolean flag) { 1440 if (this.rangeCrosshairVisible != flag) { 1441 this.rangeCrosshairVisible = flag; 1442 fireChangeEvent(); 1443 } 1444 } 1445 1446 /** 1447 * Returns a flag indicating whether or not the crosshair should "lock-on" 1448 * to actual data values. 1449 * 1450 * @return The flag. 1451 */ 1452 public boolean isRangeCrosshairLockedOnData() { 1453 return this.rangeCrosshairLockedOnData; 1454 } 1455 1456 /** 1457 * Sets the flag indicating whether or not the range crosshair should 1458 * "lock-on" to actual data values. 1459 * 1460 * @param flag the flag. 1461 */ 1462 public void setRangeCrosshairLockedOnData(boolean flag) { 1463 if (this.rangeCrosshairLockedOnData != flag) { 1464 this.rangeCrosshairLockedOnData = flag; 1465 fireChangeEvent(); 1466 } 1467 } 1468 1469 /** 1470 * Returns the range crosshair value. 1471 * 1472 * @return The value. 1473 */ 1474 public double getRangeCrosshairValue() { 1475 return this.rangeCrosshairValue; 1476 } 1477 1478 /** 1479 * Sets the domain crosshair value. 1480 * <P> 1481 * Registered listeners are notified that the plot has been modified, but 1482 * only if the crosshair is visible. 1483 * 1484 * @param value the new value. 1485 */ 1486 public void setRangeCrosshairValue(double value) { 1487 setRangeCrosshairValue(value, true); 1488 } 1489 1490 /** 1491 * Sets the range crosshair value. 1492 * <P> 1493 * Registered listeners are notified that the axis has been modified, but 1494 * only if the crosshair is visible. 1495 * 1496 * @param value the new value. 1497 * @param notify a flag that controls whether or not listeners are 1498 * notified. 1499 */ 1500 public void setRangeCrosshairValue(double value, boolean notify) { 1501 this.rangeCrosshairValue = value; 1502 if (isRangeCrosshairVisible() && notify) { 1503 fireChangeEvent(); 1504 } 1505 } 1506 1507 /** 1508 * Returns the Stroke used to draw the crosshair (if visible). 1509 * 1510 * @return The crosshair stroke. 1511 */ 1512 public Stroke getRangeCrosshairStroke() { 1513 return this.rangeCrosshairStroke; 1514 } 1515 1516 /** 1517 * Sets the Stroke used to draw the crosshairs (if visible) and notifies 1518 * registered listeners that the axis has been modified. 1519 * 1520 * @param stroke the new crosshair stroke. 1521 */ 1522 public void setRangeCrosshairStroke(Stroke stroke) { 1523 this.rangeCrosshairStroke = stroke; 1524 fireChangeEvent(); 1525 } 1526 1527 /** 1528 * Returns the range crosshair color. 1529 * 1530 * @return The crosshair color. 1531 */ 1532 public Paint getRangeCrosshairPaint() { 1533 return this.rangeCrosshairPaint; 1534 } 1535 1536 /** 1537 * Sets the Paint used to color the crosshairs (if visible) and notifies 1538 * registered listeners that the axis has been modified. 1539 * 1540 * @param paint the new crosshair paint. 1541 */ 1542 public void setRangeCrosshairPaint(Paint paint) { 1543 this.rangeCrosshairPaint = paint; 1544 fireChangeEvent(); 1545 } 1546 1547 /** 1548 * Returns the tool tip generator. 1549 * 1550 * @return The tool tip generator (possibly null). 1551 */ 1552 public ContourToolTipGenerator getToolTipGenerator() { 1553 return this.toolTipGenerator; 1554 } 1555 1556 /** 1557 * Sets the tool tip generator. 1558 * 1559 * @param generator the tool tip generator (null permitted). 1560 */ 1561 public void setToolTipGenerator(ContourToolTipGenerator generator) { 1562 //Object oldValue = this.toolTipGenerator; 1563 this.toolTipGenerator = generator; 1564 } 1565 1566 /** 1567 * Returns the URL generator for HTML image maps. 1568 * 1569 * @return The URL generator (possibly null). 1570 */ 1571 public XYURLGenerator getURLGenerator() { 1572 return this.urlGenerator; 1573 } 1574 1575 /** 1576 * Sets the URL generator for HTML image maps. 1577 * 1578 * @param urlGenerator the URL generator (null permitted). 1579 */ 1580 public void setURLGenerator(XYURLGenerator urlGenerator) { 1581 //Object oldValue = this.urlGenerator; 1582 this.urlGenerator = urlGenerator; 1583 } 1584 1585 /** 1586 * Draws a vertical line on the chart to represent a 'range marker'. 1587 * 1588 * @param g2 the graphics device. 1589 * @param plot the plot. 1590 * @param domainAxis the domain axis. 1591 * @param marker the marker line. 1592 * @param dataArea the axis data area. 1593 */ 1594 public void drawDomainMarker(Graphics2D g2, 1595 ContourPlot plot, 1596 ValueAxis domainAxis, 1597 Marker marker, 1598 Rectangle2D dataArea) { 1599 1600 if (marker instanceof ValueMarker) { 1601 ValueMarker vm = (ValueMarker) marker; 1602 double value = vm.getValue(); 1603 Range range = domainAxis.getRange(); 1604 if (!range.contains(value)) { 1605 return; 1606 } 1607 1608 double x = domainAxis.valueToJava2D(value, dataArea, 1609 RectangleEdge.BOTTOM); 1610 Line2D line = new Line2D.Double(x, dataArea.getMinY(), x, 1611 dataArea.getMaxY()); 1612 Paint paint = marker.getOutlinePaint(); 1613 Stroke stroke = marker.getOutlineStroke(); 1614 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 1615 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 1616 g2.draw(line); 1617 } 1618 1619 } 1620 1621 /** 1622 * Draws a horizontal line across the chart to represent a 'range marker'. 1623 * 1624 * @param g2 the graphics device. 1625 * @param plot the plot. 1626 * @param rangeAxis the range axis. 1627 * @param marker the marker line. 1628 * @param dataArea the axis data area. 1629 */ 1630 public void drawRangeMarker(Graphics2D g2, 1631 ContourPlot plot, 1632 ValueAxis rangeAxis, 1633 Marker marker, 1634 Rectangle2D dataArea) { 1635 1636 if (marker instanceof ValueMarker) { 1637 ValueMarker vm = (ValueMarker) marker; 1638 double value = vm.getValue(); 1639 Range range = rangeAxis.getRange(); 1640 if (!range.contains(value)) { 1641 return; 1642 } 1643 1644 double y = rangeAxis.valueToJava2D(value, dataArea, 1645 RectangleEdge.LEFT); 1646 Line2D line = new Line2D.Double(dataArea.getMinX(), y, 1647 dataArea.getMaxX(), y); 1648 Paint paint = marker.getOutlinePaint(); 1649 Stroke stroke = marker.getOutlineStroke(); 1650 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 1651 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 1652 g2.draw(line); 1653 } 1654 1655 } 1656 1657 /** 1658 * Returns the clipPath. 1659 * @return ClipPath 1660 */ 1661 public ClipPath getClipPath() { 1662 return this.clipPath; 1663 } 1664 1665 /** 1666 * Sets the clipPath. 1667 * @param clipPath The clipPath to set 1668 */ 1669 public void setClipPath(ClipPath clipPath) { 1670 this.clipPath = clipPath; 1671 } 1672 1673 /** 1674 * Returns the ptSizePct. 1675 * @return double 1676 */ 1677 public double getPtSizePct() { 1678 return this.ptSizePct; 1679 } 1680 1681 /** 1682 * Returns the renderAsPoints. 1683 * @return boolean 1684 */ 1685 public boolean isRenderAsPoints() { 1686 return this.renderAsPoints; 1687 } 1688 1689 /** 1690 * Sets the ptSizePct. 1691 * @param ptSizePct The ptSizePct to set 1692 */ 1693 public void setPtSizePct(double ptSizePct) { 1694 this.ptSizePct = ptSizePct; 1695 } 1696 1697 /** 1698 * Sets the renderAsPoints. 1699 * @param renderAsPoints The renderAsPoints to set 1700 */ 1701 public void setRenderAsPoints(boolean renderAsPoints) { 1702 this.renderAsPoints = renderAsPoints; 1703 } 1704 1705 /** 1706 * Receives notification of a change to one of the plot's axes. 1707 * 1708 * @param event information about the event. 1709 */ 1710 @Override 1711 public void axisChanged(AxisChangeEvent event) { 1712 Object source = event.getSource(); 1713 if (source.equals(this.rangeAxis) || source.equals(this.domainAxis)) { 1714 ColorBar cba = this.colorBar; 1715 if (this.colorBar.getAxis().isAutoRange()) { 1716 cba.getAxis().configure(); 1717 } 1718 1719 } 1720 super.axisChanged(event); 1721 } 1722 1723 /** 1724 * Returns the visible z-range. 1725 * 1726 * @param data the dataset. 1727 * @param x the x range. 1728 * @param y the y range. 1729 * 1730 * @return The range. 1731 */ 1732 public Range visibleRange(ContourDataset data, Range x, Range y) { 1733 Range range = data.getZValueRange(x, y); 1734 return range; 1735 } 1736 1737 /** 1738 * Returns the missingPaint. 1739 * @return Paint 1740 */ 1741 public Paint getMissingPaint() { 1742 return this.missingPaint; 1743 } 1744 1745 /** 1746 * Sets the missingPaint. 1747 * 1748 * @param paint the missingPaint to set. 1749 */ 1750 public void setMissingPaint(Paint paint) { 1751 this.missingPaint = paint; 1752 } 1753 1754 /** 1755 * Multiplies the range on the domain axis/axes by the specified factor 1756 * (to be implemented). 1757 * 1758 * @param x the x-coordinate (in Java2D space). 1759 * @param y the y-coordinate (in Java2D space). 1760 * @param factor the zoom factor. 1761 */ 1762 public void zoomDomainAxes(double x, double y, double factor) { 1763 // TODO: to be implemented 1764 } 1765 1766 /** 1767 * Zooms the domain axes (not yet implemented). 1768 * 1769 * @param x the x-coordinate (in Java2D space). 1770 * @param y the y-coordinate (in Java2D space). 1771 * @param lowerPercent the new lower bound. 1772 * @param upperPercent the new upper bound. 1773 */ 1774 public void zoomDomainAxes(double x, double y, double lowerPercent, 1775 double upperPercent) { 1776 // TODO: to be implemented 1777 } 1778 1779 /** 1780 * Multiplies the range on the range axis/axes by the specified factor. 1781 * 1782 * @param x the x-coordinate (in Java2D space). 1783 * @param y the y-coordinate (in Java2D space). 1784 * @param factor the zoom factor. 1785 */ 1786 public void zoomRangeAxes(double x, double y, double factor) { 1787 // TODO: to be implemented 1788 } 1789 1790 /** 1791 * Zooms the range axes (not yet implemented). 1792 * 1793 * @param x the x-coordinate (in Java2D space). 1794 * @param y the y-coordinate (in Java2D space). 1795 * @param lowerPercent the new lower bound. 1796 * @param upperPercent the new upper bound. 1797 */ 1798 public void zoomRangeAxes(double x, double y, double lowerPercent, 1799 double upperPercent) { 1800 // TODO: to be implemented 1801 } 1802 1803 /** 1804 * Returns <code>false</code>. 1805 * 1806 * @return A boolean. 1807 */ 1808 public boolean isDomainZoomable() { 1809 return false; 1810 } 1811 1812 /** 1813 * Returns <code>false</code>. 1814 * 1815 * @return A boolean. 1816 */ 1817 public boolean isRangeZoomable() { 1818 return false; 1819 } 1820 1821 /** 1822 * Extends plot cloning to this plot type 1823 * @see org.jfree.chart.plot.Plot#clone() 1824 */ 1825 @Override 1826 public Object clone() throws CloneNotSupportedException { 1827 ContourPlot clone = (ContourPlot) super.clone(); 1828 1829 if (this.domainAxis != null) { 1830 clone.domainAxis = (ValueAxis) this.domainAxis.clone(); 1831 clone.domainAxis.setPlot(clone); 1832 clone.domainAxis.addChangeListener(clone); 1833 } 1834 if (this.rangeAxis != null) { 1835 clone.rangeAxis = (ValueAxis) this.rangeAxis.clone(); 1836 clone.rangeAxis.setPlot(clone); 1837 clone.rangeAxis.addChangeListener(clone); 1838 } 1839 1840 if (clone.dataset != null) { 1841 clone.dataset.addChangeListener(clone); 1842 } 1843 1844 if (this.colorBar != null) { 1845 clone.colorBar = (ColorBar) this.colorBar.clone(); 1846 } 1847 1848 clone.domainMarkers = (List) ObjectUtilities.deepClone( 1849 this.domainMarkers); 1850 clone.rangeMarkers = (List) ObjectUtilities.deepClone( 1851 this.rangeMarkers); 1852 clone.annotations = (List) ObjectUtilities.deepClone(this.annotations); 1853 1854 if (this.clipPath != null) { 1855 clone.clipPath = (ClipPath) this.clipPath.clone(); 1856 } 1857 1858 return clone; 1859 } 1860 1861}