| 1 64 65 package org.jfree.chart.plot; 66 67 import java.awt.AlphaComposite ; 68 import java.awt.Composite ; 69 import java.awt.Graphics2D ; 70 import java.awt.Paint ; 71 import java.awt.RenderingHints ; 72 import java.awt.Shape ; 73 import java.awt.Stroke ; 74 import java.awt.geom.Ellipse2D ; 75 import java.awt.geom.GeneralPath ; 76 import java.awt.geom.Line2D ; 77 import java.awt.geom.Point2D ; 78 import java.awt.geom.Rectangle2D ; 79 import java.awt.geom.RectangularShape ; 80 import java.beans.PropertyChangeEvent ; 81 import java.beans.PropertyChangeListener ; 82 import java.io.Serializable ; 83 import java.util.Iterator ; 84 import java.util.List ; 85 import java.util.ResourceBundle ; 86 87 import org.jfree.chart.ClipPath; 88 import org.jfree.chart.annotations.XYAnnotation; 89 import org.jfree.chart.axis.AxisSpace; 90 import org.jfree.chart.axis.ColorBar; 91 import org.jfree.chart.axis.NumberAxis; 92 import org.jfree.chart.axis.ValueAxis; 93 import org.jfree.chart.entity.ContourEntity; 94 import org.jfree.chart.entity.EntityCollection; 95 import org.jfree.chart.event.AxisChangeEvent; 96 import org.jfree.chart.event.PlotChangeEvent; 97 import org.jfree.chart.labels.ContourToolTipGenerator; 98 import org.jfree.chart.labels.StandardContourToolTipGenerator; 99 import org.jfree.chart.urls.XYURLGenerator; 100 import org.jfree.data.Range; 101 import org.jfree.data.contour.ContourDataset; 102 import org.jfree.data.general.DatasetChangeEvent; 103 import org.jfree.data.general.DatasetUtilities; 104 import org.jfree.ui.RectangleEdge; 105 import org.jfree.ui.RectangleInsets; 106 import org.jfree.util.ObjectUtilities; 107 108 113 public class ContourPlot extends Plot implements ContourValuePlot, 114 ValueAxisPlot, 115 PropertyChangeListener , 116 Serializable , 117 Cloneable { 118 119 120 private static final long serialVersionUID = 7861072556590502247L; 121 122 123 protected static final RectangleInsets DEFAULT_INSETS 124 = new RectangleInsets(2.0, 2.0, 100.0, 10.0); 125 126 127 private ValueAxis domainAxis; 128 129 130 private ValueAxis rangeAxis; 131 132 133 private ContourDataset dataset; 134 135 136 private ColorBar colorBar = null; 137 138 139 private RectangleEdge colorBarLocation; 140 141 142 private boolean domainCrosshairVisible; 143 144 145 private double domainCrosshairValue; 146 147 148 private transient Stroke domainCrosshairStroke; 149 150 151 private transient Paint domainCrosshairPaint; 152 153 157 private boolean domainCrosshairLockedOnData = true; 158 159 160 private boolean rangeCrosshairVisible; 161 162 163 private double rangeCrosshairValue; 164 165 166 private transient Stroke rangeCrosshairStroke; 167 168 169 private transient Paint rangeCrosshairPaint; 170 171 175 private boolean rangeCrosshairLockedOnData = true; 176 177 178 private List domainMarkers; 179 180 181 private List rangeMarkers; 182 183 184 private List annotations; 185 186 187 private ContourToolTipGenerator toolTipGenerator; 188 189 190 private XYURLGenerator urlGenerator; 191 192 196 private boolean renderAsPoints = false; 197 198 202 private double ptSizePct = 0.05; 203 204 205 private transient ClipPath clipPath = null; 206 207 208 private transient Paint missingPaint = null; 209 210 211 protected static ResourceBundle localizationResources = 212 ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle"); 213 214 223 public ContourPlot(ContourDataset dataset, 224 ValueAxis domainAxis, ValueAxis rangeAxis, 225 ColorBar colorBar) { 226 227 super(); 228 229 this.dataset = dataset; 230 if (dataset != null) { 231 dataset.addChangeListener(this); 232 } 233 234 this.domainAxis = domainAxis; 235 if (domainAxis != null) { 236 domainAxis.setPlot(this); 237 domainAxis.addChangeListener(this); 238 } 239 240 this.rangeAxis = rangeAxis; 241 if (rangeAxis != null) { 242 rangeAxis.setPlot(this); 243 rangeAxis.addChangeListener(this); 244 } 245 246 this.colorBar = colorBar; 247 if (colorBar != null) { 248 colorBar.getAxis().setPlot(this); 249 colorBar.getAxis().addChangeListener(this); 250 colorBar.configure(this); 251 } 252 this.colorBarLocation = RectangleEdge.LEFT; 253 254 this.toolTipGenerator = new StandardContourToolTipGenerator(); 255 256 } 257 258 263 public RectangleEdge getColorBarLocation() { 264 return this.colorBarLocation; 265 } 266 267 273 public void setColorBarLocation(RectangleEdge edge) { 274 this.colorBarLocation = edge; 275 notifyListeners(new PlotChangeEvent(this)); 276 } 277 278 283 public ContourDataset getDataset() { 284 return this.dataset; 285 } 286 287 293 public void setDataset(ContourDataset dataset) { 294 295 ContourDataset existing = this.dataset; 298 if (existing != null) { 299 existing.removeChangeListener(this); 300 } 301 302 this.dataset = dataset; 304 if (dataset != null) { 305 setDatasetGroup(dataset.getGroup()); 306 dataset.addChangeListener(this); 307 } 308 309 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); 311 datasetChanged(event); 312 313 } 314 315 320 public ValueAxis getDomainAxis() { 321 322 ValueAxis result = this.domainAxis; 323 324 return result; 325 326 } 327 328 334 public void setDomainAxis(ValueAxis axis) { 335 336 if (isCompatibleDomainAxis(axis)) { 337 338 if (axis != null) { 339 axis.setPlot(this); 340 axis.addChangeListener(this); 341 } 342 343 if (this.domainAxis != null) { 345 this.domainAxis.removeChangeListener(this); 346 } 347 348 this.domainAxis = axis; 349 notifyListeners(new PlotChangeEvent(this)); 350 351 } 352 353 } 354 355 360 public ValueAxis getRangeAxis() { 361 362 ValueAxis result = this.rangeAxis; 363 364 return result; 365 366 } 367 368 376 public void setRangeAxis(ValueAxis axis) { 377 378 if (axis != null) { 379 axis.setPlot(this); 380 axis.addChangeListener(this); 381 } 382 383 if (this.rangeAxis != null) { 385 this.rangeAxis.removeChangeListener(this); 386 } 387 388 this.rangeAxis = axis; 389 notifyListeners(new PlotChangeEvent(this)); 390 391 } 392 393 398 public void setColorBarAxis(ColorBar axis) { 399 400 this.colorBar = axis; 401 notifyListeners(new PlotChangeEvent(this)); 402 403 } 404 405 413 public void addDomainMarker(Marker marker) { 414 415 if (this.domainMarkers == null) { 416 this.domainMarkers = new java.util.ArrayList (); 417 } 418 this.domainMarkers.add(marker); 419 notifyListeners(new PlotChangeEvent(this)); 420 421 } 422 423 426 public void clearDomainMarkers() { 427 if (this.domainMarkers != null) { 428 this.domainMarkers.clear(); 429 notifyListeners(new PlotChangeEvent(this)); 430 } 431 } 432 433 441 public void addRangeMarker(Marker marker) { 442 443 if (this.rangeMarkers == null) { 444 this.rangeMarkers = new java.util.ArrayList (); 445 } 446 this.rangeMarkers.add(marker); 447 notifyListeners(new PlotChangeEvent(this)); 448 449 } 450 451 454 public void clearRangeMarkers() { 455 if (this.rangeMarkers != null) { 456 this.rangeMarkers.clear(); 457 notifyListeners(new PlotChangeEvent(this)); 458 } 459 } 460 461 466 public void addAnnotation(XYAnnotation annotation) { 467 468 if (this.annotations == null) { 469 this.annotations = new java.util.ArrayList (); 470 } 471 this.annotations.add(annotation); 472 notifyListeners(new PlotChangeEvent(this)); 473 474 } 475 476 479 public void clearAnnotations() { 480 if (this.annotations != null) { 481 this.annotations.clear(); 482 notifyListeners(new PlotChangeEvent(this)); 483 } 484 } 485 486 494 public boolean isCompatibleDomainAxis(ValueAxis axis) { 495 496 return true; 497 498 } 499 500 516 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 517 PlotState parentState, 518 PlotRenderingInfo info) { 519 520 boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); 522 boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); 523 if (b1 || b2) { 524 return; 525 } 526 527 if (info != null) { 529 info.setPlotArea(area); 530 } 531 532 RectangleInsets insets = getInsets(); 534 insets.trim(area); 535 536 AxisSpace space = new AxisSpace(); 537 538 space = this.domainAxis.reserveSpace( 539 g2, this, area, RectangleEdge.BOTTOM, space 540 ); 541 space = this.rangeAxis.reserveSpace( 542 g2, this, area, RectangleEdge.LEFT, space 543 ); 544 545 Rectangle2D estimatedDataArea = space.shrink(area, null); 546 547 AxisSpace space2 = new AxisSpace(); 548 space2 = this.colorBar.reserveSpace( 549 g2, this, area, estimatedDataArea, this.colorBarLocation, 550 space2 551 ); 552 Rectangle2D adjustedPlotArea = space2.shrink(area, null); 553 554 Rectangle2D dataArea = space.shrink(adjustedPlotArea, null); 555 556 Rectangle2D colorBarArea = space2.reserved( 557 area, this.colorBarLocation 558 ); 559 560 if (getDataAreaRatio() != 0.0) { double ratio = getDataAreaRatio(); 563 Rectangle2D tmpDataArea = (Rectangle2D ) dataArea.clone(); 564 double h = tmpDataArea.getHeight(); 565 double w = tmpDataArea.getWidth(); 566 567 if (ratio > 0) { if (w * ratio <= h) { 569 h = ratio * w; 570 } 571 else { 572 w = h / ratio; 573 } 574 } 575 else { ratio *= -1.0; 577 double xLength = getDomainAxis().getRange().getLength(); 578 double yLength = getRangeAxis().getRange().getLength(); 579 double unitRatio = yLength / xLength; 580 581 ratio = unitRatio * ratio; 582 583 if (w * ratio <= h) { 584 h = ratio * w; 585 } 586 else { 587 w = h / ratio; 588 } 589 } 590 591 dataArea.setRect( 592 tmpDataArea.getX() + tmpDataArea.getWidth() / 2 - w / 2, 593 tmpDataArea.getY(), w, h 594 ); 595 } 596 597 if (info != null) { 598 info.setDataArea(dataArea); 599 } 600 601 CrosshairState crosshairState = new CrosshairState(); 602 crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY); 603 604 drawBackground(g2, dataArea); 606 607 double cursor = dataArea.getMaxY(); 608 if (this.domainAxis != null) { 609 this.domainAxis.draw( 610 g2, cursor, adjustedPlotArea, dataArea, RectangleEdge.BOTTOM, 611 info 612 ); 613 } 614 615 if (this.rangeAxis != null) { 616 cursor = dataArea.getMinX(); 617 this.rangeAxis.draw( 618 g2, cursor, adjustedPlotArea, dataArea, RectangleEdge.LEFT, info 619 ); 620 } 621 622 if (this.colorBar != null) { 623 cursor = 0.0; 624 cursor = this.colorBar.draw( 625 g2, cursor, adjustedPlotArea, dataArea, colorBarArea, 626 this.colorBarLocation 627 ); 628 } 629 Shape originalClip = g2.getClip(); 630 Composite originalComposite = g2.getComposite(); 631 632 g2.clip(dataArea); 633 g2.setComposite(AlphaComposite.getInstance( 634 AlphaComposite.SRC_OVER, getForegroundAlpha()) 635 ); 636 render(g2, dataArea, info, crosshairState); 637 638 if (this.domainMarkers != null) { 639 Iterator iterator = this.domainMarkers.iterator(); 640 while (iterator.hasNext()) { 641 Marker marker = (Marker) iterator.next(); 642 drawDomainMarker(g2, this, getDomainAxis(), marker, dataArea); 643 } 644 } 645 646 if (this.rangeMarkers != null) { 647 Iterator iterator = this.rangeMarkers.iterator(); 648 while (iterator.hasNext()) { 649 Marker marker = (Marker) iterator.next(); 650 drawRangeMarker(g2, this, getRangeAxis(), marker, dataArea); 651 } 652 } 653 654 657 671 g2.setClip(originalClip); 672 g2.setComposite(originalComposite); 673 drawOutline(g2, dataArea); 674 675 } 676 677 689 public void render(Graphics2D g2, Rectangle2D dataArea, 690 PlotRenderingInfo info, CrosshairState crosshairState) { 691 692 ContourDataset data = getDataset(); 695 if (data != null) { 696 697 ColorBar zAxis = getColorBar(); 698 699 if (this.clipPath != null) { 700 GeneralPath clipper = getClipPath().draw( 701 g2, dataArea, this.domainAxis, this.rangeAxis 702 ); 703 if (this.clipPath.isClip()) { 704 g2.clip(clipper); 705 } 706 } 707 708 if (this.renderAsPoints) { 709 pointRenderer(g2, dataArea, info, this, 710 this.domainAxis, this.rangeAxis, zAxis, 711 data, crosshairState); 712 } 713 else { 714 contourRenderer(g2, dataArea, info, this, 715 this.domainAxis, this.rangeAxis, zAxis, 716 data, crosshairState); 717 } 718 719 setDomainCrosshairValue(crosshairState.getCrosshairX(), false); 721 if (isDomainCrosshairVisible()) { 722 drawVerticalLine(g2, dataArea, 723 getDomainCrosshairValue(), 724 getDomainCrosshairStroke(), 725 getDomainCrosshairPaint()); 726 } 727 728 setRangeCrosshairValue(crosshairState.getCrosshairY(), false); 730 if (isRangeCrosshairVisible()) { 731 drawHorizontalLine(g2, dataArea, 732 getRangeCrosshairValue(), 733 getRangeCrosshairStroke(), 734 getRangeCrosshairPaint()); 735 } 736 737 } 738 else if (this.clipPath != null) { 739 getClipPath().draw(g2, dataArea, this.domainAxis, this.rangeAxis); 740 } 741 742 } 743 744 758 public void contourRenderer(Graphics2D g2, 759 Rectangle2D dataArea, 760 PlotRenderingInfo info, 761 ContourPlot plot, 762 ValueAxis horizontalAxis, 763 ValueAxis verticalAxis, 764 ColorBar colorBar, 765 ContourDataset data, 766 CrosshairState crosshairState) { 767 768 Rectangle2D.Double entityArea = null; 770 EntityCollection entities = null; 771 if (info != null) { 772 entities = info.getOwner().getEntityCollection(); 773 } 774 775 Rectangle2D.Double rect = null; 776 rect = new Rectangle2D.Double (); 777 778 Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 780 g2.setRenderingHint( 781 RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF 782 ); 783 784 Number [] xNumber = data.getXValues(); 786 Number [] yNumber = data.getYValues(); 787 Number [] zNumber = data.getZValues(); 788 789 double[] x = new double[xNumber.length]; 790 double[] y = new double[yNumber.length]; 791 792 for (int i = 0; i < x.length; i++) { 793 x[i] = xNumber[i].doubleValue(); 794 y[i] = yNumber[i].doubleValue(); 795 } 796 797 int[] xIndex = data.indexX(); 798 int[] indexX = data.getXIndices(); 799 boolean vertInverted = ((NumberAxis) verticalAxis).isInverted(); 800 boolean horizInverted = false; 801 if (horizontalAxis instanceof NumberAxis) { 802 horizInverted = ((NumberAxis) horizontalAxis).isInverted(); 803 } 804 double transX = 0.0; 805 double transXm1 = 0.0; 806 double transXp1 = 0.0; 807 double transDXm1 = 0.0; 808 double transDXp1 = 0.0; 809 double transDX = 0.0; 810 double transY = 0.0; 811 double transYm1 = 0.0; 812 double transYp1 = 0.0; 813 double transDYm1 = 0.0; 814 double transDYp1 = 0.0; 815 double transDY = 0.0; 816 int iMax = xIndex[xIndex.length - 1]; 817 for (int k = 0; k < x.length; k++) { 818 int i = xIndex[k]; 819 if (indexX[i] == k) { if (i == 0) { 821 transX = horizontalAxis.valueToJava2D( 822 x[k], dataArea, RectangleEdge.BOTTOM 823 ); 824 transXm1 = transX; 825 transXp1 = horizontalAxis.valueToJava2D( 826 x[indexX[i + 1]], dataArea, RectangleEdge.BOTTOM 827 ); 828 transDXm1 = Math.abs(0.5 * (transX - transXm1)); 829 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 830 } 831 else if (i == iMax) { 832 transX = horizontalAxis.valueToJava2D( 833 x[k], dataArea, RectangleEdge.BOTTOM 834 ); 835 transXm1 = horizontalAxis.valueToJava2D( 836 x[indexX[i - 1]], dataArea, RectangleEdge.BOTTOM 837 ); 838 transXp1 = transX; 839 transDXm1 = Math.abs(0.5 * (transX - transXm1)); 840 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 841 } 842 else { 843 transX = horizontalAxis.valueToJava2D( 844 x[k], dataArea, RectangleEdge.BOTTOM 845 ); 846 transXp1 = horizontalAxis.valueToJava2D( 847 x[indexX[i + 1]], dataArea, RectangleEdge.BOTTOM 848 ); 849 transDXm1 = transDXp1; 850 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 851 } 852 853 if (horizInverted) { 854 transX -= transDXp1; 855 } 856 else { 857 transX -= transDXm1; 858 } 859 860 transDX = transDXm1 + transDXp1; 861 862 transY = verticalAxis.valueToJava2D( 863 y[k], dataArea, RectangleEdge.LEFT 864 ); 865 transYm1 = transY; 866 <
|