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