Changeset 14152


Ignore:
Timestamp:
07/21/16 13:51:27 (11 months ago)
Author:
bburlacu
Message:

#2594: Improved axis scaling.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Visualization.ChartControlsExtensions/3.3/ChartUtil.cs

    r14125 r14152  
    2727  public static class ChartUtil {
    2828    public static void CalculateAxisInterval(double min, double max, int ticks, out double axisMin, out double axisMax, out double axisInterval) {
    29       var dmin = (max - min).Decimals();
    30       axisMin = min.Floor(dmin);
    31       var range = max - axisMin;
    32       var slice = range / ticks;
    33       var dslice = slice.Decimals();
    34       var floor = slice.Floor(dslice);
    35       var ceil = slice.Ceil(dslice);
    36       var axisRange = floor * ticks;
    37       axisInterval = floor;
    38       if (axisRange < max - axisMin) {
    39         axisRange = ceil * ticks;
    40         axisInterval = ceil;
     29      if (double.IsInfinity(min) || double.IsNaN(min) || double.IsInfinity(max) || double.IsNaN(max) || (min >= max))
     30        throw new ArgumentOutOfRangeException("Invalid range provided.");
     31
     32      var range = max - min;
     33      var minLog = Math.Log10(Math.Abs(min));
     34      var maxLog = Math.Log10(Math.Abs(max));
     35      var rangeLog = Math.Log10(range);
     36
     37      var dMin = (int)Math.Floor(minLog);
     38      var dMax = (int)Math.Floor(maxLog);
     39      var dRange = (int)Math.Floor(rangeLog);
     40
     41      int decimalRank;
     42      if (dMin == dMax && dMax == dRange)
     43        decimalRank = dMin - 1;
     44      else
     45        decimalRank = Math.Min(Math.Min(dMin, dMax), dRange);
     46
     47      var aMin = min.RoundDown(decimalRank);
     48      var aMax = max.RoundUp(decimalRank);
     49
     50      // if one of the interval ends is a multiple of 5 or 10, change the other interval end to be a multiple as well
     51      if (((aMin % 5).IsAlmost(0) || (aMin % 10).IsAlmost(0)) && !((aMax % 5).IsAlmost(0) || (aMax % 10).IsAlmost(0))) {
     52        aMax = Math.Min(aMax + 5 - aMax % 5, aMax + 10 - aMax % 10);
     53      } else if (((aMax % 5).IsAlmost(0) || (aMax % 10).IsAlmost(0)) && !((aMin % 5).IsAlmost(0) || (aMin % 10).IsAlmost(0))) {
     54        aMin = Math.Max(aMin - aMin % 5, aMin - aMin % 10);
    4155      }
    42       axisMax = axisMin + axisRange;
     56
     57      axisMin = aMin;
     58      axisMax = aMax;
     59      axisInterval = (aMax - aMin) / ticks;
    4360    }
    4461
     
    6683    }
    6784
     85    // find the number of decimals needed to represent the value
    6886    private static int Decimals(this double x) {
    6987      if (x.IsAlmost(0) || double.IsInfinity(x) || double.IsNaN(x))
     
    7997    }
    8098
     99    private static double RoundDown(this double value, int decimalRank) {
     100      if (decimalRank > 0) {
     101        var floor = (int)Math.Floor(value);
     102        var pow = (int)Math.Pow(10, decimalRank);
     103        // round down to nearest multiple of 5 or 10
     104        var mod = Math.Min(floor % pow, floor % (pow / 2));
     105        return floor - mod;
     106      }
     107      return value.Floor(Math.Abs(decimalRank));
     108    }
     109
     110    private static double RoundUp(this double value, int decimalRank) {
     111      if (decimalRank > 0) {
     112        var ceil = (int)Math.Ceiling(value);
     113        var pow = (int)Math.Pow(10, decimalRank);
     114        // round up to nearest multiple of 5 or 10
     115        var mod = ceil % (pow / 2) == 0 || ceil % pow == 0 ? 0 : Math.Min(pow / 2 - ceil % (pow / 2), pow - ceil % pow);
     116        return ceil + mod;
     117      }
     118      return value.Ceil(Math.Abs(decimalRank));
     119    }
     120
     121    private static double RoundNearest(this double value, int decimalRank) {
     122      var nearestDown = value.RoundDown(decimalRank);
     123      var nearestUp = value.RoundUp(decimalRank);
     124
     125      if (nearestUp - value > value - nearestDown)
     126        return nearestDown;
     127
     128      return nearestUp;
     129    }
     130
    81131    // rounds down to the nearest value according to the given number of decimal precision
    82132    private static double Floor(this double value, int precision) {
     
    85135    }
    86136
     137    // rounds up to the nearest value according to the given number of decimal precision
    87138    private static double Ceil(this double value, int precision) {
    88139      var n = Math.Pow(10, precision);
    89140      return Math.Round(Math.Ceiling(value * n) / n, precision);
    90     }
    91 
    92     private static double Round(this double value, int precision) {
    93       var n = Math.Pow(10, precision);
    94       return Math.Round(Math.Round(value * n) / n, precision);
    95141    }
    96142
Note: See TracChangeset for help on using the changeset viewer.