1 62 63 package org.jfree.chart.renderer.xy; 64 65 import java.awt.Color ; 66 import java.awt.Graphics2D ; 67 import java.awt.Paint ; 68 import java.awt.Shape ; 69 import java.awt.Stroke ; 70 import java.awt.geom.GeneralPath ; 71 import java.awt.geom.Line2D ; 72 import java.awt.geom.Rectangle2D ; 73 import java.io.IOException ; 74 import java.io.ObjectInputStream ; 75 import java.io.ObjectOutputStream ; 76 import java.io.Serializable ; 77 78 import org.jfree.chart.LegendItem; 79 import org.jfree.chart.axis.ValueAxis; 80 import org.jfree.chart.entity.EntityCollection; 81 import org.jfree.chart.entity.XYItemEntity; 82 import org.jfree.chart.event.RendererChangeEvent; 83 import org.jfree.chart.labels.XYToolTipGenerator; 84 import org.jfree.chart.plot.CrosshairState; 85 import org.jfree.chart.plot.PlotOrientation; 86 import org.jfree.chart.plot.PlotRenderingInfo; 87 import org.jfree.chart.plot.XYPlot; 88 import org.jfree.data.xy.XYDataset; 89 import org.jfree.io.SerialUtilities; 90 import org.jfree.ui.RectangleEdge; 91 import org.jfree.util.PaintUtilities; 92 import org.jfree.util.PublicCloneable; 93 import org.jfree.util.ShapeUtilities; 94 95 104 public class XYDifferenceRenderer extends AbstractXYItemRenderer 105 implements XYItemRenderer, 106 Cloneable , 107 PublicCloneable, 108 Serializable { 109 110 111 private static final long serialVersionUID = -8447915602375584857L; 112 113 114 private transient Paint positivePaint; 115 116 117 private transient Paint negativePaint; 118 119 120 private boolean shapesVisible; 121 122 123 private transient Shape legendLine; 124 125 128 public XYDifferenceRenderer() { 129 this(Color.green, Color.red, false); 130 } 131 132 141 public XYDifferenceRenderer(Paint positivePaint, Paint negativePaint, 142 boolean shapes) { 143 if (positivePaint == null) { 144 throw new IllegalArgumentException ( 145 "Null 'positivePaint' argument."); 146 } 147 if (negativePaint == null) { 148 throw new IllegalArgumentException ( 149 "Null 'negativePaint' argument."); 150 } 151 this.positivePaint = positivePaint; 152 this.negativePaint = negativePaint; 153 this.shapesVisible = shapes; 154 this.legendLine = new Line2D.Double (-7.0, 0.0, 7.0, 0.0); 155 } 156 157 162 public Paint getPositivePaint() { 163 return this.positivePaint; 164 } 165 166 171 public void setPositivePaint(Paint paint) { 172 if (paint == null) { 173 throw new IllegalArgumentException ("Null 'paint' argument."); 174 } 175 this.positivePaint = paint; 176 notifyListeners(new RendererChangeEvent(this)); 177 } 178 179 184 public Paint getNegativePaint() { 185 return this.negativePaint; 186 } 187 188 193 public void setNegativePaint(Paint paint) { 194 if (paint == null) { 195 throw new IllegalArgumentException ("Null 'paint' argument."); 196 } 197 this.negativePaint = paint; 198 notifyListeners(new RendererChangeEvent(this)); 199 } 200 201 207 public boolean getShapesVisible() { 208 return this.shapesVisible; 209 } 210 211 217 public void setShapesVisible(boolean flag) { 218 this.shapesVisible = flag; 219 notifyListeners(new RendererChangeEvent(this)); 220 } 221 222 227 public Shape getLegendLine() { 228 return this.legendLine; 229 } 230 231 237 public void setLegendLine(Shape line) { 238 if (line == null) { 239 throw new IllegalArgumentException ("Null 'line' argument."); 240 } 241 this.legendLine = line; 242 notifyListeners(new RendererChangeEvent(this)); 243 } 244 245 261 public XYItemRendererState initialise(Graphics2D g2, 262 Rectangle2D dataArea, 263 XYPlot plot, 264 XYDataset data, 265 PlotRenderingInfo info) { 266 267 return super.initialise(g2, dataArea, plot, data, info); 268 269 } 270 271 277 public int getPassCount() { 278 return 2; 279 } 280 281 299 public void drawItem(Graphics2D g2, 300 XYItemRendererState state, 301 Rectangle2D dataArea, 302 PlotRenderingInfo info, 303 XYPlot plot, 304 ValueAxis domainAxis, 305 ValueAxis rangeAxis, 306 XYDataset dataset, 307 int series, 308 int item, 309 CrosshairState crosshairState, 310 int pass) { 311 312 if (pass == 0) { 313 drawItemPass0(g2, dataArea, info, plot, domainAxis, rangeAxis, 314 dataset, series, item, crosshairState); 315 } 316 else if (pass == 1) { 317 drawItemPass1(g2, dataArea, info, plot, domainAxis, rangeAxis, 318 dataset, series, item, crosshairState); 319 } 320 321 } 322 323 339 protected void drawItemPass0(Graphics2D g2, 340 Rectangle2D dataArea, 341 PlotRenderingInfo info, 342 XYPlot plot, 343 ValueAxis domainAxis, 344 ValueAxis rangeAxis, 345 XYDataset dataset, 346 int series, 347 int item, 348 CrosshairState crosshairState) { 349 350 if (series == 0) { 351 352 PlotOrientation orientation = plot.getOrientation(); 353 RectangleEdge domainAxisLocation = plot.getDomainAxisEdge(); 354 RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); 355 356 double y0 = dataset.getYValue(0, item); 357 double x1 = dataset.getXValue(1, item); 358 double y1 = dataset.getYValue(1, item); 359 360 double transY0 = rangeAxis.valueToJava2D(y0, dataArea, 361 rangeAxisLocation); 362 double transX1 = domainAxis.valueToJava2D(x1, dataArea, 363 domainAxisLocation); 364 double transY1 = rangeAxis.valueToJava2D(y1, dataArea, 365 rangeAxisLocation); 366 367 if (item > 0) { 368 double prevx0 = dataset.getXValue(0, item - 1); 369 double prevy0 = dataset.getYValue(0, item - 1); 370 double prevy1 = dataset.getYValue(1, item - 1); 371 372 double prevtransX0 = domainAxis.valueToJava2D(prevx0, dataArea, 373 domainAxisLocation); 374 double prevtransY0 = rangeAxis.valueToJava2D(prevy0, dataArea, 375 rangeAxisLocation); 376 double prevtransY1 = rangeAxis.valueToJava2D(prevy1, dataArea, 377 rangeAxisLocation); 378 379 Shape positive = getPositiveArea((float) prevtransX0, 380 (float) prevtransY0, (float) prevtransY1, 381 (float) transX1, (float) transY0, (float) transY1, 382 orientation); 383 if (positive != null) { 384 g2.setPaint(getPositivePaint()); 385 g2.fill(positive); 386 } 387 388 Shape negative = getNegativeArea((float) prevtransX0, 389 (float) prevtransY0, (float) prevtransY1, 390 (float) transX1, (float) transY0, (float) transY1, 391 orientation); 392 393 if (negative != null) { 394 g2.setPaint(getNegativePaint()); 395 g2.fill(negative); 396 } 397 } 398 } 399 400 } 401 402 420 protected void drawItemPass1(Graphics2D g2, 421 Rectangle2D dataArea, 422 PlotRenderingInfo info, 423 XYPlot plot, 424 ValueAxis domainAxis, 425 ValueAxis rangeAxis, 426 XYDataset dataset, 427 int series, 428 int item, 429 CrosshairState crosshairState) { 430 431 Shape entityArea = null; 432 EntityCollection entities = null; 433 if (info != null) { 434 entities = info.getOwner().getEntityCollection(); 435 } 436 437 Paint seriesPaint = getItemPaint(series, item); 438 Stroke seriesStroke = getItemStroke(series, item); 439 g2.setPaint(seriesPaint); 440 g2.setStroke(seriesStroke); 441 442 if (series == 0) { 443 444 PlotOrientation orientation = plot.getOrientation(); 445 RectangleEdge domainAxisLocation = plot.getDomainAxisEdge(); 446 RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); 447 448 double x0 = dataset.getXValue(0, item); 449 double y0 = dataset.getYValue(0, item); 450 double x1 = dataset.getXValue(1, item); 451 double y1 = dataset.getYValue(1, item); 452 453 double transX0 = domainAxis.valueToJava2D(x0, dataArea, 454 domainAxisLocation); 455 double transY0 = rangeAxis.valueToJava2D(y0, dataArea, 456 rangeAxisLocation); 457 double transX1 = domainAxis.valueToJava2D(x1, dataArea, 458 domainAxisLocation); 459 double transY1 = rangeAxis.valueToJava2D(y1, dataArea, 460 rangeAxisLocation); 461 462 if (item > 0) { 463 double prevx0 = dataset.getXValue(0, item - 1); 465 double prevy0 = dataset.getYValue(0, item - 1); 466 double prevx1 = dataset.getXValue(1, item - 1); 467 double prevy1 = dataset.getYValue(1, item - 1); 468 469 double prevtransX0 = domainAxis.valueToJava2D(prevx0, dataArea, 470 domainAxisLocation); 471 double prevtransY0 = rangeAxis.valueToJava2D(prevy0, dataArea, 472 rangeAxisLocation); 473 double prevtransX1 = domainAxis.valueToJava2D(prevx1, dataArea, 474 domainAxisLocation); 475 double prevtransY1 = rangeAxis.valueToJava2D(prevy1, dataArea, 476 rangeAxisLocation); 477 478 Line2D line0 = null; 479 Line2D line1 = null; 480 if (orientation == PlotOrientation.HORIZONTAL) { 481 line0 = new Line2D.Double (transY0, transX0, prevtransY0, 482 prevtransX0); 483 line1 = new Line2D.Double (transY1, transX1, prevtransY1, 484 prevtransX1); 485 } 486 else if (orientation == PlotOrientation.VERTICAL) { 487 line0 = new Line2D.Double (transX0, transY0, prevtransX0, 488 prevtransY0); 489 line1 = new Line2D.Double (transX1, transY1, prevtransX1, 490 prevtransY1); 491 } 492 if (line0 != null && line0.intersects(dataArea)) { 493 g2.setPaint(getItemPaint(series, item)); 494 g2.setStroke(getItemStroke(series, item)); 495 g2.draw(line0); 496 } 497 if (line1 != null && line1.intersects(dataArea)) { 498 g2.setPaint(getItemPaint(1, item)); 499 g2.setStroke(getItemStroke(1, item)); 500 g2.draw(line1); 501 } 502 } 503 504 if (getShapesVisible()) { 505 Shape shape0 = getItemShape(series, item); 506 if (orientation == PlotOrientation.HORIZONTAL) { 507 shape0 = ShapeUtilities.createTranslatedShape(shape0, 508 transY0, transX0); 509 } 510 else { shape0 = ShapeUtilities.createTranslatedShape(shape0, 512 transX0, transY0); 513 } 514 if (shape0.intersects(dataArea)) { 515 g2.setPaint(getItemPaint(series, item)); 516 g2.fill(shape0); 517 } 518 entityArea = shape0; 519 520 if (entities != null) { 522 if (entityArea == null) { 523 entityArea = new Rectangle2D.Double (transX0 - 2, 524 transY0 - 2, 4, 4); 525 } 526 String tip = null; 527 XYToolTipGenerator generator = getToolTipGenerator(series, 528 item); 529 if (generator != null) { 530 tip = generator.generateToolTip(dataset, series, item); 531 } 532 String url = null; 533 if (getURLGenerator() != null) { 534 url = getURLGenerator().generateURL(dataset, series, 535 item); 536 } 537 XYItemEntity entity = new XYItemEntity(entityArea, dataset, 538 series, item, tip, url); 539 entities.add(entity); 540 } 541 542 Shape shape1 = getItemShape(series + 1, item); 543 if (orientation == PlotOrientation.HORIZONTAL) { 544 shape1 = ShapeUtilities.createTranslatedShape(shape1, 545 transY1, transX1); 546 } 547 else { shape1 = ShapeUtilities.createTranslatedShape(shape1, 549 transX1, transY1); 550 } 551 if (shape1.intersects(dataArea)) { 552 g2.setPaint(getItemPaint(series + 1, item)); 553 g2.fill(shape1); 554 } 555 entityArea = shape1; 556 557 if (entities != null) { 559 if (entityArea == null) { 560 entityArea = new Rectangle2D.Double (transX1 - 2, 561 transY1 - 2, 4, 4); 562 } 563 String tip = null; 564 XYToolTipGenerator generator = getToolTipGenerator(series, 565 item); 566 if (generator != null) { 567 tip = generator.generateToolTip(dataset, series + 1, 568 item); 569 } 570 String url = null; 571 if (getURLGenerator() != null) { 572 url = getURLGenerator().generateURL(dataset, 573 series + 1, item); 574 } 575 XYItemEntity entity = new XYItemEntity(entityArea, dataset, 576 series + 1, item, tip, url); 577 entities.add(entity); 578 } 579 } 580 updateCrosshairValues(crosshairState, x1, y1, transX1, transY1, 581 orientation); 582 } 583 584 } 585 586 599 protected Shape getPositiveArea(float x0, float y0A, float y0B, 600 float x1, float y1A, float y1B, 601 PlotOrientation orientation) { 602 603 Shape result = null; 604 605 boolean startsNegative = (y0A >= y0B); 606 boolean endsNegative = (y1A >= y1B); 607 if (orientation == PlotOrientation.HORIZONTAL) { 608 startsNegative = (y0B >= y0A); 609 endsNegative = (y1B >= y1A); 610 } 611 612 if (startsNegative) { if (endsNegative) { 614 result = null; 616 } 617 else { 618 float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B); 620 GeneralPath area = new GeneralPath (); 621 if (orientation == PlotOrientation.HORIZONTAL) { 622 area.moveTo(y1A, x1); 623 area.lineTo(p[1], p[0]); 624 area.lineTo(y1B, x1); 625 area.closePath(); 626 } 627 else if (orientation == PlotOrientation.VERTICAL) { 628 area.moveTo(x1, y1A); 629 area.lineTo(p[0], p[1]); 630 area.lineTo(x1, y1B); 631 area.closePath(); 632 } 633 result = area; 634 } 635 } 636 else { if (endsNegative) { 638 float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B); 640 GeneralPath area = new GeneralPath (); 641 if (orientation == PlotOrientation.HORIZONTAL) { 642 area.moveTo(y0A, x0); 643 area.lineTo(p[1], p[0]); 644 area.lineTo(y0B, x0); 645 area.closePath(); 646 } 647 else if (orientation == PlotOrientation.VERTICAL) { 648 area.moveTo(x0, y0A); 649 area.lineTo(p[0], p[1]); 650 area.lineTo(x0, y0B); 651 area.closePath(); 652 } 653 result = area; 654 655 } 656 else { 657 GeneralPath area = new GeneralPath (); 658 if (orientation == PlotOrientation.HORIZONTAL) { 659 area.moveTo(y0A, x0); 660 area.lineTo(y1A, x1); 661 area.lineTo(y1B, x1); 662 area.lineTo(y0B, x0); 663 area.closePath(); 664 } 665 else if (orientation == PlotOrientation.VERTICAL) { 666 area.moveTo(x0, y0A); 667 area.lineTo(x1, y1A); 668 area.lineTo(x1, y1B); 669 area.lineTo(x0, y0B); 670 area.closePath(); 671 } 672 result = area; 673 } 674 675 } 676 677 return result; 678 679 } 680 681 694 protected Shape getNegativeArea(float x0, float y0A, float y0B, 695 float x1, float y1A, float y1B, 696 PlotOrientation orientation) { 697 698 Shape result = null; 699 700 boolean startsNegative = (y0A >= y0B); 701 boolean endsNegative = (y1A >= y1B); 702 if (orientation == PlotOrientation.HORIZONTAL) { 703 startsNegative = (y0B >= y0A); 704 endsNegative = (y1B >= y1A); 705 } 706 if (startsNegative) { if (endsNegative) { GeneralPath area = new GeneralPath (); 709 if (orientation == PlotOrientation.HORIZONTAL) { 710 area.moveTo(y0A, x0); 711 area.lineTo(y1A, x1); 712 area.lineTo(y1B, x1); 713 area.lineTo(y0B, x0); 714 area.closePath(); 715 } 716 else if (orientation == PlotOrientation.VERTICAL) { 717 area.moveTo(x0, y0A); 718 area.lineTo(x1, y1A); 719 area.lineTo(x1, y1B); 720 area.lineTo(x0, y0B); 721 area.closePath(); 722 } 723 result = area; 724 } 725 else { float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B); 727 GeneralPath area = new GeneralPath (); 728 if (orientation == PlotOrientation.HORIZONTAL) { 729 area.moveTo(y0A, x0); 730 area.lineTo(p[1], p[0]); 731 area.lineTo(y0B, x0); 732 area.closePath(); 733 } 734 else if (orientation == PlotOrientation.VERTICAL) { 735 area.moveTo(x0, y0A); 736 area.lineTo(p[0], p[1]); 737 area.lineTo(x0, y0B); 738 area.closePath(); 739 } 740 result = area; 741 } 742 } 743 else { 744 if (endsNegative) { 745 float[] p = getIntersection(x0, y0A, x1, y1A, x0, y0B, x1, y1B); 747 GeneralPath area = new GeneralPath (); 748 if (orientation == PlotOrientation.HORIZONTAL) { 749 area.moveTo(y1A, x1); 750 area.lineTo(p[1], p[0]); 751 area.lineTo(y1B, x1); 752 area.closePath(); 753 } 754 else if (orientation == PlotOrientation.VERTICAL) { 755 area.moveTo(x1, y1A); 756 area.lineTo(p[0], p[1]); 757 area.lineTo(x1, y1B); 758 area.closePath(); 759 } 760 result = area; 761 } 762 else { 763 } 765 766 } 767 768 return result; 769 770 } 771 772 786 private float[] getIntersection(float x1, float y1, float x2, float y2, 787 float x3, float y3, float x4, float y4) { 788 789 float n = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); 790 float d = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); 791 float u = n / d; 792 793 float[] result = new float[2]; 794 result[0] = x1 + u * (x2 - x1); 795 result[1] = y1 + u * (y2 - y1); 796 return result; 797 798 } 799 800 809 public LegendItem getLegendItem(int datasetIndex, int series) { 810 LegendItem result = null; 811 XYPlot p = getPlot(); 812 if (p != null) { 813 XYDataset dataset = p.getDataset(datasetIndex); 814 if (dataset != null) { 815 if (getItemVisible(series, 0)) { 816 String label = getLegendItemLabelGenerator().generateLabel( 817 dataset, series); 818 String description = label; 819 String toolTipText = null; 820 if (getLegendItemToolTipGenerator() != null) { 821 toolTipText 822 = getLegendItemToolTipGenerator().generateLabel( 823 dataset, series); 824 } 825 String urlText = null; 826 if (getLegendItemURLGenerator() != null) { 827 urlText = getLegendItemURLGenerator().generateLabel( 828 dataset, series); 829 } 830 Paint paint = getSeriesPaint(series); 831 Stroke stroke = getSeriesStroke(series); 832 Line2D line = new Line2D.Double (-7.0, 0.0, 7.0, 0.0); 834 result = new LegendItem(label, description, 835 toolTipText, urlText, line, stroke, paint); 836 } 837 } 838 839 } 840 841 return result; 842 843 } 844 845 852 public boolean equals(Object obj) { 853 if (obj == this) { 854 return true; 855 } 856 if (!(obj instanceof XYDifferenceRenderer)) { 857 return false; 858 } 859 if (!super.equals(obj)) { 860 return false; 861 } 862 XYDifferenceRenderer that = (XYDifferenceRenderer) obj; 863 if (!PaintUtilities.equal(this.positivePaint, that.positivePaint)) { 864 return false; 865 } 866 if (!PaintUtilities.equal(this.negativePaint, that.negativePaint)) { 867 return false; 868 } 869 if (this.shapesVisible != that.shapesVisible) { 870 return false; 871 } 872 if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) { 873 return false; 874 } 875 return true; 876 } 877 878 885 public Object clone() throws CloneNotSupportedException { 886 return super.clone(); 887 } 888 889 896 private void writeObject(ObjectOutputStream stream) throws IOException { 897 stream.defaultWriteObject(); 898 SerialUtilities.writePaint(this.positivePaint, stream); 899 SerialUtilities.writePaint(this.negativePaint, stream); 900 SerialUtilities.writeShape(this.legendLine, stream); 901 } 902 903 911 private void readObject(ObjectInputStream stream) 912 throws IOException , ClassNotFoundException { 913 stream.defaultReadObject(); 914 this.positivePaint = SerialUtilities.readPaint(stream); 915 this.negativePaint = SerialUtilities.readPaint(stream); 916 this.legendLine = SerialUtilities.readShape(stream); 917 } 918 919 } 920 | Popular Tags |