Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
07/21/16 16:57:58 (8 years ago)
Author:
mkommend
Message:

#2594: Merged r13764, r13765, r13807, r14007, r14008, r14014, r14152, r14155, r14156, r14159 to stable.

Location:
stable
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • stable

  • stable/HeuristicLab.Visualization.ChartControlsExtensions/3.3/ChartUtil.cs

    r13765 r14161  
    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 = 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 dRange = (int)Math.Round(Math.Log10(range));
     34      int decimalRank = dRange - 1;
     35      var aMin = min.RoundDown(decimalRank);
     36      var aMax = max.RoundUp(decimalRank);
     37
     38      // if one of the interval ends is a multiple of 5 or 10, change the other interval end to be a multiple as well
     39      if ((aMin.Mod(5).IsAlmost(0) || aMin.Mod(10).IsAlmost(0)) && Math.Abs(aMax) >= 5 && !(aMax.Mod(5).IsAlmost(0) || aMax.Mod(10).IsAlmost(0))) {
     40        aMax = Math.Min(aMax + 5 - aMax % 5, aMax + 10 - aMax % 10);
     41      } else if ((aMax.Mod(5).IsAlmost(0) || aMax.Mod(10).IsAlmost(0)) && Math.Abs(aMin) >= 5 && !(aMin.Mod(5).IsAlmost(0) || aMin.Mod(10).IsAlmost(0))) {
     42        aMin = Math.Max(aMin - aMin.Mod(5), aMin - aMin.Mod(10));
    4143      }
    42       axisMax = axisMin + axisRange;
     44
     45      axisMin = aMin;
     46      axisMax = aMax;
     47      axisInterval = (aMax - aMin) / ticks;
    4348    }
    4449
     50    /// <summary>
     51    /// Tries to find an axis interval with as few fractional digits as possible (because it looks nicer).  we only try between 3 and 5 ticks (inclusive) because it wouldn't make sense to exceed this interval.
     52    /// </summary>
     53    public static void CalculateOptimalAxisInterval(double min, double max, out double axisMin, out double axisMax, out double axisInterval) {
     54      CalculateAxisInterval(min, max, 5, out axisMin, out axisMax, out axisInterval);
     55      int bestLsp = int.MaxValue;
     56      for (int ticks = 3; ticks <= 5; ++ticks) {
     57        double aMin, aMax, aInterval;
     58        CalculateAxisInterval(min, max, ticks, out aMin, out aMax, out aInterval);
     59        var x = aInterval;
     60        int lsp = 0; // position of the least significant fractional digit
     61        while (x - Math.Floor(x) > 0) {
     62          ++lsp;
     63          x *= 10;
     64        }
     65        if (lsp <= bestLsp) {
     66          axisMin = aMin;
     67          axisMax = aMax;
     68          axisInterval = aInterval;
     69          bestLsp = lsp;
     70        }
     71      }
     72    }
     73
     74    // find the number of decimals needed to represent the value
    4575    private static int Decimals(this double x) {
    46       if (double.IsInfinity(x) || double.IsNaN(x))
     76      if (x.IsAlmost(0) || double.IsInfinity(x) || double.IsNaN(x))
    4777        return 0;
    4878
    4979      var v = Math.Abs(x);
    50       int d = 0;
     80      int d = 1;
    5181      while (v < 1) {
    5282        v *= 10;
     
    5484      }
    5585      return d;
     86    }
     87
     88    private static double RoundDown(this double value, int decimalRank) {
     89      if (decimalRank > 0) {
     90        var floor = (int)Math.Floor(value);
     91        var pow = (int)Math.Pow(10, decimalRank);
     92        var mod = Mod(floor, pow);
     93        return floor - mod;
     94      }
     95      return value.Floor(Math.Abs(decimalRank));
     96    }
     97
     98    private static double RoundUp(this double value, int decimalRank) {
     99      if (decimalRank > 0) {
     100        var ceil = (int)Math.Ceiling(value);
     101        var pow = (int)Math.Pow(10, decimalRank);
     102        var mod = Mod(ceil, pow);
     103        return ceil - mod + pow;
     104      }
     105      return value.Ceil(Math.Abs(decimalRank));
     106    }
     107
     108    private static double RoundNearest(this double value, int decimalRank) {
     109      var nearestDown = value.RoundDown(decimalRank);
     110      var nearestUp = value.RoundUp(decimalRank);
     111
     112      if (nearestUp - value > value - nearestDown)
     113        return nearestDown;
     114
     115      return nearestUp;
    56116    }
    57117
     
    62122    }
    63123
     124    // rounds up to the nearest value according to the given number of decimal precision
    64125    private static double Ceil(this double value, int precision) {
    65126      var n = Math.Pow(10, precision);
     
    67128    }
    68129
    69     private static double Round(this double value, int precision) {
    70       var n = Math.Pow(10, precision);
    71       return Math.Round(Math.Round(value * n) / n, precision);
     130    private static bool IsAlmost(this double value, double other, double eps = 1e-12) {
     131      return Math.Abs(value - other) < eps;
     132    }
     133
     134    private static double Mod(this double a, double b) {
     135      return a - b * Math.Floor(a / b);
    72136    }
    73137  }
Note: See TracChangeset for help on using the changeset viewer.