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