1 98 99 package org.jfree.chart.renderer.xy; 100 101 import java.awt.Graphics2D ; 102 import java.awt.Image ; 103 import java.awt.Paint ; 104 import java.awt.Point ; 105 import java.awt.Shape ; 106 import java.awt.Stroke ; 107 import java.awt.geom.GeneralPath ; 108 import java.awt.geom.Line2D ; 109 import java.awt.geom.Rectangle2D ; 110 import java.io.IOException ; 111 import java.io.ObjectInputStream ; 112 import java.io.ObjectOutputStream ; 113 import java.io.Serializable ; 114 115 import org.jfree.chart.LegendItem; 116 import org.jfree.chart.axis.ValueAxis; 117 import org.jfree.chart.entity.EntityCollection; 118 import org.jfree.chart.event.RendererChangeEvent; 119 import org.jfree.chart.labels.XYToolTipGenerator; 120 import org.jfree.chart.plot.CrosshairState; 121 import org.jfree.chart.plot.Plot; 122 import org.jfree.chart.plot.PlotOrientation; 123 import org.jfree.chart.plot.PlotRenderingInfo; 124 import org.jfree.chart.plot.XYPlot; 125 import org.jfree.chart.urls.XYURLGenerator; 126 import org.jfree.data.xy.XYDataset; 127 import org.jfree.io.SerialUtilities; 128 import org.jfree.ui.RectangleEdge; 129 import org.jfree.util.BooleanList; 130 import org.jfree.util.BooleanUtilities; 131 import org.jfree.util.ObjectUtilities; 132 import org.jfree.util.PublicCloneable; 133 import org.jfree.util.ShapeUtilities; 134 import org.jfree.util.UnitType; 135 136 141 public class StandardXYItemRenderer extends AbstractXYItemRenderer 142 implements XYItemRenderer, 143 Cloneable , 144 PublicCloneable, 145 Serializable { 146 147 148 private static final long serialVersionUID = -3271351259436865995L; 149 150 151 public static final int SHAPES = 1; 152 153 154 public static final int LINES = 2; 155 156 157 public static final int SHAPES_AND_LINES = SHAPES | LINES; 158 159 160 public static final int IMAGES = 4; 161 162 163 public static final int DISCONTINUOUS = 8; 164 165 166 public static final int DISCONTINUOUS_LINES = LINES | DISCONTINUOUS; 167 168 169 private boolean baseShapesVisible; 170 171 172 private boolean plotLines; 173 174 175 private boolean plotImages; 176 177 178 private boolean plotDiscontinuous; 179 180 181 private UnitType gapThresholdType = UnitType.RELATIVE; 182 183 184 private double gapThreshold = 1.0; 185 186 187 private Boolean shapesFilled; 188 189 193 private BooleanList seriesShapesFilled; 194 195 196 private boolean baseShapesFilled; 197 198 202 private boolean drawSeriesLineAsPath; 203 204 208 private transient Shape legendLine; 209 210 213 public StandardXYItemRenderer() { 214 this(LINES, null); 215 } 216 217 225 public StandardXYItemRenderer(int type) { 226 this(type, null); 227 } 228 229 239 public StandardXYItemRenderer(int type, 240 XYToolTipGenerator toolTipGenerator) { 241 this(type, toolTipGenerator, null); 242 } 243 244 255 public StandardXYItemRenderer(int type, 256 XYToolTipGenerator toolTipGenerator, 257 XYURLGenerator urlGenerator) { 258 259 super(); 260 setToolTipGenerator(toolTipGenerator); 261 setURLGenerator(urlGenerator); 262 if ((type & SHAPES) != 0) { 263 this.baseShapesVisible = true; 264 } 265 if ((type & LINES) != 0) { 266 this.plotLines = true; 267 } 268 if ((type & IMAGES) != 0) { 269 this.plotImages = true; 270 } 271 if ((type & DISCONTINUOUS) != 0) { 272 this.plotDiscontinuous = true; 273 } 274 275 this.shapesFilled = null; 276 this.seriesShapesFilled = new BooleanList(); 277 this.baseShapesFilled = true; 278 this.legendLine = new Line2D.Double (-7.0, 0.0, 7.0, 0.0); 279 this.drawSeriesLineAsPath = false; 280 } 281 282 287 public boolean getBaseShapesVisible() { 288 return this.baseShapesVisible; 289 } 290 291 297 public void setBaseShapesVisible(boolean flag) { 298 if (this.baseShapesVisible != flag) { 299 this.baseShapesVisible = flag; 300 notifyListeners(new RendererChangeEvent(this)); 301 } 302 } 303 304 306 319 public boolean getItemShapeFilled(int series, int item) { 320 return getSeriesShapesFilled(series); 321 } 322 323 331 public boolean getSeriesShapesFilled(int series) { 332 333 if (this.shapesFilled != null) { 335 return this.shapesFilled.booleanValue(); 336 } 337 338 Boolean flag = this.seriesShapesFilled.getBoolean(series); 340 if (flag != null) { 341 return flag.booleanValue(); 342 } 343 else { 344 return this.baseShapesFilled; 345 } 346 347 } 348 349 354 public void setShapesFilled(boolean filled) { 355 setShapesFilled(BooleanUtilities.valueOf(filled)); 357 } 358 359 364 public void setShapesFilled(Boolean filled) { 365 this.shapesFilled = filled; 366 } 367 368 374 public void setSeriesShapesFilled(int series, Boolean flag) { 375 this.seriesShapesFilled.setBoolean(series, flag); 376 } 377 378 383 public boolean getBaseShapesFilled() { 384 return this.baseShapesFilled; 385 } 386 387 392 public void setBaseShapesFilled(boolean flag) { 393 this.baseShapesFilled = flag; 394 } 395 396 401 public boolean getPlotLines() { 402 return this.plotLines; 403 } 404 405 411 public void setPlotLines(boolean flag) { 412 if (this.plotLines != flag) { 413 this.plotLines = flag; 414 notifyListeners(new RendererChangeEvent(this)); 415 } 416 } 417 418 423 public UnitType getGapThresholdType() { 424 return this.gapThresholdType; 425 } 426 427 432 public void setGapThresholdType(UnitType thresholdType) { 433 if (thresholdType == null) { 434 throw new IllegalArgumentException ( 435 "Null 'thresholdType' argument."); 436 } 437 this.gapThresholdType = thresholdType; 438 notifyListeners(new RendererChangeEvent(this)); 439 } 440 441 446 public double getGapThreshold() { 447 return this.gapThreshold; 448 } 449 450 455 public void setGapThreshold(double t) { 456 this.gapThreshold = t; 457 notifyListeners(new RendererChangeEvent(this)); 458 } 459 460 465 public boolean getPlotImages() { 466 return this.plotImages; 467 } 468 469 475 public void setPlotImages(boolean flag) { 476 if (this.plotImages != flag) { 477 this.plotImages = flag; 478 notifyListeners(new RendererChangeEvent(this)); 479 } 480 } 481 482 487 public boolean getPlotDiscontinuous() { 488 return this.plotDiscontinuous; 489 } 490 491 497 public boolean getDrawSeriesLineAsPath() { 498 return this.drawSeriesLineAsPath; 499 } 500 501 507 public void setDrawSeriesLineAsPath(boolean flag) { 508 this.drawSeriesLineAsPath = flag; 509 } 510 511 516 public Shape getLegendLine() { 517 return this.legendLine; 518 } 519 520 526 public void setLegendLine(Shape line) { 527 if (line == null) { 528 throw new IllegalArgumentException ("Null 'line' argument."); 529 } 530 this.legendLine = line; 531 notifyListeners(new RendererChangeEvent(this)); 532 } 533 534 542 public LegendItem getLegendItem(int datasetIndex, int series) { 543 XYPlot plot = getPlot(); 544 if (plot == null) { 545 return null; 546 } 547 LegendItem result = null; 548 XYDataset dataset = plot.getDataset(datasetIndex); 549 if (dataset != null) { 550 if (getItemVisible(series, 0)) { 551 String label = getLegendItemLabelGenerator().generateLabel( 552 dataset, series); 553 String description = label; 554 String toolTipText = null; 555 if (getLegendItemToolTipGenerator() != null) { 556 toolTipText = getLegendItemToolTipGenerator().generateLabel( 557 dataset, series); 558 } 559 String urlText = null; 560 if (getLegendItemURLGenerator() != null) { 561 urlText = getLegendItemURLGenerator().generateLabel( 562 dataset, series); 563 } 564 Shape shape = getSeriesShape(series); 565 boolean shapeFilled = getSeriesShapesFilled(series); 566 Paint paint = getSeriesPaint(series); 567 Paint linePaint = paint; 568 Stroke lineStroke = getSeriesStroke(series); 569 result = new LegendItem(label, description, toolTipText, 570 urlText, this.baseShapesVisible, shape, shapeFilled, 571 paint, !shapeFilled, paint, lineStroke, 572 this.plotLines, this.legendLine, lineStroke, linePaint); 573 } 574 } 575 return result; 576 } 577 578 583 public static class State extends XYItemRendererState { 584 585 586 public GeneralPath seriesPath; 587 588 589 private int seriesIndex; 590 591 595 private boolean lastPointGood; 596 597 602 public State(PlotRenderingInfo info) { 603 super(info); 604 } 605 606 612 public boolean isLastPointGood() { 613 return this.lastPointGood; 614 } 615 616 622 public void setLastPointGood(boolean good) { 623 this.lastPointGood = good; 624 } 625 626 631 public int getSeriesIndex() { 632 return seriesIndex; 633 } 634 635 640 public void setSeriesIndex(int index) { 641 this.seriesIndex = index; 642 } 643 } 644 645 661 public XYItemRendererState initialise(Graphics2D g2, 662 Rectangle2D dataArea, 663 XYPlot plot, 664 XYDataset data, 665 PlotRenderingInfo info) { 666 667 State state = new State(info); 668 state.seriesPath = new GeneralPath (); 669 state.seriesIndex = -1; 670 return state; 671 672 } 673 674 692 public void drawItem(Graphics2D g2, 693 XYItemRendererState state, 694 Rectangle2D dataArea, 695 PlotRenderingInfo info, 696 XYPlot plot, 697 ValueAxis domainAxis, 698 ValueAxis rangeAxis, 699 XYDataset dataset, 700 int series, 701 int item, 702 CrosshairState crosshairState, 703 int pass) { 704 705 boolean itemVisible = getItemVisible(series, item); 706 707 Shape entityArea = null; 709 EntityCollection entities = null; 710 if (info != null) { 711 entities = info.getOwner().getEntityCollection(); 712 } 713 714 PlotOrientation orientation = plot.getOrientation(); 715 Paint paint = getItemPaint(series, item); 716 Stroke seriesStroke = getItemStroke(series, item); 717 g2.setPaint(paint); 718 g2.setStroke(seriesStroke); 719 720 double x1 = dataset.getXValue(series, item); 722 double y1 = dataset.getYValue(series, item); 723 if (Double.isNaN(x1) || Double.isNaN(y1)) { 724 itemVisible = false; 725 } 726 727 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 728 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 729 double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); 730 double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation); 731 732 if (getPlotLines()) { 733 if (this.drawSeriesLineAsPath) { 734 State s = (State) state; 735 if (s.getSeriesIndex() != series) { 736 s.seriesPath.reset(); 738 s.lastPointGood = false; 739 s.setSeriesIndex(series); 740 } 741 742 if (itemVisible && !Double.isNaN(transX1) 744 && !Double.isNaN(transY1)) { 745 float x = (float) transX1; 746 float y = (float) transY1; 747 if (orientation == PlotOrientation.HORIZONTAL) { 748 x = (float) transY1; 749 y = (float) transX1; 750 } 751 if (s.isLastPointGood()) { 752 s.seriesPath.lineTo(x, y); 754 } 755 else { 756 s.seriesPath.moveTo(x, y); 757 } 758 s.setLastPointGood(true); 759 } 760 else { 761 s.setLastPointGood(false); 762 } 763 if (item == dataset.getItemCount(series) - 1) { 764 if (s.seriesIndex == series) { 765 g2.setStroke(getSeriesStroke(series)); 767 g2.setPaint(getSeriesPaint(series)); 768 g2.draw(s.seriesPath); 769 } 770 } 771 } 772 773 else if (item != 0 && itemVisible) { 774 double x0 = dataset.getXValue(series, item - 1); 776 double y0 = dataset.getYValue(series, item - 1); 777 if (!Double.isNaN(x0) && !Double.isNaN(y0)) { 778 boolean drawLine = true; 779 if (getPlotDiscontinuous()) { 780 int numX = dataset.getItemCount(series); 783 double minX = dataset.getXValue(series, 0); 784 double maxX = dataset.getXValue(series, numX - 1); 785 if (this.gapThresholdType == UnitType.ABSOLUTE) { 786 drawLine = Math.abs(x1 - x0) <= this.gapThreshold; 787 } 788 else { 789 drawLine = Math.abs(x1 - x0) <= ((maxX - minX) 790 / numX * getGapThreshold()); 791 } 792 } 793 if (drawLine) { 794 double transX0 = domainAxis.valueToJava2D(x0, dataArea, 795 xAxisLocation); 796 double transY0 = rangeAxis.valueToJava2D(y0, dataArea, 797 yAxisLocation); 798 799 if (Double.isNaN(transX0) || Double.isNaN(transY0) 801 || Double.isNaN(transX1) || Double.isNaN(transY1)) { 802 return; 803 } 804 805 if (orientation == PlotOrientation.HORIZONTAL) { 806 state.workingLine.setLine(transY0, transX0, 807 transY1, transX1); 808 } 809 else if (orientation == PlotOrientation.VERTICAL) { 810 state.workingLine.setLine(transX0, transY0, 811 transX1, transY1); 812 } 813 814 if (state.workingLine.intersects(dataArea)) { 815 g2.draw(state.workingLine); 816 } 817 } 818 } 819 } 820 } 821 822 if (!itemVisible) { 826 return; 827 } 828 829 if (getBaseShapesVisible()) { 830 831 Shape shape = getItemShape(series, item); 832 if (orientation == PlotOrientation.HORIZONTAL) { 833 shape = ShapeUtilities.createTranslatedShape(shape, transY1, 834 transX1); 835 } 836 else if (orientation == PlotOrientation.VERTICAL) { 837 shape = ShapeUtilities.createTranslatedShape(shape, transX1, 838 transY1); 839 } 840 if (shape.intersects(dataArea)) { 841 if (getItemShapeFilled(series, item)) { 842 g2.fill(shape); 843 } 844 else { 845 g2.draw(shape); 846 } 847 } 848 entityArea = shape; 849 850 } 851 852 if (getPlotImages()) { 853 Image image = getImage(plot, series, item, transX1, transY1); 854 if (image != null) { 855 Point hotspot = getImageHotspot(plot, series, item, transX1, 856 transY1, image); 857 g2.drawImage(image, (int) (transX1 - hotspot.getX()), 858 (int) (transY1 - hotspot.getY()), null); 859 entityArea = new Rectangle2D.Double (transX1 - hotspot.getX(), 860 transY1 - hotspot.getY(), image.getWidth(null), 861 image.getHeight(null)); 862 } 863 864 } 865 866 if (isItemLabelVisible(series, item)) { 868 double xx = transX1; 869 double yy = transY1; 870 if (orientation == PlotOrientation.HORIZONTAL) { 871 xx = transY1; 872 yy = transX1; 873 } 874 drawItemLabel(g2, orientation, dataset, series, item, xx, yy, 875 (y1 < 0.0)); 876 } 877 878 updateCrosshairValues(crosshairState, x1, y1, transX1, transY1, 879 orientation); 880 881 if (entities != null) { 883 addEntity(entities, entityArea, dataset, series, item, 884 transX1, transY1); 885 } 886 887 } 888 889 896 public boolean equals(Object obj) { 897 898 if (obj == this) { 899 return true; 900 } 901 if (!(obj instanceof StandardXYItemRenderer)) { 902 return false; 903 } 904 if (!super.equals(obj)) { 905 return false; 906 } 907 StandardXYItemRenderer that = (StandardXYItemRenderer) obj; 908 if (this.baseShapesVisible != that.baseShapesVisible) { 909 return false; 910 } 911 if (this.plotLines != that.plotLines) { 912 return false; 913 } 914 if (this.plotImages != that.plotImages) { 915 return false; 916 } 917 if (this.plotDiscontinuous != that.plotDiscontinuous) { 918 return false; 919 } 920 if (this.gapThresholdType != that.gapThresholdType) { 921 return false; 922 } 923 if (this.gapThreshold != that.gapThreshold) { 924 return false; 925 } 926 if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) { 927 return false; 928 } 929 if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) { 930 return false; 931 } 932 return true; 933 934 } 935 936 943 public Object clone() throws CloneNotSupportedException { 944 return super.clone(); 945 } 946 947 953 965 protected Image getImage(Plot plot, int series, int item, 966 double x, double y) { 967 return null; 971 } 972 973 990 protected Point getImageHotspot(Plot plot, int series, int item, 991 double x, double y, Image image) { 992 993 int height = image.getHeight(null); 994 int width = image.getWidth(null); 995 return new Point (width / 2, height / 2); 996 997 } 998 999 1007 private void readObject(ObjectInputStream stream) 1008 throws IOException , ClassNotFoundException { 1009 stream.defaultReadObject(); 1010 this.legendLine = SerialUtilities.readShape(stream); 1011 } 1012 1013 1020 private void writeObject(ObjectOutputStream stream) throws IOException { 1021 stream.defaultWriteObject(); 1022 SerialUtilities.writeShape(this.legendLine, stream); 1023 } 1024} 1025 | Popular Tags |