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