[14125]  1  #region License Information


 2 


 3  /* HeuristicLab


 4  * Copyright (C) 20022016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)


 5  *


 6  * This file is part of HeuristicLab.


 7  *


 8  * HeuristicLab is free software: you can redistribute it and/or modify


 9  * it under the terms of the GNU General Public License as published by


 10  * the Free Software Foundation, either version 3 of the License, or


 11  * (at your option) any later version.


 12  *


 13  * HeuristicLab is distributed in the hope that it will be useful,


 14  * but WITHOUT ANY WARRANTY; without even the implied warranty of


 15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the


 16  * GNU General Public License for more details.


 17  *


 18  * You should have received a copy of the GNU General Public License


 19  * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.


 20  */


 21 


 22  #endregion


 23 


 24  using System;


 25 


 26  namespace HeuristicLab.Visualization.ChartControlsExtensions {


 27  public static class ChartUtil {


 28  public static void CalculateAxisInterval(double min, double max, int ticks, out double axisMin, out double axisMax, out double axisInterval) {


[14152]  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;


[14155]  33  var dRange = (int)Math.Round(Math.Log10(range));


 34  int decimalRank = dRange  1;


[14152]  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


[14159]  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));


[14125]  43  }


[14152]  44 


 45  axisMin = aMin;


 46  axisMax = aMax;


 47  axisInterval = (aMax  aMin) / ticks;


[14008]  48  }


 49 


[14155]  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>


[14008]  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  }


[14125]  72  }


 73 


[14152]  74  // find the number of decimals needed to represent the value


[14125]  75  private static int Decimals(this double x) {


 76  if (x.IsAlmost(0)  double.IsInfinity(x)  double.IsNaN(x))


 77  return 0;


 78 


 79  var v = Math.Abs(x);


 80  int d = 1;


 81  while (v < 1) {


 82  v *= 10;


 83  d++;


 84  }


 85  return d;


 86  }


 87 


[14152]  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);


[14159]  92  var mod = Mod(floor, pow);


[14152]  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);


[14159]  102  var mod = Mod(ceil, pow);


[14155]  103  return ceil  mod + pow;


[14152]  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;


 116  }


 117 


[14125]  118  // rounds down to the nearest value according to the given number of decimal precision


 119  private static double Floor(this double value, int precision) {


 120  var n = Math.Pow(10, precision);


 121  return Math.Round(Math.Floor(value * n) / n, precision);


 122  }


 123 


[14152]  124  // rounds up to the nearest value according to the given number of decimal precision


[14125]  125  private static double Ceil(this double value, int precision) {


 126  var n = Math.Pow(10, precision);


 127  return Math.Round(Math.Ceiling(value * n) / n, precision);


 128  }


 129 


 130  private static bool IsAlmost(this double value, double other, double eps = 1e12) {


 131  return Math.Abs(value  other) < eps;


 132  }


[14159]  133 


 134  private static double Mod(this double a, double b) {


 135  return a  b * Math.Floor(a / b);


 136  }


[14125]  137  }


 138  }

