1 57 58 package org.jfree.chart.renderer; 59 60 import java.awt.Font ; 61 import java.awt.GradientPaint ; 62 import java.awt.Graphics2D ; 63 import java.awt.Paint ; 64 import java.awt.Shape ; 65 import java.awt.Stroke ; 66 import java.awt.geom.Point2D ; 67 import java.awt.geom.Rectangle2D ; 68 import java.io.Serializable ; 69 70 import org.jfree.chart.axis.CategoryAxis; 71 import org.jfree.chart.axis.ValueAxis; 72 import org.jfree.chart.entity.CategoryItemEntity; 73 import org.jfree.chart.entity.EntityCollection; 74 import org.jfree.chart.event.RendererChangeEvent; 75 import org.jfree.chart.labels.CategoryItemLabelGenerator; 76 import org.jfree.chart.plot.CategoryPlot; 77 import org.jfree.chart.plot.PlotOrientation; 78 import org.jfree.chart.plot.PlotRenderingInfo; 79 import org.jfree.data.CategoryDataset; 80 import org.jfree.ui.GradientPaintTransformer; 81 import org.jfree.ui.RectangleEdge; 82 import org.jfree.ui.RefineryUtilities; 83 import org.jfree.ui.StandardGradientPaintTransformer; 84 import org.jfree.util.PublicCloneable; 85 86 91 public class BarRenderer extends AbstractCategoryItemRenderer 92 implements Cloneable , PublicCloneable, Serializable { 93 94 95 public static final double DEFAULT_ITEM_MARGIN = 0.20; 96 97 98 public static final double BAR_OUTLINE_WIDTH_THRESHOLD = 3.0; 99 100 101 private double itemMargin; 102 103 104 private boolean drawBarOutline; 105 106 107 private double maxBarWidth; 108 109 110 private double minimumBarLength; 111 112 113 private GradientPaintTransformer gradientPaintTransformer; 114 115 116 private ItemLabelPosition positiveItemLabelPositionFallback; 117 118 119 private ItemLabelPosition negativeItemLabelPositionFallback; 120 121 122 private double upperClip; 123 124 125 private double lowerClip; 126 127 130 public BarRenderer() { 131 super(); 132 this.itemMargin = DEFAULT_ITEM_MARGIN; 133 this.drawBarOutline = true; 134 this.maxBarWidth = 1.0; this.positiveItemLabelPositionFallback = null; 136 this.negativeItemLabelPositionFallback = null; 137 this.gradientPaintTransformer = new StandardGradientPaintTransformer(); 138 this.minimumBarLength = 0.0; 139 } 140 141 146 public double getItemMargin() { 147 return this.itemMargin; 148 } 149 150 157 public void setItemMargin(double percent) { 158 this.itemMargin = percent; 159 firePropertyChanged("ItemMargin", null, null); 160 } 161 162 167 public boolean isDrawBarOutline() { 168 return this.drawBarOutline; 169 } 170 171 176 public void setDrawBarOutline(boolean draw) { 177 this.drawBarOutline = draw; 178 firePropertyChanged("DrawBarOutline", null, null); 179 } 180 181 186 public double getMaxBarWidth() { 187 return this.maxBarWidth; 188 } 189 190 196 public void setMaxBarWidth(double percent) { 197 this.maxBarWidth = percent; 198 notifyListeners(new RendererChangeEvent(this)); 199 } 200 201 public double getMinimumBarLength() { 202 return this.minimumBarLength; 203 } 204 205 public void setMinimumBarLength(double min) { 206 this.minimumBarLength = min; 207 notifyListeners(new RendererChangeEvent(this)); 208 } 209 210 216 public GradientPaintTransformer getGradientPaintTransformer() { 217 return this.gradientPaintTransformer; 218 } 219 220 226 public void setGradientPaintTransformer(GradientPaintTransformer transformer) { 227 this.gradientPaintTransformer = transformer; 228 notifyListeners(new RendererChangeEvent(this)); 229 } 230 231 236 public ItemLabelPosition getPositiveItemLabelPositionFallback() { 237 return this.positiveItemLabelPositionFallback; 238 } 239 240 246 public void setPositiveItemLabelPositionFallback(ItemLabelPosition position) { 247 this.positiveItemLabelPositionFallback = position; 248 notifyListeners(new RendererChangeEvent(this)); 249 } 250 251 256 public ItemLabelPosition getNegativeItemLabelPositionFallback() { 257 return this.negativeItemLabelPositionFallback; 258 } 259 260 266 public void setNegativeItemLabelPositionFallback(ItemLabelPosition position) { 267 this.negativeItemLabelPositionFallback = position; 268 notifyListeners(new RendererChangeEvent(this)); 269 } 270 271 278 public double getLowerClip() { 279 return this.lowerClip; 280 } 281 282 289 public double getUpperClip() { 290 return this.upperClip; 291 } 292 293 308 public CategoryItemRendererState initialise(Graphics2D g2, 309 Rectangle2D dataArea, 310 CategoryPlot plot, 311 Integer rendererIndex, 312 PlotRenderingInfo info) { 313 314 CategoryItemRendererState state = super.initialise(g2, dataArea, plot, rendererIndex, info); 315 316 ValueAxis rangeAxis = getRangeAxis(plot, rendererIndex); 318 this.lowerClip = rangeAxis.getRange().getLowerBound(); 319 this.upperClip = rangeAxis.getRange().getUpperBound(); 320 321 calculateBarWidth(plot, dataArea, rendererIndex, state); 323 324 return state; 325 326 } 327 328 336 protected void calculateBarWidth(CategoryPlot plot, 337 Rectangle2D dataArea, 338 Integer rendererIndex, 339 CategoryItemRendererState state) { 340 341 CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); 342 CategoryDataset dataset = getDataset(plot, rendererIndex); 343 if (dataset != null) { 344 int columns = dataset.getColumnCount(); 345 int rows = dataset.getRowCount(); 346 double space = 0.0; 347 PlotOrientation orientation = plot.getOrientation(); 348 if (orientation == PlotOrientation.HORIZONTAL) { 349 space = dataArea.getHeight(); 350 } 351 else if (orientation == PlotOrientation.VERTICAL) { 352 space = dataArea.getWidth(); 353 } 354 double maxWidth = space * getMaxBarWidth(); 355 double categoryMargin = 0.0; 356 double currentItemMargin = 0.0; 357 if (columns > 1) { 358 categoryMargin = domainAxis.getCategoryMargin(); 359 } 360 if (rows > 1) { 361 currentItemMargin = getItemMargin(); 362 } 363 double used = space * (1 - domainAxis.getLowerMargin() - domainAxis.getUpperMargin() 364 - categoryMargin - currentItemMargin); 365 if ((rows * columns) > 0) { 366 state.setBarWidth(Math.min(used / (rows * columns), maxWidth)); 367 } 368 else { 369 state.setBarWidth(Math.min(used, maxWidth)); 370 } 371 } 372 } 373 374 388 protected double calculateBarW0(CategoryPlot plot, 389 PlotOrientation orientation, 390 Rectangle2D dataArea, 391 CategoryAxis domainAxis, 392 CategoryItemRendererState state, 393 int row, 394 int column) { 395 double space = 0.0; 397 if (orientation == PlotOrientation.HORIZONTAL) { 398 space = dataArea.getHeight(); 399 } 400 else { 401 space = dataArea.getWidth(); 402 } 403 double barW0 = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, 404 plot.getDomainAxisEdge()); 405 int seriesCount = getRowCount(); 406 int categoryCount = getColumnCount(); 407 if (seriesCount > 1) { 408 double seriesGap = space * getItemMargin() / (categoryCount * (seriesCount - 1)); 409 double seriesW = calculateSeriesWidth(space, domainAxis, categoryCount, seriesCount); 410 barW0 = barW0 + row * (seriesW + seriesGap) 411 + (seriesW / 2.0) - (state.getBarWidth() / 2.0); 412 } 413 else { 414 barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, 415 plot.getDomainAxisEdge()) 416 - state.getBarWidth() / 2.0; 417 } 418 return barW0; 419 } 420 421 429 protected double[] calculateBarL0L1(double value) { 430 431 double base = 0.0; 432 double lclip = getLowerClip(); 433 double uclip = getUpperClip(); 434 if (uclip <= 0.0) { if (value >= uclip) { 436 return null; } 438 base = uclip; 439 if (value <= lclip) { 440 value = lclip; 441 } 442 } 443 else if (lclip <= 0.0) { if (value >= uclip) { 445 value = uclip; 446 } 447 else { 448 if (value <= lclip) { 449 value = lclip; 450 } 451 } 452 } 453 else { if (value <= lclip) { 455 return null; } 457 base = lclip; 458 if (value >= uclip) { 459 value = uclip; 460 } 461 } 462 return new double[] {base, value}; 463 } 464 465 478 public void drawItem(Graphics2D g2, 479 CategoryItemRendererState state, 480 Rectangle2D dataArea, 481 CategoryPlot plot, 482 CategoryAxis domainAxis, 483 ValueAxis rangeAxis, 484 CategoryDataset dataset, 485 int row, 486 int column) { 487 488 Number dataValue = dataset.getValue(row, column); 490 if (dataValue == null) { 491 return; 492 } 493 494 double value = dataValue.doubleValue(); 495 496 PlotOrientation orientation = plot.getOrientation(); 497 double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis, state, row, column); 498 double[] barL0L1 = calculateBarL0L1(value); 499 if (barL0L1 == null) { 500 return; } 502 503 RectangleEdge edge = plot.getRangeAxisEdge(); 504 double transL0 = rangeAxis.translateValueToJava2D(barL0L1[0], dataArea, edge); 505 double transL1 = rangeAxis.translateValueToJava2D(barL0L1[1], dataArea, edge); 506 double barL0 = Math.min(transL0, transL1); 507 double barLength = Math.max(Math.abs(transL1 - transL0), this.minimumBarLength); 508 509 Rectangle2D bar = null; 511 if (orientation == PlotOrientation.HORIZONTAL) { 512 bar = new Rectangle2D.Double (barL0, barW0, barLength, state.getBarWidth()); 513 } 514 else { 515 bar = new Rectangle2D.Double (barW0, barL0, state.getBarWidth(), barLength); 516 } 517 Paint itemPaint = getItemPaint(row, column); 518 if (this.gradientPaintTransformer != null && itemPaint instanceof GradientPaint ) { 519 GradientPaint gp = (GradientPaint ) itemPaint; 520 itemPaint = this.gradientPaintTransformer.transform(gp, bar); 521 } 522 g2.setPaint(itemPaint); 523 g2.fill(bar); 524 525 if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { 527 Stroke stroke = getItemOutlineStroke(row, column); 528 Paint paint = getItemOutlinePaint(row, column); 529 if (stroke != null && paint != null) { 530 g2.setStroke(stroke); 531 g2.setPaint(paint); 532 g2.draw(bar); 533 } 534 } 535 536 CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); 537 if (generator != null && isItemLabelVisible(row, column)) { 538 drawItemLabel(g2, dataset, row, column, plot, generator, bar, (value < 0.0)); 539 } 540 541 if (state.getInfo() != null) { 543 EntityCollection entities = state.getInfo().getOwner().getEntityCollection(); 544 if (entities != null) { 545 String tip = null; 546 if (generator != null) { 547 tip = generator.generateToolTip(dataset, row, column); 548 } 549 String url = null; 550 if (getItemURLGenerator(row, column) != null) { 551 url = getItemURLGenerator(row, column).generateURL(dataset, row, column); 552 } 553 CategoryItemEntity entity = new CategoryItemEntity( 554 bar, tip, url, dataset, row, dataset.getColumnKey(column), column 555 ); 556 entities.addEntity(entity); 557 } 558 559 } 560 561 } 562 563 573 protected double calculateSeriesWidth(double space, CategoryAxis axis, 574 int categories, int series) { 575 double factor = 1.0 - getItemMargin() - axis.getLowerMargin() - axis.getUpperMargin(); 576 if (categories > 1) { 577 factor = factor - axis.getCategoryMargin(); 578 } 579 return (space * factor) / (categories * series); 580 } 581 582 595 protected void drawItemLabel(Graphics2D g2, 596 CategoryDataset data, 597 int row, 598 int column, 599 CategoryPlot plot, 600 CategoryItemLabelGenerator generator, 601 Rectangle2D bar, 602 boolean negative) { 603 604 Font labelFont = getItemLabelFont(row, column); 606 g2.setFont(labelFont); 607 Paint paint = getItemLabelPaint(row, column); 608 g2.setPaint(paint); 609 String label = generator.generateItemLabel(data, row, column); 610 611 ItemLabelPosition position = null; 613 if (!negative) { 614 position = getPositiveItemLabelPosition(row, column); 615 } 616 else { 617 position = getNegativeItemLabelPosition(row, column); 618 } 619 620 Point2D anchorPoint = calculateLabelAnchorPoint(position.getItemLabelAnchor(), 622 bar, 623 plot.getOrientation()); 624 625 if (isInternalAnchor(position.getItemLabelAnchor())) { 626 Shape bounds = RefineryUtilities.calculateRotatedStringBounds( 627 label, g2, 628 (float) anchorPoint.getX(), 629 (float) anchorPoint.getY(), 630 position.getTextAnchor(), 631 position.getRotationAnchor(), 632 position.getAngle()); 633 634 if (!bar.contains(bounds.getBounds2D())) { 635 if (!negative) { 636 position = getPositiveItemLabelPositionFallback(); 637 } 638 else { 639 position = getNegativeItemLabelPositionFallback(); 640 } 641 if (position != null) { 642 anchorPoint = calculateLabelAnchorPoint(position.getItemLabelAnchor(), 643 bar, 644 plot.getOrientation()); 645 } 646 } 647 648 } 649 650 if (position != null) { 651 RefineryUtilities.drawRotatedString(label, g2, 652 (float) anchorPoint.getX(), 653 (float) anchorPoint.getY(), 654 position.getTextAnchor(), 655 position.getRotationAnchor(), 656 position.getAngle()); 657 } 658 } 659 660 669 private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor, 670 Rectangle2D bar, PlotOrientation orientation) { 671 672 Point2D result = null; 673 double offset = getItemLabelAnchorOffset(); 674 double x0 = bar.getX() - offset; 675 double x1 = bar.getX(); 676 double x2 = bar.getX() + offset; 677 double x3 = bar.getCenterX(); 678 double x4 = bar.getMaxX() - offset; 679 double x5 = bar.getMaxX(); 680 double x6 = bar.getMaxX() + offset; 681 682 double y0 = bar.getMaxY() + offset; 683 double y1 = bar.getMaxY(); 684 double y2 = bar.getMaxY() - offset; 685 double y3 = bar.getCenterY(); 686 double y4 = bar.getMinY() + offset; 687 double y5 = bar.getMinY(); 688 double y6 = bar.getMinY() - offset; 689 690 if (anchor == ItemLabelAnchor.CENTER) { 691 result = new Point2D.Double (x3, y3); 692 } 693 else if (anchor == ItemLabelAnchor.INSIDE1) { 694 result = new Point2D.Double (x4, y4); 695 } 696 else if (anchor == ItemLabelAnchor.INSIDE2) { 697 result = new Point2D.Double (x4, y4); 698 } 699 else if (anchor == ItemLabelAnchor.INSIDE3) { 700 result = new Point2D.Double (x4, y3); 701 } 702 else if (anchor == ItemLabelAnchor.INSIDE4) { 703 result = new Point2D.Double (x4, y2); 704 } 705 else if (anchor == ItemLabelAnchor.INSIDE5) { 706 result = new Point2D.Double (x4, y2); 707 } 708 else if (anchor == ItemLabelAnchor.INSIDE6) { 709 result = new Point2D.Double (x3, y2); 710 } 711 else if (anchor == ItemLabelAnchor.INSIDE7) { 712 result = new Point2D.Double (x2, y2); 713 } 714 else if (anchor == ItemLabelAnchor.INSIDE8) { 715 result = new Point2D.Double (x2, y2); 716 } 717 else if (anchor == ItemLabelAnchor.INSIDE9) { 718 result = new Point2D.Double (x2, y3); 719 } 720 else if (anchor == ItemLabelAnchor.INSIDE10) { 721 result = new Point2D.Double (x2, y4); 722 } 723 else if (anchor == ItemLabelAnchor.INSIDE11) { 724 result = new Point2D.Double (x2, y4); 725 } 726 else if (anchor == ItemLabelAnchor.INSIDE12) { 727 result = new Point2D.Double (x3, y4); 728 } 729 else if (anchor == ItemLabelAnchor.OUTSIDE1) { 730 result = new Point2D.Double (x5, y6); 731 } 732 else if (anchor == ItemLabelAnchor.OUTSIDE2) { 733 result = new Point2D.Double (x6, y5); 734 } 735 else if (anchor == ItemLabelAnchor.OUTSIDE3) { 736 result = new Point2D.Double (x6, y3); 737 } 738 else if (anchor == ItemLabelAnchor.OUTSIDE4) { 739 result = new Point2D.Double (x6, y1); 740 } 741 else if (anchor == ItemLabelAnchor.OUTSIDE5) { 742 result = new Point2D.Double (x5, y0); 743 } 744 else if (anchor == ItemLabelAnchor.OUTSIDE6) { 745 result = new Point2D.Double (x3, y0); 746 } 747 else if (anchor == ItemLabelAnchor.OUTSIDE7) { 748 result = new Point2D.Double (x1, y0); 749 } 750 else if (anchor == ItemLabelAnchor.OUTSIDE8) { 751 result = new Point2D.Double (x0, y1); 752 } 753 else if (anchor == ItemLabelAnchor.OUTSIDE9) { 754 result = new Point2D.Double (x0, y3); 755 } 756 else if (anchor == ItemLabelAnchor.OUTSIDE10) { 757 result = new Point2D.Double (x0, y5); 758 } 759 else if (anchor == ItemLabelAnchor.OUTSIDE11) { 760 result = new Point2D.Double (x1, y6); 761 } 762 else if (anchor == ItemLabelAnchor.OUTSIDE12) { 763 result = new Point2D.Double (x3, y6); 764 } 765 766 return result; 767 768 } 769 770 777 private boolean isInternalAnchor(ItemLabelAnchor anchor) { 778 return anchor == ItemLabelAnchor.CENTER 779 || anchor == ItemLabelAnchor.INSIDE1 780 || anchor == ItemLabelAnchor.INSIDE2 781 || anchor == ItemLabelAnchor.INSIDE3 782 || anchor == ItemLabelAnchor.INSIDE4 783 || anchor == ItemLabelAnchor.INSIDE5 784 || anchor == ItemLabelAnchor.INSIDE6 785 || anchor == ItemLabelAnchor.INSIDE7 786 || anchor == ItemLabelAnchor.INSIDE8 787 || anchor == ItemLabelAnchor.INSIDE9 788 || anchor == ItemLabelAnchor.INSIDE10 789 || anchor == ItemLabelAnchor.INSIDE11 790 || anchor == ItemLabelAnchor.INSIDE12; 791 792 } 793 794 801 public boolean equals(Object object) { 802 803 if (object == null) { 804 return false; 805 } 806 807 if (object == this) { 808 return true; 809 } 810 811 if (super.equals(object) && (object instanceof BarRenderer)) { 812 813 BarRenderer r = (BarRenderer) object; 814 boolean b0 = (this.itemMargin == r.itemMargin); 815 boolean b1 = (this.drawBarOutline == r.drawBarOutline); 816 return b0 && b1; 817 } 818 819 return false; 820 821 } 822 823 } 824 | Popular Tags |