1 90 91 package org.jfree.chart.axis; 92 93 import java.awt.Graphics2D ; 94 import java.awt.geom.Rectangle2D ; 95 import java.text.DecimalFormat ; 96 import java.text.NumberFormat ; 97 import java.util.List ; 98 99 import org.jfree.chart.plot.Plot; 100 import org.jfree.chart.plot.ValueAxisPlot; 101 import org.jfree.data.Range; 102 import org.jfree.ui.RectangleEdge; 103 import org.jfree.ui.TextAnchor; 104 105 110 public class LogarithmicAxis extends NumberAxis { 111 112 113 private static final long serialVersionUID = 2502918599004103054L; 114 115 116 public static final double LOG10_VALUE = Math.log(10.0); 117 118 119 public static final double SMALL_LOG_VALUE = 1e-100; 120 121 122 protected boolean allowNegativesFlag = false; 123 124 126 protected boolean strictValuesFlag = true; 127 128 129 protected final NumberFormat numberFormatterObj 130 = NumberFormat.getInstance(); 131 132 133 protected boolean expTickLabelsFlag = false; 134 135 136 protected boolean log10TickLabelsFlag = false; 137 138 139 protected boolean autoRangeNextLogFlag = false; 140 141 142 protected boolean smallLogFlag = false; 143 144 149 public LogarithmicAxis(String label) { 150 super(label); 151 setupNumberFmtObj(); } 153 154 161 public void setAllowNegativesFlag(boolean flgVal) { 162 this.allowNegativesFlag = flgVal; 163 } 164 165 172 public boolean getAllowNegativesFlag() { 173 return this.allowNegativesFlag; 174 } 175 176 184 public void setStrictValuesFlag(boolean flgVal) { 185 this.strictValuesFlag = flgVal; 186 } 187 188 196 public boolean getStrictValuesFlag() { 197 return this.strictValuesFlag; 198 } 199 200 208 public void setExpTickLabelsFlag(boolean flgVal) { 209 this.expTickLabelsFlag = flgVal; 210 setupNumberFmtObj(); } 212 213 219 public boolean getExpTickLabelsFlag() { 220 return this.expTickLabelsFlag; 221 } 222 223 229 public void setLog10TickLabelsFlag(boolean flag) { 230 this.log10TickLabelsFlag = flag; 231 } 232 233 240 public boolean getLog10TickLabelsFlag() { 241 return this.log10TickLabelsFlag; 242 } 243 244 253 public void setAutoRangeNextLogFlag(boolean flag) { 254 this.autoRangeNextLogFlag = flag; 255 } 256 257 263 public boolean getAutoRangeNextLogFlag() { 264 return this.autoRangeNextLogFlag; 265 } 266 267 273 public void setRange(Range range) { 274 super.setRange(range); setupSmallLogFlag(); } 277 278 282 protected void setupSmallLogFlag() { 283 double lowerVal = getRange().getLowerBound(); 286 this.smallLogFlag 287 = (!this.allowNegativesFlag && lowerVal < 10.0 && lowerVal > 0.0); 288 } 289 290 294 protected void setupNumberFmtObj() { 295 if (this.numberFormatterObj instanceof DecimalFormat ) { 296 ((DecimalFormat ) this.numberFormatterObj).applyPattern( 299 this.expTickLabelsFlag ? "0E0" : "0.###" 300 ); 301 } 302 } 303 304 315 protected double switchedLog10(double val) { 316 return this.smallLogFlag ? Math.log(val) 317 / LOG10_VALUE : adjustedLog10(val); 318 } 319 320 332 public double adjustedLog10(double val) { 333 boolean negFlag = (val < 0.0); 334 if (negFlag) { 335 val = -val; } 337 if (val < 10.0) { val += (10.0 - val) / 10; } 340 return negFlag ? -(Math.log(val) / LOG10_VALUE) 342 : (Math.log(val) / LOG10_VALUE); 343 } 344 345 355 protected double computeLogFloor(double lower) { 356 357 double logFloor; 358 if (this.allowNegativesFlag) { 359 if (lower > 10.0) { logFloor = Math.log(lower) / LOG10_VALUE; 363 logFloor = Math.floor(logFloor); 364 logFloor = Math.pow(10, logFloor); 365 } 366 else if (lower < -10.0) { logFloor = Math.log(-lower) / LOG10_VALUE; 369 logFloor = Math.floor(-logFloor); 371 logFloor = -Math.pow(10, -logFloor); 373 } 374 else { 375 logFloor = Math.floor(lower); } 378 } 379 else { 380 if (lower > 0.0) { logFloor = Math.log(lower) / LOG10_VALUE; 384 logFloor = Math.floor(logFloor); 385 logFloor = Math.pow(10, logFloor); 386 } 387 else { 388 logFloor = Math.floor(lower); } 391 } 392 return logFloor; 393 } 394 395 405 protected double computeLogCeil(double upper) { 406 407 double logCeil; 408 if (this.allowNegativesFlag) { 409 if (upper > 10.0) { 411 logCeil = Math.log(upper) / LOG10_VALUE; 414 logCeil = Math.ceil(logCeil); 415 logCeil = Math.pow(10, logCeil); 416 } 417 else if (upper < -10.0) { 418 logCeil = Math.log(-upper) / LOG10_VALUE; 421 logCeil = Math.ceil(-logCeil); 423 logCeil = -Math.pow(10, -logCeil); 425 } 426 else { 427 logCeil = Math.ceil(upper); } 430 } 431 else { 432 if (upper > 0.0) { 434 logCeil = Math.log(upper) / LOG10_VALUE; 437 logCeil = Math.ceil(logCeil); 438 logCeil = Math.pow(10, logCeil); 439 } 440 else { 441 logCeil = Math.ceil(upper); } 444 } 445 return logCeil; 446 } 447 448 451 public void autoAdjustRange() { 452 453 Plot plot = getPlot(); 454 if (plot == null) { 455 return; } 457 458 if (plot instanceof ValueAxisPlot) { 459 ValueAxisPlot vap = (ValueAxisPlot) plot; 460 461 double lower; 462 Range r = vap.getDataRange(this); 463 if (r == null) { 464 r = new Range(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND); 466 lower = r.getLowerBound(); } 468 else { 469 lower = r.getLowerBound(); if (this.strictValuesFlag 472 && !this.allowNegativesFlag && lower <= 0.0) { 473 throw new RuntimeException ( 475 "Values less than or equal to " 476 + "zero not allowed with logarithmic axis" 477 ); 478 } 479 } 480 481 final double lowerMargin; 483 if (lower > 0.0 && (lowerMargin=getLowerMargin()) > 0.0) { 484 final double logLower = (Math.log(lower) / LOG10_VALUE); 486 double logAbs; if((logAbs=Math.abs(logLower)) < 1.0) { 488 logAbs = 1.0; } lower = Math.pow(10, (logLower - (logAbs * lowerMargin))); 491 } 492 493 if (this.autoRangeNextLogFlag) { 496 lower = computeLogFloor(lower); 497 } 498 499 if (!this.allowNegativesFlag && lower >= 0.0 500 && lower < SMALL_LOG_VALUE) { 501 lower = r.getLowerBound(); } 504 505 double upper = r.getUpperBound(); 506 507 final double upperMargin; 509 if (upper > 0.0 && (upperMargin=getUpperMargin()) > 0.0) { 510 final double logUpper = (Math.log(upper) / LOG10_VALUE); 512 double logAbs; if((logAbs=Math.abs(logUpper)) < 1.0) { 514 logAbs = 1.0; } upper = Math.pow(10, (logUpper + (logAbs * upperMargin))); 517 } 518 519 if (!this.allowNegativesFlag && upper < 1.0 && upper > 0.0 520 && lower > 0.0) { 521 double expVal = Math.log(upper) / LOG10_VALUE; 525 expVal = Math.ceil(-expVal + 0.001); expVal = Math.pow(10, expVal); upper = (expVal > 0.0) ? Math.ceil(upper * expVal) / expVal 529 : Math.ceil(upper); 530 } 531 else { 532 upper = (this.autoRangeNextLogFlag) ? computeLogCeil(upper) 536 : Math.ceil(upper); 537 } 538 double minRange = getAutoRangeMinimumSize(); 540 if (upper - lower < minRange) { 541 upper = (upper + lower + minRange) / 2; 542 lower = (upper + lower - minRange) / 2; 543 if (upper - lower < minRange) { 546 double absUpper = Math.abs(upper); 547 double adjVal = (absUpper > SMALL_LOG_VALUE) ? absUpper 549 / 100.0 : 0.01; 550 upper = (upper + lower + adjVal) / 2; 551 lower = (upper + lower - adjVal) / 2; 552 } 553 } 554 555 setRange(new Range(lower, upper), false, false); 556 setupSmallLogFlag(); } 558 } 559 560 572 public double valueToJava2D(double value, Rectangle2D plotArea, 573 RectangleEdge edge) { 574 575 Range range = getRange(); 576 double axisMin = switchedLog10(range.getLowerBound()); 577 double axisMax = switchedLog10(range.getUpperBound()); 578 579 double min = 0.0; 580 double max = 0.0; 581 if (RectangleEdge.isTopOrBottom(edge)) { 582 min = plotArea.getMinX(); 583 max = plotArea.getMaxX(); 584 } 585 else if (RectangleEdge.isLeftOrRight(edge)) { 586 min = plotArea.getMaxY(); 587 max = plotArea.getMinY(); 588 } 589 590 value = switchedLog10(value); 591 592 if (isInverted()) { 593 return max 594 - (((value - axisMin) / (axisMax - axisMin)) * (max - min)); 595 } 596 else { 597 return min 598 + (((value - axisMin) / (axisMax - axisMin)) * (max - min)); 599 } 600 601 } 602 603 614 public double java2DToValue(double java2DValue, Rectangle2D plotArea, 615 RectangleEdge edge) { 616 617 Range range = getRange(); 618 double axisMin = switchedLog10(range.getLowerBound()); 619 double axisMax = switchedLog10(range.getUpperBound()); 620 621 double plotMin = 0.0; 622 double plotMax = 0.0; 623 if (RectangleEdge.isTopOrBottom(edge)) { 624 plotMin = plotArea.getX(); 625 plotMax = plotArea.getMaxX(); 626 } 627 else if (RectangleEdge.isLeftOrRight(edge)) { 628 plotMin = plotArea.getMaxY(); 629 plotMax = plotArea.getMinY(); 630 } 631 632 if (isInverted()) { 633 return Math.pow( 634 10, axisMax - ((java2DValue - plotMin) / (plotMax - plotMin)) 635 * (axisMax - axisMin) 636 ); 637 } 638 else { 639 return Math.pow( 640 10, axisMin + ((java2DValue - plotMin) / (plotMax - plotMin)) 641 * (axisMax - axisMin) 642 ); 643 } 644 } 645 646 656 protected List refreshTicksHorizontal(Graphics2D g2, 657 Rectangle2D dataArea, 658 RectangleEdge edge) { 659 660 List ticks = new java.util.ArrayList (); 661 Range range = getRange(); 662 663 double lowerBoundVal = range.getLowerBound(); 665 if (this.smallLogFlag && lowerBoundVal < SMALL_LOG_VALUE) { 668 lowerBoundVal = SMALL_LOG_VALUE; 669 } 670 671 double upperBoundVal = range.getUpperBound(); 673 674 int iBegCount = (int) Math.rint(switchedLog10(lowerBoundVal)); 676 int iEndCount = (int) Math.rint(switchedLog10(upperBoundVal)); 678 679 if (iBegCount == iEndCount && iBegCount > 0 680 && Math.pow(10, iBegCount) > lowerBoundVal) { 681 --iBegCount; } 685 686 double currentTickValue; 687 String tickLabel; 688 boolean zeroTickFlag = false; 689 for (int i = iBegCount; i <= iEndCount; i++) { 690 for (int j = 0; j < 10; ++j) { 692 if (this.smallLogFlag) { 694 currentTickValue = Math.pow(10, i) + (Math.pow(10, i) * j); 696 if (this.expTickLabelsFlag 697 || (i < 0 && currentTickValue > 0.0 698 && currentTickValue < 1.0)) { 699 if (j == 0 || (i > -4 && j < 2) 702 || currentTickValue >= upperBoundVal) { 703 this.numberFormatterObj 708 .setMaximumFractionDigits(-i); 709 tickLabel = makeTickLabel(currentTickValue, true); 711 } 712 else { tickLabel = ""; 714 } 715 } 716 else { tickLabel = (j < 1 || (i < 1 && j < 5) || (j < 4 - i) 721 || currentTickValue >= upperBoundVal) 722 ? makeTickLabel(currentTickValue) : ""; 723 } 724 } 725 else { if (zeroTickFlag) { --j; } currentTickValue = (i >= 0) 730 ? Math.pow(10, i) + (Math.pow(10, i) * j) 731 : -(Math.pow(10, -i) - (Math.pow(10, -i - 1) * j)); 732 if (!zeroTickFlag) { if (Math.abs(currentTickValue - 1.0) < 0.0001 734 && lowerBoundVal <= 0.0 && upperBoundVal >= 0.0) { 735 currentTickValue = 0.0; zeroTickFlag = true; } 739 } 740 else { zeroTickFlag = false; } tickLabel = ((this.expTickLabelsFlag && j < 2) 748 || j < 1 749 || (i < 1 && j < 5) || (j < 4 - i) 750 || currentTickValue >= upperBoundVal) 751 ? makeTickLabel(currentTickValue) : ""; 752 } 753 754 if (currentTickValue > upperBoundVal) { 755 return ticks; } 758 759 if (currentTickValue >= lowerBoundVal - SMALL_LOG_VALUE) { 760 TextAnchor anchor = null; 762 TextAnchor rotationAnchor = null; 763 double angle = 0.0; 764 if (isVerticalTickLabels()) { 765 anchor = TextAnchor.CENTER_RIGHT; 766 rotationAnchor = TextAnchor.CENTER_RIGHT; 767 if (edge == RectangleEdge.TOP) { 768 angle = Math.PI / 2.0; 769 } 770 else { 771 angle = -Math.PI / 2.0; 772 } 773 } 774 else { 775 if (edge == RectangleEdge.TOP) { 776 anchor = TextAnchor.BOTTOM_CENTER; 777 rotationAnchor = TextAnchor.BOTTOM_CENTER; 778 } 779 else { 780 anchor = TextAnchor.TOP_CENTER; 781 rotationAnchor = TextAnchor.TOP_CENTER; 782 } 783 } 784 785 Tick tick = new NumberTick( 786 new Double (currentTickValue), tickLabel, anchor, 787 rotationAnchor, angle 788 ); 789 ticks.add(tick); 790 } 791 } 792 } 793 return ticks; 794 795 } 796 797 807 protected List refreshTicksVertical(Graphics2D g2, 808 Rectangle2D dataArea, 809 RectangleEdge edge) { 810 811 List ticks = new java.util.ArrayList (); 812 813 double lowerBoundVal = getRange().getLowerBound(); 815 if (this.smallLogFlag && lowerBoundVal < SMALL_LOG_VALUE) { 818 lowerBoundVal = SMALL_LOG_VALUE; 819 } 820 double upperBoundVal = getRange().getUpperBound(); 822 823 int iBegCount = (int) Math.rint(switchedLog10(lowerBoundVal)); 825 int iEndCount = (int) Math.rint(switchedLog10(upperBoundVal)); 827 828 if (iBegCount == iEndCount && iBegCount > 0 829 && Math.pow(10, iBegCount) > lowerBoundVal) { 830 --iBegCount; } 834 835 double tickVal; 836 String tickLabel; 837 boolean zeroTickFlag = false; 838 for (int i = iBegCount; i <= iEndCount; i++) { 839 int jEndCount = 10; 841 if (i == iEndCount) { 842 jEndCount = 1; 843 } 844 845 for (int j = 0; j < jEndCount; j++) { 846 if (this.smallLogFlag) { 848 tickVal = Math.pow(10, i) + (Math.pow(10, i) * j); 850 if (j == 0) { 851 if (this.log10TickLabelsFlag) { 853 tickLabel = "10^" + i; } 856 else { if (this.expTickLabelsFlag) { 858 tickLabel = "1e" + i; } 861 else { if (i >= 0) { NumberFormat format 865 = getNumberFormatOverride(); 866 if (format != null) { 867 tickLabel = format.format(tickVal); 868 } 869 else { 870 tickLabel = Long.toString((long) 871 Math.rint(tickVal)); 872 } 873 } 874 else { 875 this.numberFormatterObj 879 .setMaximumFractionDigits(-i); 880 tickLabel = this.numberFormatterObj.format( 882 tickVal 883 ); 884 } 885 } 886 } 887 } 888 else { tickLabel = ""; } 891 } 892 else { if (zeroTickFlag) { --j; 895 } tickVal = (i >= 0) ? Math.pow(10, i) + (Math.pow(10, i) * j) 897 : -(Math.pow(10, -i) - (Math.pow(10, -i - 1) * j)); 898 if (j == 0) { if (!zeroTickFlag) { if (i > iBegCount && i < iEndCount 902 && Math.abs(tickVal - 1.0) < 0.0001) { 903 tickVal = 0.0; zeroTickFlag = true; tickLabel = "0"; } 909 else { 910 if (this.log10TickLabelsFlag) { 913 tickLabel = (((i < 0) ? "-" : "") 915 + "10^" + Math.abs(i)); 916 } 917 else { 918 if (this.expTickLabelsFlag) { 919 tickLabel = (((i < 0) ? "-" : "") 921 + "1e" + Math.abs(i)); 922 } 923 else { 924 NumberFormat format 925 = getNumberFormatOverride(); 926 if (format != null) { 927 tickLabel = format.format(tickVal); 928 } 929 else { 930 tickLabel = Long.toString( 931 (long) Math.rint(tickVal) 932 ); 933 } 934 } 935 } 936 } 937 } 938 else { tickLabel = ""; zeroTickFlag = false; } 942 } 943 else { tickLabel = ""; zeroTickFlag = false; } 947 } 948 949 if (tickVal > upperBoundVal) { 950 return ticks; } 952 953 if (tickVal >= lowerBoundVal - SMALL_LOG_VALUE) { 954 TextAnchor anchor = null; 956 TextAnchor rotationAnchor = null; 957 double angle = 0.0; 958 if (isVerticalTickLabels()) { 959 if (edge == RectangleEdge.LEFT) { 960 anchor = TextAnchor.BOTTOM_CENTER; 961 rotationAnchor = TextAnchor.BOTTOM_CENTER; 962 angle = -Math.PI / 2.0; 963 } 964 else { 965 anchor = TextAnchor.BOTTOM_CENTER; 966 rotationAnchor = TextAnchor.BOTTOM_CENTER; 967 angle = Math.PI / 2.0; 968 } 969 } 970 else { 971 if (edge == RectangleEdge.LEFT) { 972 anchor = TextAnchor.CENTER_RIGHT; 973 rotationAnchor = TextAnchor.CENTER_RIGHT; 974 } 975 else { 976 anchor = TextAnchor.CENTER_LEFT; 977 rotationAnchor = TextAnchor.CENTER_LEFT; 978 } 979 } 980 ticks.add( 982 new NumberTick( 983 new Double (tickVal), tickLabel, anchor, 984 rotationAnchor, angle 985 ) 986 ); 987 988 } 989 } 990 } 991 return ticks; 992 } 993 994 1003 protected String makeTickLabel(double val, boolean forceFmtFlag) { 1004 if (this.expTickLabelsFlag || forceFmtFlag) { 1005 return this.numberFormatterObj.format(val).toLowerCase(); 1008 } 1009 return getTickUnit().valueToString(val); 1010 } 1011 1012 1018 protected String makeTickLabel(double val) { 1019 return makeTickLabel(val, false); 1020 } 1021 1022} 1023 | Popular Tags |