001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ---------------- 028 * ContourPlot.java 029 * ---------------- 030 * (C) Copyright 2002-2007, 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 * 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.renderer.xy.XYBlockRenderer; 103 import org.jfree.chart.urls.XYURLGenerator; 104 import org.jfree.data.Range; 105 import org.jfree.data.contour.ContourDataset; 106 import org.jfree.data.general.DatasetChangeEvent; 107 import org.jfree.data.general.DatasetUtilities; 108 import org.jfree.ui.RectangleEdge; 109 import org.jfree.ui.RectangleInsets; 110 import org.jfree.util.ObjectUtilities; 111 112 /** 113 * A class for creating shaded contours. 114 * 115 * @deprecated This plot is no longer supported, please use {@link XYPlot} with 116 * an {@link XYBlockRenderer}. 117 */ 118 public class ContourPlot extends Plot implements ContourValuePlot, 119 ValueAxisPlot, 120 PropertyChangeListener, 121 Serializable, 122 Cloneable { 123 124 /** For serialization. */ 125 private static final long serialVersionUID = 7861072556590502247L; 126 127 /** The default insets. */ 128 protected static final RectangleInsets DEFAULT_INSETS 129 = new RectangleInsets(2.0, 2.0, 100.0, 10.0); 130 131 /** The domain axis (used for the x-values). */ 132 private ValueAxis domainAxis; 133 134 /** The range axis (used for the y-values). */ 135 private ValueAxis rangeAxis; 136 137 /** The dataset. */ 138 private ContourDataset dataset; 139 140 /** The colorbar axis (used for the z-values). */ 141 private ColorBar colorBar = null; 142 143 /** The color bar location. */ 144 private RectangleEdge colorBarLocation; 145 146 /** A flag that controls whether or not a domain crosshair is drawn..*/ 147 private boolean domainCrosshairVisible; 148 149 /** The domain crosshair value. */ 150 private double domainCrosshairValue; 151 152 /** The pen/brush used to draw the crosshair (if any). */ 153 private transient Stroke domainCrosshairStroke; 154 155 /** The color used to draw the crosshair (if any). */ 156 private transient Paint domainCrosshairPaint; 157 158 /** 159 * A flag that controls whether or not the crosshair locks onto actual data 160 * points. 161 */ 162 private boolean domainCrosshairLockedOnData = true; 163 164 /** A flag that controls whether or not a range crosshair is drawn..*/ 165 private boolean rangeCrosshairVisible; 166 167 /** The range crosshair value. */ 168 private double rangeCrosshairValue; 169 170 /** The pen/brush used to draw the crosshair (if any). */ 171 private transient Stroke rangeCrosshairStroke; 172 173 /** The color used to draw the crosshair (if any). */ 174 private transient Paint rangeCrosshairPaint; 175 176 /** 177 * A flag that controls whether or not the crosshair locks onto actual data 178 * points. 179 */ 180 private boolean rangeCrosshairLockedOnData = true; 181 182 /** 183 * Defines dataArea rectangle as the ratio formed from dividing height by 184 * width (of the dataArea). Modifies plot area calculations. 185 * ratio>0 will attempt to layout the plot so that the 186 * dataArea.height/dataArea.width = ratio. 187 * ratio<0 will attempt to layout the plot so that the 188 * dataArea.height/dataArea.width in plot units (not java2D units as when 189 * ratio>0) = -1.*ratio. 190 */ //dmo 191 private double dataAreaRatio = 0.0; //zero when the parameter is not set 192 193 /** A list of markers (optional) for the domain axis. */ 194 private List domainMarkers; 195 196 /** A list of markers (optional) for the range axis. */ 197 private List rangeMarkers; 198 199 /** A list of annotations (optional) for the plot. */ 200 private List annotations; 201 202 /** The tool tip generator. */ 203 private ContourToolTipGenerator toolTipGenerator; 204 205 /** The URL text generator. */ 206 private XYURLGenerator urlGenerator; 207 208 /** 209 * Controls whether data are render as filled rectangles or rendered as 210 * points 211 */ 212 private boolean renderAsPoints = false; 213 214 /** 215 * Size of points rendered when renderAsPoints = true. Size is relative to 216 * dataArea 217 */ 218 private double ptSizePct = 0.05; 219 220 /** Contains the a ClipPath to "trim" the contours. */ 221 private transient ClipPath clipPath = null; 222 223 /** Set to Paint to represent missing values. */ 224 private transient Paint missingPaint = null; 225 226 /** The resourceBundle for the localization. */ 227 protected static ResourceBundle localizationResources = 228 ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle"); 229 230 /** 231 * Creates a new plot with no dataset or axes. 232 */ 233 public ContourPlot() { 234 this(null, null, null, null); 235 } 236 237 /** 238 * Constructs a contour plot with the specified axes (other attributes take 239 * default values). 240 * 241 * @param dataset The dataset. 242 * @param domainAxis The domain axis. 243 * @param rangeAxis The range axis. 244 * @param colorBar The z-axis axis. 245 */ 246 public ContourPlot(ContourDataset dataset, 247 ValueAxis domainAxis, ValueAxis rangeAxis, 248 ColorBar colorBar) { 249 250 super(); 251 252 this.dataset = dataset; 253 if (dataset != null) { 254 dataset.addChangeListener(this); 255 } 256 257 this.domainAxis = domainAxis; 258 if (domainAxis != null) { 259 domainAxis.setPlot(this); 260 domainAxis.addChangeListener(this); 261 } 262 263 this.rangeAxis = rangeAxis; 264 if (rangeAxis != null) { 265 rangeAxis.setPlot(this); 266 rangeAxis.addChangeListener(this); 267 } 268 269 this.colorBar = colorBar; 270 if (colorBar != null) { 271 colorBar.getAxis().setPlot(this); 272 colorBar.getAxis().addChangeListener(this); 273 colorBar.configure(this); 274 } 275 this.colorBarLocation = RectangleEdge.LEFT; 276 277 this.toolTipGenerator = new StandardContourToolTipGenerator(); 278 279 } 280 281 /** 282 * Returns the color bar location. 283 * 284 * @return The color bar location. 285 */ 286 public RectangleEdge getColorBarLocation() { 287 return this.colorBarLocation; 288 } 289 290 /** 291 * Sets the color bar location and sends a {@link PlotChangeEvent} to all 292 * registered listeners. 293 * 294 * @param edge the location. 295 */ 296 public void setColorBarLocation(RectangleEdge edge) { 297 this.colorBarLocation = edge; 298 fireChangeEvent(); 299 } 300 301 /** 302 * Returns the primary dataset for the plot. 303 * 304 * @return The primary dataset (possibly <code>null</code>). 305 */ 306 public ContourDataset getDataset() { 307 return this.dataset; 308 } 309 310 /** 311 * Sets the dataset for the plot, replacing the existing dataset if there 312 * is one. 313 * 314 * @param dataset the dataset (<code>null</code> permitted). 315 */ 316 public void setDataset(ContourDataset dataset) { 317 318 // if there is an existing dataset, remove the plot from the list of 319 // change listeners... 320 ContourDataset existing = this.dataset; 321 if (existing != null) { 322 existing.removeChangeListener(this); 323 } 324 325 // set the new dataset, and register the chart as a change listener... 326 this.dataset = dataset; 327 if (dataset != null) { 328 setDatasetGroup(dataset.getGroup()); 329 dataset.addChangeListener(this); 330 } 331 332 // send a dataset change event to self... 333 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); 334 datasetChanged(event); 335 336 } 337 338 /** 339 * Returns the domain axis for the plot. 340 * 341 * @return The domain axis. 342 */ 343 public ValueAxis getDomainAxis() { 344 345 ValueAxis result = this.domainAxis; 346 347 return result; 348 349 } 350 351 /** 352 * Sets the domain axis for the plot (this must be compatible with the plot 353 * type or an exception is thrown). 354 * 355 * @param axis The new axis. 356 */ 357 public void setDomainAxis(ValueAxis axis) { 358 359 if (isCompatibleDomainAxis(axis)) { 360 361 if (axis != null) { 362 axis.setPlot(this); 363 axis.addChangeListener(this); 364 } 365 366 // plot is likely registered as a listener with the existing axis... 367 if (this.domainAxis != null) { 368 this.domainAxis.removeChangeListener(this); 369 } 370 371 this.domainAxis = axis; 372 fireChangeEvent(); 373 374 } 375 376 } 377 378 /** 379 * Returns the range axis for the plot. 380 * 381 * @return The range axis. 382 */ 383 public ValueAxis getRangeAxis() { 384 385 ValueAxis result = this.rangeAxis; 386 387 return result; 388 389 } 390 391 /** 392 * Sets the range axis for the plot. 393 * <P> 394 * An exception is thrown if the new axis and the plot are not mutually 395 * compatible. 396 * 397 * @param axis The new axis (null permitted). 398 */ 399 public void setRangeAxis(ValueAxis axis) { 400 401 if (axis != null) { 402 axis.setPlot(this); 403 axis.addChangeListener(this); 404 } 405 406 // plot is likely registered as a listener with the existing axis... 407 if (this.rangeAxis != null) { 408 this.rangeAxis.removeChangeListener(this); 409 } 410 411 this.rangeAxis = axis; 412 fireChangeEvent(); 413 414 } 415 416 /** 417 * Sets the colorbar for the plot. 418 * 419 * @param axis The new axis (null permitted). 420 */ 421 public void setColorBarAxis(ColorBar axis) { 422 423 this.colorBar = axis; 424 fireChangeEvent(); 425 426 } 427 428 /** 429 * Returns the data area ratio. 430 * 431 * @return The ratio. 432 */ 433 public double getDataAreaRatio() { 434 return this.dataAreaRatio; 435 } 436 437 /** 438 * Sets the data area ratio. 439 * 440 * @param ratio the ratio. 441 */ 442 public void setDataAreaRatio(double ratio) { 443 this.dataAreaRatio = ratio; 444 } 445 446 /** 447 * Adds a marker for the domain axis. 448 * <P> 449 * Typically a marker will be drawn by the renderer as a line perpendicular 450 * to the range axis, however this is entirely up to the renderer. 451 * 452 * @param marker the marker. 453 */ 454 public void addDomainMarker(Marker marker) { 455 456 if (this.domainMarkers == null) { 457 this.domainMarkers = new java.util.ArrayList(); 458 } 459 this.domainMarkers.add(marker); 460 fireChangeEvent(); 461 462 } 463 464 /** 465 * Clears all the domain markers. 466 */ 467 public void clearDomainMarkers() { 468 if (this.domainMarkers != null) { 469 this.domainMarkers.clear(); 470 fireChangeEvent(); 471 } 472 } 473 474 /** 475 * Adds a marker for the range axis. 476 * <P> 477 * Typically a marker will be drawn by the renderer as a line perpendicular 478 * to the range axis, however this is entirely up to the renderer. 479 * 480 * @param marker The marker. 481 */ 482 public void addRangeMarker(Marker marker) { 483 484 if (this.rangeMarkers == null) { 485 this.rangeMarkers = new java.util.ArrayList(); 486 } 487 this.rangeMarkers.add(marker); 488 fireChangeEvent(); 489 490 } 491 492 /** 493 * Clears all the range markers. 494 */ 495 public void clearRangeMarkers() { 496 if (this.rangeMarkers != null) { 497 this.rangeMarkers.clear(); 498 fireChangeEvent(); 499 } 500 } 501 502 /** 503 * Adds an annotation to the plot. 504 * 505 * @param annotation the annotation. 506 */ 507 public void addAnnotation(XYAnnotation annotation) { 508 509 if (this.annotations == null) { 510 this.annotations = new java.util.ArrayList(); 511 } 512 this.annotations.add(annotation); 513 fireChangeEvent(); 514 515 } 516 517 /** 518 * Clears all the annotations. 519 */ 520 public void clearAnnotations() { 521 if (this.annotations != null) { 522 this.annotations.clear(); 523 fireChangeEvent(); 524 } 525 } 526 527 /** 528 * Checks the compatibility of a domain axis, returning true if the axis is 529 * compatible with the plot, and false otherwise. 530 * 531 * @param axis The proposed axis. 532 * 533 * @return <code>true</code> if the axis is compatible with the plot. 534 */ 535 public boolean isCompatibleDomainAxis(ValueAxis axis) { 536 537 return true; 538 539 } 540 541 /** 542 * Draws the plot on a Java 2D graphics device (such as the screen or a 543 * printer). 544 * <P> 545 * The optional <code>info</code> argument collects information about the 546 * rendering of the plot (dimensions, tooltip information etc). Just pass 547 * in <code>null</code> if you do not need this information. 548 * 549 * @param g2 the graphics device. 550 * @param area the area within which the plot (including axis labels) 551 * should be drawn. 552 * @param anchor the anchor point (<code>null</code> permitted). 553 * @param parentState the state from the parent plot, if there is one. 554 * @param info collects chart drawing information (<code>null</code> 555 * permitted). 556 */ 557 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 558 PlotState parentState, 559 PlotRenderingInfo info) { 560 561 // if the plot area is too small, just return... 562 boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); 563 boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); 564 if (b1 || b2) { 565 return; 566 } 567 568 // record the plot area... 569 if (info != null) { 570 info.setPlotArea(area); 571 } 572 573 // adjust the drawing area for plot insets (if any)... 574 RectangleInsets insets = getInsets(); 575 insets.trim(area); 576 577 AxisSpace space = new AxisSpace(); 578 579 space = this.domainAxis.reserveSpace(g2, this, area, 580 RectangleEdge.BOTTOM, space); 581 space = this.rangeAxis.reserveSpace(g2, this, area, 582 RectangleEdge.LEFT, space); 583 584 Rectangle2D estimatedDataArea = space.shrink(area, null); 585 586 AxisSpace space2 = new AxisSpace(); 587 space2 = this.colorBar.reserveSpace(g2, this, area, estimatedDataArea, 588 this.colorBarLocation, space2); 589 Rectangle2D adjustedPlotArea = space2.shrink(area, null); 590 591 Rectangle2D dataArea = space.shrink(adjustedPlotArea, null); 592 593 Rectangle2D colorBarArea = space2.reserved(area, this.colorBarLocation); 594 595 // additional dataArea modifications 596 if (getDataAreaRatio() != 0.0) { //check whether modification is 597 double ratio = getDataAreaRatio(); 598 Rectangle2D tmpDataArea = (Rectangle2D) dataArea.clone(); 599 double h = tmpDataArea.getHeight(); 600 double w = tmpDataArea.getWidth(); 601 602 if (ratio > 0) { // ratio represents pixels 603 if (w * ratio <= h) { 604 h = ratio * w; 605 } 606 else { 607 w = h / ratio; 608 } 609 } 610 else { // ratio represents axis units 611 ratio *= -1.0; 612 double xLength = getDomainAxis().getRange().getLength(); 613 double yLength = getRangeAxis().getRange().getLength(); 614 double unitRatio = yLength / xLength; 615 616 ratio = unitRatio * ratio; 617 618 if (w * ratio <= h) { 619 h = ratio * w; 620 } 621 else { 622 w = h / ratio; 623 } 624 } 625 626 dataArea.setRect(tmpDataArea.getX() + tmpDataArea.getWidth() / 2 627 - w / 2, tmpDataArea.getY(), w, h); 628 } 629 630 if (info != null) { 631 info.setDataArea(dataArea); 632 } 633 634 CrosshairState crosshairState = new CrosshairState(); 635 crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY); 636 637 // draw the plot background... 638 drawBackground(g2, dataArea); 639 640 double cursor = dataArea.getMaxY(); 641 if (this.domainAxis != null) { 642 this.domainAxis.draw(g2, cursor, adjustedPlotArea, dataArea, 643 RectangleEdge.BOTTOM, info); 644 } 645 646 if (this.rangeAxis != null) { 647 cursor = dataArea.getMinX(); 648 this.rangeAxis.draw(g2, cursor, adjustedPlotArea, dataArea, 649 RectangleEdge.LEFT, info); 650 } 651 652 if (this.colorBar != null) { 653 cursor = 0.0; 654 cursor = this.colorBar.draw(g2, cursor, adjustedPlotArea, dataArea, 655 colorBarArea, this.colorBarLocation); 656 } 657 Shape originalClip = g2.getClip(); 658 Composite originalComposite = g2.getComposite(); 659 660 g2.clip(dataArea); 661 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 662 getForegroundAlpha())); 663 render(g2, dataArea, info, crosshairState); 664 665 if (this.domainMarkers != null) { 666 Iterator iterator = this.domainMarkers.iterator(); 667 while (iterator.hasNext()) { 668 Marker marker = (Marker) iterator.next(); 669 drawDomainMarker(g2, this, getDomainAxis(), marker, dataArea); 670 } 671 } 672 673 if (this.rangeMarkers != null) { 674 Iterator iterator = this.rangeMarkers.iterator(); 675 while (iterator.hasNext()) { 676 Marker marker = (Marker) iterator.next(); 677 drawRangeMarker(g2, this, getRangeAxis(), marker, dataArea); 678 } 679 } 680 681 // TO DO: these annotations only work with XYPlot, see if it is possible to 682 // make ContourPlot a subclass of XYPlot (DG); 683 684 // // draw the annotations... 685 // if (this.annotations != null) { 686 // Iterator iterator = this.annotations.iterator(); 687 // while (iterator.hasNext()) { 688 // Annotation annotation = (Annotation) iterator.next(); 689 // if (annotation instanceof XYAnnotation) { 690 // XYAnnotation xya = (XYAnnotation) annotation; 691 // // get the annotation to draw itself... 692 // xya.draw(g2, this, dataArea, getDomainAxis(), 693 // getRangeAxis()); 694 // } 695 // } 696 // } 697 698 g2.setClip(originalClip); 699 g2.setComposite(originalComposite); 700 drawOutline(g2, dataArea); 701 702 } 703 704 /** 705 * Draws a representation of the data within the dataArea region, using the 706 * current renderer. 707 * <P> 708 * The <code>info</code> and <code>crosshairState</code> arguments may be 709 * <code>null</code>. 710 * 711 * @param g2 the graphics device. 712 * @param dataArea the region in which the data is to be drawn. 713 * @param info an optional object for collection dimension information. 714 * @param crosshairState an optional object for collecting crosshair info. 715 */ 716 public void render(Graphics2D g2, Rectangle2D dataArea, 717 PlotRenderingInfo info, CrosshairState crosshairState) { 718 719 // now get the data and plot it (the visual representation will depend 720 // on the renderer that has been set)... 721 ContourDataset data = getDataset(); 722 if (data != null) { 723 724 ColorBar zAxis = getColorBar(); 725 726 if (this.clipPath != null) { 727 GeneralPath clipper = getClipPath().draw(g2, dataArea, 728 this.domainAxis, this.rangeAxis); 729 if (this.clipPath.isClip()) { 730 g2.clip(clipper); 731 } 732 } 733 734 if (this.renderAsPoints) { 735 pointRenderer(g2, dataArea, info, this, this.domainAxis, 736 this.rangeAxis, zAxis, data, crosshairState); 737 } 738 else { 739 contourRenderer(g2, dataArea, info, this, this.domainAxis, 740 this.rangeAxis, zAxis, data, crosshairState); 741 } 742 743 // draw vertical crosshair if required... 744 setDomainCrosshairValue(crosshairState.getCrosshairX(), false); 745 if (isDomainCrosshairVisible()) { 746 drawVerticalLine(g2, dataArea, 747 getDomainCrosshairValue(), 748 getDomainCrosshairStroke(), 749 getDomainCrosshairPaint()); 750 } 751 752 // draw horizontal crosshair if required... 753 setRangeCrosshairValue(crosshairState.getCrosshairY(), false); 754 if (isRangeCrosshairVisible()) { 755 drawHorizontalLine(g2, dataArea, 756 getRangeCrosshairValue(), 757 getRangeCrosshairStroke(), 758 getRangeCrosshairPaint()); 759 } 760 761 } 762 else if (this.clipPath != null) { 763 getClipPath().draw(g2, dataArea, this.domainAxis, this.rangeAxis); 764 } 765 766 } 767 768 /** 769 * Fills the plot. 770 * 771 * @param g2 the graphics device. 772 * @param dataArea the area within which the data is being drawn. 773 * @param info collects information about the drawing. 774 * @param plot the plot (can be used to obtain standard color 775 * information etc). 776 * @param horizontalAxis the domain (horizontal) axis. 777 * @param verticalAxis the range (vertical) axis. 778 * @param colorBar the color bar axis. 779 * @param data the dataset. 780 * @param crosshairState information about crosshairs on a plot. 781 */ 782 public void contourRenderer(Graphics2D g2, 783 Rectangle2D dataArea, 784 PlotRenderingInfo info, 785 ContourPlot plot, 786 ValueAxis horizontalAxis, 787 ValueAxis verticalAxis, 788 ColorBar colorBar, 789 ContourDataset data, 790 CrosshairState crosshairState) { 791 792 // setup for collecting optional entity info... 793 Rectangle2D.Double entityArea = null; 794 EntityCollection entities = null; 795 if (info != null) { 796 entities = info.getOwner().getEntityCollection(); 797 } 798 799 Rectangle2D.Double rect = null; 800 rect = new Rectangle2D.Double(); 801 802 //turn off anti-aliasing when filling rectangles 803 Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 804 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 805 RenderingHints.VALUE_ANTIALIAS_OFF); 806 807 // get the data points 808 Number[] xNumber = data.getXValues(); 809 Number[] yNumber = data.getYValues(); 810 Number[] zNumber = data.getZValues(); 811 812 double[] x = new double[xNumber.length]; 813 double[] y = new double[yNumber.length]; 814 815 for (int i = 0; i < x.length; i++) { 816 x[i] = xNumber[i].doubleValue(); 817 y[i] = yNumber[i].doubleValue(); 818 } 819 820 int[] xIndex = data.indexX(); 821 int[] indexX = data.getXIndices(); 822 boolean vertInverted = ((NumberAxis) verticalAxis).isInverted(); 823 boolean horizInverted = false; 824 if (horizontalAxis instanceof NumberAxis) { 825 horizInverted = ((NumberAxis) horizontalAxis).isInverted(); 826 } 827 double transX = 0.0; 828 double transXm1 = 0.0; 829 double transXp1 = 0.0; 830 double transDXm1 = 0.0; 831 double transDXp1 = 0.0; 832 double transDX = 0.0; 833 double transY = 0.0; 834 double transYm1 = 0.0; 835 double transYp1 = 0.0; 836 double transDYm1 = 0.0; 837 double transDYp1 = 0.0; 838 double transDY = 0.0; 839 int iMax = xIndex[xIndex.length - 1]; 840 for (int k = 0; k < x.length; k++) { 841 int i = xIndex[k]; 842 if (indexX[i] == k) { // this is a new column 843 if (i == 0) { 844 transX = horizontalAxis.valueToJava2D(x[k], dataArea, 845 RectangleEdge.BOTTOM); 846 transXm1 = transX; 847 transXp1 = horizontalAxis.valueToJava2D( 848 x[indexX[i + 1]], dataArea, RectangleEdge.BOTTOM); 849 transDXm1 = Math.abs(0.5 * (transX - transXm1)); 850 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 851 } 852 else if (i == iMax) { 853 transX = horizontalAxis.valueToJava2D(x[k], dataArea, 854 RectangleEdge.BOTTOM); 855 transXm1 = horizontalAxis.valueToJava2D(x[indexX[i - 1]], 856 dataArea, RectangleEdge.BOTTOM); 857 transXp1 = transX; 858 transDXm1 = Math.abs(0.5 * (transX - transXm1)); 859 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 860 } 861 else { 862 transX = horizontalAxis.valueToJava2D(x[k], dataArea, 863 RectangleEdge.BOTTOM); 864 transXp1 = horizontalAxis.valueToJava2D(x[indexX[i + 1]], 865 dataArea, RectangleEdge.BOTTOM); 866 transDXm1 = transDXp1; 867 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 868 } 869 870 if (horizInverted) { 871 transX -= transDXp1; 872 } 873 else { 874 transX -= transDXm1; 875 } 876 877 transDX = transDXm1 + transDXp1; 878 879 transY = verticalAxis.valueToJava2D(y[k], dataArea, 880 RectangleEdge.LEFT); 881 transYm1 = transY; 882 if (k + 1 == y.length) { 883 continue; 884 } 885 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea, 886 RectangleEdge.LEFT); 887 transDYm1 = Math.abs(0.5 * (transY - transYm1)); 888 transDYp1 = Math.abs(0.5 * (transY - transYp1)); 889 } 890 else if ((i < indexX.length - 1 891 && indexX[i + 1] - 1 == k) || k == x.length - 1) { 892 // end of column 893 transY = verticalAxis.valueToJava2D(y[k], dataArea, 894 RectangleEdge.LEFT); 895 transYm1 = verticalAxis.valueToJava2D(y[k - 1], dataArea, 896 RectangleEdge.LEFT); 897 transYp1 = transY; 898 transDYm1 = Math.abs(0.5 * (transY - transYm1)); 899 transDYp1 = Math.abs(0.5 * (transY - transYp1)); 900 } 901 else { 902 transY = verticalAxis.valueToJava2D(y[k], dataArea, 903 RectangleEdge.LEFT); 904 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea, 905 RectangleEdge.LEFT); 906 transDYm1 = transDYp1; 907 transDYp1 = Math.abs(0.5 * (transY - transYp1)); 908 } 909 if (vertInverted) { 910 transY -= transDYm1; 911 } 912 else { 913 transY -= transDYp1; 914 } 915 916 transDY = transDYm1 + transDYp1; 917 918 rect.setRect(transX, transY, transDX, transDY); 919 if (zNumber[k] != null) { 920 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue())); 921 g2.fill(rect); 922 } 923 else if (this.missingPaint != null) { 924 g2.setPaint(this.missingPaint); 925 g2.fill(rect); 926 } 927 928 entityArea = rect; 929 930 // add an entity for the item... 931 if (entities != null) { 932 String tip = ""; 933 if (getToolTipGenerator() != null) { 934 tip = this.toolTipGenerator.generateToolTip(data, k); 935 } 936 // Shape s = g2.getClip(); 937 // if (s.contains(rect) || s.intersects(rect)) { 938 String url = null; 939 // if (getURLGenerator() != null) { //dmo: look at this later 940 // url = getURLGenerator().generateURL(data, series, item); 941 // } 942 // Unlike XYItemRenderer, we need to clone entityArea since it 943 // reused. 944 ContourEntity entity = new ContourEntity( 945 (Rectangle2D.Double) entityArea.clone(), tip, url); 946 entity.setIndex(k); 947 entities.add(entity); 948 // } 949 } 950 951 // do we need to update the crosshair values? 952 if (plot.isDomainCrosshairLockedOnData()) { 953 if (plot.isRangeCrosshairLockedOnData()) { 954 // both axes 955 crosshairState.updateCrosshairPoint(x[k], y[k], transX, 956 transY, PlotOrientation.VERTICAL); 957 } 958 else { 959 // just the horizontal axis... 960 crosshairState.updateCrosshairX(transX); 961 } 962 } 963 else { 964 if (plot.isRangeCrosshairLockedOnData()) { 965 // just the vertical axis... 966 crosshairState.updateCrosshairY(transY); 967 } 968 } 969 } 970 971 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias); 972 973 return; 974 975 } 976 977 /** 978 * Draws the visual representation of a single data item. 979 * 980 * @param g2 the graphics device. 981 * @param dataArea the area within which the data is being drawn. 982 * @param info collects information about the drawing. 983 * @param plot the plot (can be used to obtain standard color 984 * information etc). 985 * @param domainAxis the domain (horizontal) axis. 986 * @param rangeAxis the range (vertical) axis. 987 * @param colorBar the color bar axis. 988 * @param data the dataset. 989 * @param crosshairState information about crosshairs on a plot. 990 */ 991 public void pointRenderer(Graphics2D g2, 992 Rectangle2D dataArea, 993 PlotRenderingInfo info, 994 ContourPlot plot, 995 ValueAxis domainAxis, 996 ValueAxis rangeAxis, 997 ColorBar colorBar, 998 ContourDataset data, 999 CrosshairState crosshairState) { 1000 1001 // setup for collecting optional entity info... 1002 RectangularShape entityArea = null; 1003 EntityCollection entities = null; 1004 if (info != null) { 1005 entities = info.getOwner().getEntityCollection(); 1006 } 1007 1008 // Rectangle2D.Double rect = null; 1009 // rect = new Rectangle2D.Double(); 1010 RectangularShape rect = new Ellipse2D.Double(); 1011 1012 1013 //turn off anti-aliasing when filling rectangles 1014 Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 1015 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 1016 RenderingHints.VALUE_ANTIALIAS_OFF); 1017 1018 // if (tooltips!=null) tooltips.clearToolTips(); // reset collection 1019 // get the data points 1020 Number[] xNumber = data.getXValues(); 1021 Number[] yNumber = data.getYValues(); 1022 Number[] zNumber = data.getZValues(); 1023 1024 double[] x = new double[xNumber.length]; 1025 double[] y = new double[yNumber.length]; 1026 1027 for (int i = 0; i < x.length; i++) { 1028 x[i] = xNumber[i].doubleValue(); 1029 y[i] = yNumber[i].doubleValue(); 1030 } 1031 1032 double transX = 0.0; 1033 double transDX = 0.0; 1034 double transY = 0.0; 1035 double transDY = 0.0; 1036 double size = dataArea.getWidth() * this.ptSizePct; 1037 for (int k = 0; k < x.length; k++) { 1038 1039 transX = domainAxis.valueToJava2D(x[k], dataArea, 1040 RectangleEdge.BOTTOM) - 0.5 * size; 1041 transY = rangeAxis.valueToJava2D(y[k], dataArea, RectangleEdge.LEFT) 1042 - 0.5 * size; 1043 transDX = size; 1044 transDY = size; 1045 1046 rect.setFrame(transX, transY, transDX, transDY); 1047 1048 if (zNumber[k] != null) { 1049 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue())); 1050 g2.fill(rect); 1051 } 1052 else if (this.missingPaint != null) { 1053 g2.setPaint(this.missingPaint); 1054 g2.fill(rect); 1055 } 1056 1057 1058 entityArea = rect; 1059 1060 // add an entity for the item... 1061 if (entities != null) { 1062 String tip = null; 1063 if (getToolTipGenerator() != null) { 1064 tip = this.toolTipGenerator.generateToolTip(data, k); 1065 } 1066 String url = null; 1067 // if (getURLGenerator() != null) { //dmo: look at this later 1068 // url = getURLGenerator().generateURL(data, series, item); 1069 // } 1070 // Unlike XYItemRenderer, we need to clone entityArea since it 1071 // reused. 1072 ContourEntity entity = new ContourEntity( 1073 (RectangularShape) entityArea.clone(), tip, url); 1074 entity.setIndex(k); 1075 entities.add(entity); 1076 } 1077 1078 // do we need to update the crosshair values? 1079 if (plot.isDomainCrosshairLockedOnData()) { 1080 if (plot.isRangeCrosshairLockedOnData()) { 1081 // both axes 1082 crosshairState.updateCrosshairPoint(x[k], y[k], transX, 1083 transY, PlotOrientation.VERTICAL); 1084 } 1085 else { 1086 // just the horizontal axis... 1087 crosshairState.updateCrosshairX(transX); 1088 } 1089 } 1090 else { 1091 if (plot.isRangeCrosshairLockedOnData()) { 1092 // just the vertical axis... 1093 crosshairState.updateCrosshairY(transY); 1094 } 1095 } 1096 } 1097 1098 1099 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias); 1100 1101 return; 1102 1103 } 1104 1105 /** 1106 * Utility method for drawing a crosshair on the chart (if required). 1107 * 1108 * @param g2 The graphics device. 1109 * @param dataArea The data area. 1110 * @param value The coordinate, where to draw the line. 1111 * @param stroke The stroke to use. 1112 * @param paint The paint to use. 1113 */ 1114 protected void drawVerticalLine(Graphics2D g2, Rectangle2D dataArea, 1115 double value, Stroke stroke, Paint paint) { 1116 1117 double xx = getDomainAxis().valueToJava2D(value, dataArea, 1118 RectangleEdge.BOTTOM); 1119 Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx, 1120 dataArea.getMaxY()); 1121 g2.setStroke(stroke); 1122 g2.setPaint(paint); 1123 g2.draw(line); 1124 1125 } 1126 1127 /** 1128 * Utility method for drawing a crosshair on the chart (if required). 1129 * 1130 * @param g2 The graphics device. 1131 * @param dataArea The data area. 1132 * @param value The coordinate, where to draw the line. 1133 * @param stroke The stroke to use. 1134 * @param paint The paint to use. 1135 */ 1136 protected void drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea, 1137 double value, Stroke stroke, 1138 Paint paint) { 1139 1140 double yy = getRangeAxis().valueToJava2D(value, dataArea, 1141 RectangleEdge.LEFT); 1142 Line2D line = new Line2D.Double(dataArea.getMinX(), yy, 1143 dataArea.getMaxX(), yy); 1144 g2.setStroke(stroke); 1145 g2.setPaint(paint); 1146 g2.draw(line); 1147 1148 } 1149 1150 /** 1151 * Handles a 'click' on the plot by updating the anchor values... 1152 * 1153 * @param x x-coordinate, where the click occured. 1154 * @param y y-coordinate, where the click occured. 1155 * @param info An object for collection dimension information. 1156 */ 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 public void zoom(double percent) { 1188 1189 if (percent > 0) { 1190 // double range = this.domainAxis.getRange().getLength(); 1191 // double scaledRange = range * percent; 1192 // domainAxis.setAnchoredRange(scaledRange); 1193 1194 // range = this.rangeAxis.getRange().getLength(); 1195 // scaledRange = range * percent; 1196 // rangeAxis.setAnchoredRange(scaledRange); 1197 } 1198 else { 1199 getRangeAxis().setAutoRange(true); 1200 getDomainAxis().setAutoRange(true); 1201 } 1202 1203 } 1204 1205 /** 1206 * Returns the plot type as a string. 1207 * 1208 * @return A short string describing the type of plot. 1209 */ 1210 public String getPlotType() { 1211 return localizationResources.getString("Contour_Plot"); 1212 } 1213 1214 /** 1215 * Returns the range for an axis. 1216 * 1217 * @param axis the axis. 1218 * 1219 * @return The range for an axis. 1220 */ 1221 public Range getDataRange(ValueAxis axis) { 1222 1223 if (this.dataset == null) { 1224 return null; 1225 } 1226 1227 Range result = null; 1228 1229 if (axis == getDomainAxis()) { 1230 result = DatasetUtilities.findDomainBounds(this.dataset); 1231 } 1232 else if (axis == getRangeAxis()) { 1233 result = DatasetUtilities.findRangeBounds(this.dataset); 1234 } 1235 1236 return result; 1237 1238 } 1239 1240 /** 1241 * Returns the range for the Contours. 1242 * 1243 * @return The range for the Contours (z-axis). 1244 */ 1245 public Range getContourDataRange() { 1246 1247 Range result = null; 1248 1249 ContourDataset data = getDataset(); 1250 1251 if (data != null) { 1252 Range h = getDomainAxis().getRange(); 1253 Range v = getRangeAxis().getRange(); 1254 result = this.visibleRange(data, h, v); 1255 } 1256 1257 return result; 1258 } 1259 1260 /** 1261 * Notifies all registered listeners of a property change. 1262 * <P> 1263 * One source of property change events is the plot's renderer. 1264 * 1265 * @param event Information about the property change. 1266 */ 1267 public void propertyChange(PropertyChangeEvent event) { 1268 fireChangeEvent(); 1269 } 1270 1271 /** 1272 * Receives notification of a change to the plot's dataset. 1273 * <P> 1274 * The chart reacts by passing on a chart change event to all registered 1275 * listeners. 1276 * 1277 * @param event Information about the event (not used here). 1278 */ 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 public void axisChanged(AxisChangeEvent event) { 1711 Object source = event.getSource(); 1712 if (source.equals(this.rangeAxis) || source.equals(this.domainAxis)) { 1713 ColorBar cba = this.colorBar; 1714 if (this.colorBar.getAxis().isAutoRange()) { 1715 cba.getAxis().configure(); 1716 } 1717 1718 } 1719 super.axisChanged(event); 1720 } 1721 1722 /** 1723 * Returns the visible z-range. 1724 * 1725 * @param data the dataset. 1726 * @param x the x range. 1727 * @param y the y range. 1728 * 1729 * @return The range. 1730 */ 1731 public Range visibleRange(ContourDataset data, Range x, Range y) { 1732 Range range = null; 1733 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 public Object clone() throws CloneNotSupportedException { 1826 ContourPlot clone = (ContourPlot) super.clone(); 1827 1828 if (this.domainAxis != null) { 1829 clone.domainAxis = (ValueAxis) this.domainAxis.clone(); 1830 clone.domainAxis.setPlot(clone); 1831 clone.domainAxis.addChangeListener(clone); 1832 } 1833 if (this.rangeAxis != null) { 1834 clone.rangeAxis = (ValueAxis) this.rangeAxis.clone(); 1835 clone.rangeAxis.setPlot(clone); 1836 clone.rangeAxis.addChangeListener(clone); 1837 } 1838 1839 if (clone.dataset != null) { 1840 clone.dataset.addChangeListener(clone); 1841 } 1842 1843 if (this.colorBar != null) { 1844 clone.colorBar = (ColorBar) this.colorBar.clone(); 1845 } 1846 1847 clone.domainMarkers = (List) ObjectUtilities.deepClone( 1848 this.domainMarkers); 1849 clone.rangeMarkers = (List) ObjectUtilities.deepClone( 1850 this.rangeMarkers); 1851 clone.annotations = (List) ObjectUtilities.deepClone(this.annotations); 1852 1853 if (this.clipPath != null) { 1854 clone.clipPath = (ClipPath) this.clipPath.clone(); 1855 } 1856 1857 return clone; 1858 } 1859 1860 }