1 81 82 package org.jfree.chart.renderer.category; 83 84 import java.awt.BasicStroke ; 85 import java.awt.Color ; 86 import java.awt.Font ; 87 import java.awt.GradientPaint ; 88 import java.awt.Graphics2D ; 89 import java.awt.Paint ; 90 import java.awt.Shape ; 91 import java.awt.Stroke ; 92 import java.awt.geom.Line2D ; 93 import java.awt.geom.Point2D ; 94 import java.awt.geom.Rectangle2D ; 95 import java.io.Serializable ; 96 97 import org.jfree.chart.LegendItem; 98 import org.jfree.chart.axis.CategoryAxis; 99 import org.jfree.chart.axis.ValueAxis; 100 import org.jfree.chart.entity.EntityCollection; 101 import org.jfree.chart.event.RendererChangeEvent; 102 import org.jfree.chart.labels.CategoryItemLabelGenerator; 103 import org.jfree.chart.labels.ItemLabelAnchor; 104 import org.jfree.chart.labels.ItemLabelPosition; 105 import org.jfree.chart.plot.CategoryPlot; 106 import org.jfree.chart.plot.PlotOrientation; 107 import org.jfree.chart.plot.PlotRenderingInfo; 108 import org.jfree.data.Range; 109 import org.jfree.data.category.CategoryDataset; 110 import org.jfree.data.general.DatasetUtilities; 111 import org.jfree.text.TextUtilities; 112 import org.jfree.ui.GradientPaintTransformer; 113 import org.jfree.ui.RectangleEdge; 114 import org.jfree.ui.StandardGradientPaintTransformer; 115 import org.jfree.util.ObjectUtilities; 116 import org.jfree.util.PublicCloneable; 117 118 121 public class BarRenderer extends AbstractCategoryItemRenderer 122 implements Cloneable , PublicCloneable, Serializable { 123 124 125 private static final long serialVersionUID = 6000649414965887481L; 126 127 128 public static final double DEFAULT_ITEM_MARGIN = 0.20; 129 130 134 public static final double BAR_OUTLINE_WIDTH_THRESHOLD = 3.0; 135 136 137 private double itemMargin; 138 139 140 private boolean drawBarOutline; 141 142 143 private double maximumBarWidth; 144 145 146 private double minimumBarLength; 147 148 152 private GradientPaintTransformer gradientPaintTransformer; 153 154 158 private ItemLabelPosition positiveItemLabelPositionFallback; 159 160 164 private ItemLabelPosition negativeItemLabelPositionFallback; 165 166 167 private double upperClip; 168 170 171 private double lowerClip; 172 174 175 private double base; 176 177 181 private boolean includeBaseInRange; 182 183 186 public BarRenderer() { 187 super(); 188 this.base = 0.0; 189 this.includeBaseInRange = true; 190 this.itemMargin = DEFAULT_ITEM_MARGIN; 191 this.drawBarOutline = true; 192 this.maximumBarWidth = 1.0; 193 this.positiveItemLabelPositionFallback = null; 195 this.negativeItemLabelPositionFallback = null; 196 this.gradientPaintTransformer = new StandardGradientPaintTransformer(); 197 this.minimumBarLength = 0.0; 198 } 199 200 205 public double getBase() { 206 return this.base; 207 } 208 209 215 public void setBase(double base) { 216 this.base = base; 217 notifyListeners(new RendererChangeEvent(this)); 218 } 219 220 226 public double getItemMargin() { 227 return this.itemMargin; 228 } 229 230 238 public void setItemMargin(double percent) { 239 this.itemMargin = percent; 240 notifyListeners(new RendererChangeEvent(this)); 241 } 242 243 248 public boolean isDrawBarOutline() { 249 return this.drawBarOutline; 250 } 251 252 258 public void setDrawBarOutline(boolean draw) { 259 this.drawBarOutline = draw; 260 notifyListeners(new RendererChangeEvent(this)); 261 } 262 263 269 public double getMaximumBarWidth() { 270 return this.maximumBarWidth; 271 } 272 273 280 public void setMaximumBarWidth(double percent) { 281 this.maximumBarWidth = percent; 282 notifyListeners(new RendererChangeEvent(this)); 283 } 284 285 290 public double getMinimumBarLength() { 291 return this.minimumBarLength; 292 } 293 294 302 public void setMinimumBarLength(double min) { 303 this.minimumBarLength = min; 304 notifyListeners(new RendererChangeEvent(this)); 305 } 306 307 313 public GradientPaintTransformer getGradientPaintTransformer() { 314 return this.gradientPaintTransformer; 315 } 316 317 323 public void setGradientPaintTransformer( 324 GradientPaintTransformer transformer) { 325 this.gradientPaintTransformer = transformer; 326 notifyListeners(new RendererChangeEvent(this)); 327 } 328 329 335 public ItemLabelPosition getPositiveItemLabelPositionFallback() { 336 return this.positiveItemLabelPositionFallback; 337 } 338 339 346 public void setPositiveItemLabelPositionFallback( 347 ItemLabelPosition position) { 348 this.positiveItemLabelPositionFallback = position; 349 notifyListeners(new RendererChangeEvent(this)); 350 } 351 352 358 public ItemLabelPosition getNegativeItemLabelPositionFallback() { 359 return this.negativeItemLabelPositionFallback; 360 } 361 362 369 public void setNegativeItemLabelPositionFallback( 370 ItemLabelPosition position) { 371 this.negativeItemLabelPositionFallback = position; 372 notifyListeners(new RendererChangeEvent(this)); 373 } 374 375 385 public boolean getIncludeBaseInRange() { 386 return this.includeBaseInRange; 387 } 388 389 399 public void setIncludeBaseInRange(boolean include) { 400 if (this.includeBaseInRange != include) { 401 this.includeBaseInRange = include; 402 notifyListeners(new RendererChangeEvent(this)); 403 } 404 } 405 406 412 public double getLowerClip() { 413 return this.lowerClip; 415 } 416 417 423 public double getUpperClip() { 424 return this.upperClip; 426 } 427 428 441 public CategoryItemRendererState initialise(Graphics2D g2, 442 Rectangle2D dataArea, 443 CategoryPlot plot, 444 int rendererIndex, 445 PlotRenderingInfo info) { 446 447 CategoryItemRendererState state = super.initialise(g2, dataArea, plot, 448 rendererIndex, info); 449 450 ValueAxis rangeAxis = getRangeAxis(plot, rendererIndex); 452 this.lowerClip = rangeAxis.getRange().getLowerBound(); 453 this.upperClip = rangeAxis.getRange().getUpperBound(); 454 455 calculateBarWidth(plot, dataArea, rendererIndex, state); 457 458 return state; 459 460 } 461 462 470 protected void calculateBarWidth(CategoryPlot plot, 471 Rectangle2D dataArea, 472 int rendererIndex, 473 CategoryItemRendererState state) { 474 475 CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); 476 CategoryDataset dataset = plot.getDataset(rendererIndex); 477 if (dataset != null) { 478 int columns = dataset.getColumnCount(); 479 int rows = dataset.getRowCount(); 480 double space = 0.0; 481 PlotOrientation orientation = plot.getOrientation(); 482 if (orientation == PlotOrientation.HORIZONTAL) { 483 space = dataArea.getHeight(); 484 } 485 else if (orientation == PlotOrientation.VERTICAL) { 486 space = dataArea.getWidth(); 487 } 488 double maxWidth = space * getMaximumBarWidth(); 489 double categoryMargin = 0.0; 490 double currentItemMargin = 0.0; 491 if (columns > 1) { 492 categoryMargin = domainAxis.getCategoryMargin(); 493 } 494 if (rows > 1) { 495 currentItemMargin = getItemMargin(); 496 } 497 double used = space * (1 - domainAxis.getLowerMargin() 498 - domainAxis.getUpperMargin() 499 - categoryMargin - currentItemMargin); 500 if ((rows * columns) > 0) { 501 state.setBarWidth(Math.min(used / (rows * columns), maxWidth)); 502 } 503 else { 504 state.setBarWidth(Math.min(used, maxWidth)); 505 } 506 } 507 } 508 509 524 protected double calculateBarW0(CategoryPlot plot, 525 PlotOrientation orientation, 526 Rectangle2D dataArea, 527 CategoryAxis domainAxis, 528 CategoryItemRendererState state, 529 int row, 530 int column) { 531 double space = 0.0; 533 if (orientation == PlotOrientation.HORIZONTAL) { 534 space = dataArea.getHeight(); 535 } 536 else { 537 space = dataArea.getWidth(); 538 } 539 double barW0 = domainAxis.getCategoryStart(column, getColumnCount(), 540 dataArea, plot.getDomainAxisEdge()); 541 int seriesCount = getRowCount(); 542 int categoryCount = getColumnCount(); 543 if (seriesCount > 1) { 544 double seriesGap = space * getItemMargin() 545 / (categoryCount * (seriesCount - 1)); 546 double seriesW = calculateSeriesWidth(space, domainAxis, 547 categoryCount, seriesCount); 548 barW0 = barW0 + row * (seriesW + seriesGap) 549 + (seriesW / 2.0) - (state.getBarWidth() / 2.0); 550 } 551 else { 552 barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(), 553 dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() 554 / 2.0; 555 } 556 return barW0; 557 } 558 559 567 protected double[] calculateBarL0L1(double value) { 568 double lclip = getLowerClip(); 569 double uclip = getUpperClip(); 570 double barLow = Math.min(this.base, value); 571 double barHigh = Math.max(this.base, value); 572 if (barHigh < lclip) { return null; 574 } 575 if (barLow > uclip) { return null; 577 } 578 barLow = Math.max(barLow, lclip); 579 barHigh = Math.min(barHigh, uclip); 580 return new double[] {barLow, barHigh}; 581 } 582 583 594 public Range findRangeBounds(CategoryDataset dataset) { 595 Range result = DatasetUtilities.findRangeBounds(dataset); 596 if (result != null) { 597 if (this.includeBaseInRange) { 598 result = Range.expandToInclude(result, this.base); 599 } 600 } 601 return result; 602 } 603 604 612 public LegendItem getLegendItem(int datasetIndex, int series) { 613 614 CategoryPlot cp = getPlot(); 615 if (cp == null) { 616 return null; 617 } 618 619 CategoryDataset dataset; 620 dataset = cp.getDataset(datasetIndex); 621 String label = getLegendItemLabelGenerator().generateLabel(dataset, 622 series); 623 String description = label; 624 String toolTipText = null; 625 if (getLegendItemToolTipGenerator() != null) { 626 toolTipText = getLegendItemToolTipGenerator().generateLabel( 627 dataset, series); 628 } 629 String urlText = null; 630 if (getLegendItemURLGenerator() != null) { 631 urlText = getLegendItemURLGenerator().generateLabel(dataset, 632 series); 633 } 634 Shape shape = new Rectangle2D.Double (-4.0, -4.0, 8.0, 8.0); 635 Paint paint = getSeriesPaint(series); 636 Paint outlinePaint = getSeriesOutlinePaint(series); 637 Stroke outlineStroke = getSeriesOutlineStroke(series); 638 639 return new LegendItem(label, description, toolTipText, urlText, 640 true, shape, true, paint, 641 isDrawBarOutline(), outlinePaint, outlineStroke, 642 false, new Line2D.Float (), new BasicStroke (1.0f), 643 Color.black); 644 } 645 646 660 public void drawItem(Graphics2D g2, 661 CategoryItemRendererState state, 662 Rectangle2D dataArea, 663 CategoryPlot plot, 664 CategoryAxis domainAxis, 665 ValueAxis rangeAxis, 666 CategoryDataset dataset, 667 int row, 668 int column, 669 int pass) { 670 671 Number dataValue = dataset.getValue(row, column); 673 if (dataValue == null) { 674 return; 675 } 676 677 double value = dataValue.doubleValue(); 678 679 PlotOrientation orientation = plot.getOrientation(); 680 double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis, 681 state, row, column); 682 double[] barL0L1 = calculateBarL0L1(value); 683 if (barL0L1 == null) { 684 return; } 686 687 RectangleEdge edge = plot.getRangeAxisEdge(); 688 double transL0 = rangeAxis.valueToJava2D(barL0L1[0], dataArea, edge); 689 double transL1 = rangeAxis.valueToJava2D(barL0L1[1], dataArea, edge); 690 double barL0 = Math.min(transL0, transL1); 691 double barLength = Math.max(Math.abs(transL1 - transL0), 692 getMinimumBarLength()); 693 694 Rectangle2D bar = null; 696 if (orientation == PlotOrientation.HORIZONTAL) { 697 bar = new Rectangle2D.Double (barL0, barW0, barLength, 698 state.getBarWidth()); 699 } 700 else { 701 bar = new Rectangle2D.Double (barW0, barL0, state.getBarWidth(), 702 barLength); 703 } 704 Paint itemPaint = getItemPaint(row, column); 705 GradientPaintTransformer t = getGradientPaintTransformer(); 706 if (t != null && itemPaint instanceof GradientPaint ) { 707 itemPaint = t.transform((GradientPaint ) itemPaint, bar); 708 } 709 g2.setPaint(itemPaint); 710 g2.fill(bar); 711 712 if (isDrawBarOutline() 714 && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 715 Stroke stroke = getItemOutlineStroke(row, column); 716 Paint paint = getItemOutlinePaint(row, column); 717 if (stroke != null && paint != null) { 718 g2.setStroke(stroke); 719 g2.setPaint(paint); 720 g2.draw(bar); 721 } 722 } 723 724 CategoryItemLabelGenerator generator 725 = getItemLabelGenerator(row, column); 726 if (generator != null && isItemLabelVisible(row, column)) { 727 drawItemLabel(g2, dataset, row, column, plot, generator, bar, 728 (value < 0.0)); 729 } 730 731 EntityCollection entities = state.getEntityCollection(); 733 if (entities != null) { 734 addItemEntity(entities, dataset, row, column, bar); 735 } 736 737 } 738 739 749 protected double calculateSeriesWidth(double space, CategoryAxis axis, 750 int categories, int series) { 751 double factor = 1.0 - getItemMargin() - axis.getLowerMargin() 752 - axis.getUpperMargin(); 753 if (categories > 1) { 754 factor = factor - axis.getCategoryMargin(); 755 } 756 return (space * factor) / (categories * series); 757 } 758 759 772 protected void drawItemLabel(Graphics2D g2, 773 CategoryDataset data, 774 int row, 775 int column, 776 CategoryPlot plot, 777 CategoryItemLabelGenerator generator, 778 Rectangle2D bar, 779 boolean negative) { 780 781 String label = generator.generateLabel(data, row, column); 782 if (label == null) { 783 return; } 785 786 Font labelFont = getItemLabelFont(row, column); 787 g2.setFont(labelFont); 788 Paint paint = getItemLabelPaint(row, column); 789 g2.setPaint(paint); 790 791 ItemLabelPosition position = null; 793 if (!negative) { 794 position = getPositiveItemLabelPosition(row, column); 795 } 796 else { 797 position = getNegativeItemLabelPosition(row, column); 798 } 799 800 Point2D anchorPoint = calculateLabelAnchorPoint( 802 position.getItemLabelAnchor(), bar, plot.getOrientation()); 803 804 if (isInternalAnchor(position.getItemLabelAnchor())) { 805 Shape bounds = TextUtilities.calculateRotatedStringBounds(label, 806 g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), 807 position.getTextAnchor(), position.getAngle(), 808 position.getRotationAnchor()); 809 810 if (bounds != null) { 811 if (!bar.contains(bounds.getBounds2D())) { 812 if (!negative) { 813 position = getPositiveItemLabelPositionFallback(); 814 } 815 else { 816 position = getNegativeItemLabelPositionFallback(); 817 } 818 if (position != null) { 819 anchorPoint = calculateLabelAnchorPoint( 820 position.getItemLabelAnchor(), bar, 821 plot.getOrientation()); 822 } 823 } 824 } 825 826 } 827 828 if (position != null) { 829 TextUtilities.drawRotatedString(label, g2, 830 (float) anchorPoint.getX(), (float) anchorPoint.getY(), 831 position.getTextAnchor(), position.getAngle(), 832 position.getRotationAnchor()); 833 } 834 } 835 836 845 private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor, 846 Rectangle2D bar, 847 PlotOrientation orientation) { 848 849 Point2D result = null; 850 double offset = getItemLabelAnchorOffset(); 851 double x0 = bar.getX() - offset; 852 double x1 = bar.getX(); 853 double x2 = bar.getX() + offset; 854 double x3 = bar.getCenterX(); 855 double x4 = bar.getMaxX() - offset; 856 double x5 = bar.getMaxX(); 857 double x6 = bar.getMaxX() + offset; 858 859 double y0 = bar.getMaxY() + offset; 860 double y1 = bar.getMaxY(); 861 double y2 = bar.getMaxY() - offset; 862 double y3 = bar.getCenterY(); 863 double y4 = bar.getMinY() + offset; 864 double y5 = bar.getMinY(); 865 double y6 = bar.getMinY() - offset; 866 867 if (anchor == ItemLabelAnchor.CENTER) { 868 result = new Point2D.Double (x3, y3); 869 } 870 else if (anchor == ItemLabelAnchor.INSIDE1) { 871 result = new Point2D.Double (x4, y4); 872 } 873 else if (anchor == ItemLabelAnchor.INSIDE2) { 874 result = new Point2D.Double (x4, y4); 875 } 876 else if (anchor == ItemLabelAnchor.INSIDE3) { 877 result = new Point2D.Double (x4, y3); 878 } 879 else if (anchor == ItemLabelAnchor.INSIDE4) { 880 result = new Point2D.Double (x4, y2); 881 } 882 else if (anchor == ItemLabelAnchor.INSIDE5) { 883 result = new Point2D.Double (x4, y2); 884 } 885 else if (anchor == ItemLabelAnchor.INSIDE6) { 886 result = new Point2D.Double (x3, y2); 887 } 888 else if (anchor == ItemLabelAnchor.INSIDE7) { 889 result = new Point2D.Double (x2, y2); 890 } 891 else if (anchor == ItemLabelAnchor.INSIDE8) { 892 result = new Point2D.Double (x2, y2); 893 } 894 else if (anchor == ItemLabelAnchor.INSIDE9) { 895 result = new Point2D.Double (x2, y3); 896 } 897 else if (anchor == ItemLabelAnchor.INSIDE10) { 898 result = new Point2D.Double (x2, y4); 899 } 900 else if (anchor == ItemLabelAnchor.INSIDE11) { 901 result = new Point2D.Double (x2, y4); 902 } 903 else if (anchor == ItemLabelAnchor.INSIDE12) { 904 result = new Point2D.Double (x3, y4); 905 } 906 else if (anchor == ItemLabelAnchor.OUTSIDE1) { 907 result = new Point2D.Double (x5, y6); 908 } 909 else if (anchor == ItemLabelAnchor.OUTSIDE2) { 910 result = new Point2D.Double (x6, y5); 911 } 912 else if (anchor == ItemLabelAnchor.OUTSIDE3) { 913 result = new Point2D.Double (x6, y3); 914 } 915 else if (anchor == ItemLabelAnchor.OUTSIDE4) { 916 result = new Point2D.Double (x6, y1); 917 } 918 else if (anchor == ItemLabelAnchor.OUTSIDE5) { 919 result = new Point2D.Double (x5, y0); 920 } 921 else if (anchor == ItemLabelAnchor.OUTSIDE6) { 922 result = new Point2D.Double (x3, y0); 923 } 924 else if (anchor == ItemLabelAnchor.OUTSIDE7) { 925 result = new Point2D.Double (x1, y0); 926 } 927 else if (anchor == ItemLabelAnchor.OUTSIDE8) { 928 result = new Point2D.Double (x0, y1); 929 } 930 else if (anchor == ItemLabelAnchor.OUTSIDE9) { 931 result = new Point2D.Double (x0, y3); 932 } 933 else if (anchor == ItemLabelAnchor.OUTSIDE10) { 934 result = new Point2D.Double (x0, y5); 935 } 936 else if (anchor == ItemLabelAnchor.OUTSIDE11) { 937 result = new Point2D.Double (x1, y6); 938 } 939 else if (anchor == ItemLabelAnchor.OUTSIDE12) { 940 result = new Point2D.Double (x3, y6); 941 } 942 943 return result; 944 945 } 946 947 954 private boolean isInternalAnchor(ItemLabelAnchor anchor) { 955 return anchor == ItemLabelAnchor.CENTER 956 || anchor == ItemLabelAnchor.INSIDE1 957 || anchor == ItemLabelAnchor.INSIDE2 958 || anchor == ItemLabelAnchor.INSIDE3 959 || anchor == ItemLabelAnchor.INSIDE4 960 || anchor == ItemLabelAnchor.INSIDE5 961 || anchor == ItemLabelAnchor.INSIDE6 962 || anchor == ItemLabelAnchor.INSIDE7 963 || anchor == ItemLabelAnchor.INSIDE8 964 || anchor == ItemLabelAnchor.INSIDE9 965 || anchor == ItemLabelAnchor.INSIDE10 966 || anchor == ItemLabelAnchor.INSIDE11 967 || anchor == ItemLabelAnchor.INSIDE12; 968 } 969 970 977 public boolean equals(Object obj) { 978 979 if (obj == this) { 980 return true; 981 } 982 if (!(obj instanceof BarRenderer)) { 983 return false; 984 } 985 if (!super.equals(obj)) { 986 return false; 987 } 988 BarRenderer that = (BarRenderer) obj; 989 if (this.base != that.base) { 990 return false; 991 } 992 if (this.itemMargin != that.itemMargin) { 993 return false; 994 } 995 if (this.drawBarOutline != that.drawBarOutline) { 996 return false; 997 } 998 if (this.maximumBarWidth != that.maximumBarWidth) { 999 return false; 1000 } 1001 if (this.minimumBarLength != that.minimumBarLength) { 1002 return false; 1003 } 1004 if (!ObjectUtilities.equal(this.gradientPaintTransformer, 1005 that.gradientPaintTransformer)) { 1006 return false; 1007 } 1008 if (!ObjectUtilities.equal(this.positiveItemLabelPositionFallback, 1009 that.positiveItemLabelPositionFallback)) { 1010 return false; 1011 } 1012 if (!ObjectUtilities.equal(this.negativeItemLabelPositionFallback, 1013 that.negativeItemLabelPositionFallback)) { 1014 return false; 1015 } 1016 return true; 1017 1018 } 1019 1020} 1021 | Popular Tags |