source: trunk/sources/HeuristicLab.Visualization.ChartControlsExtensions/3.3/ChartUtil.cs @ 14008

Last change on this file since 14008 was 14008, checked in by bburlacu, 6 years ago

#2594: Added chart util method to calculate the "optimal" axis interval (reducing the number of fractional digits to make it look nice). Modified the regression solution line chart and scatter plot to use the new scaling method.

File size: 3.5 KB
Line 
1#region License Information
2
3/* HeuristicLab
4 * Copyright (C) 2002-2016 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
24using System;
25
26namespace 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) {
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;
41      }
42      axisMax = axisMin + axisRange;
43    }
44
45    // this method tries to find an axis interval with as few fractional digits as possible (because it looks nicer)
46    // we only try between 3 and 5 ticks (inclusive) because it wouldn't make sense to exceed this interval
47    public static void CalculateOptimalAxisInterval(double min, double max, out double axisMin, out double axisMax, out double axisInterval) {
48      CalculateAxisInterval(min, max, 5, out axisMin, out axisMax, out axisInterval);
49      int bestLsp = int.MaxValue;
50      for (int ticks = 3; ticks <= 5; ++ticks) {
51        double aMin, aMax, aInterval;
52        CalculateAxisInterval(min, max, ticks, out aMin, out aMax, out aInterval);
53        var x = aInterval;
54        int lsp = 0; // position of the least significant fractional digit
55        while (x - Math.Floor(x) > 0) {
56          ++lsp;
57          x *= 10;
58        }
59        if (lsp <= bestLsp) {
60          axisMin = aMin;
61          axisMax = aMax;
62          axisInterval = aInterval;
63          bestLsp = lsp;
64        }
65      }
66    }
67
68    private static int Decimals(this double x) {
69      if (x.IsAlmost(0) || double.IsInfinity(x) || double.IsNaN(x))
70        return 0;
71
72      var v = Math.Abs(x);
73      int d = 1;
74      while (v < 1) {
75        v *= 10;
76        d++;
77      }
78      return d;
79    }
80
81    // rounds down to the nearest value according to the given number of decimal precision
82    private static double Floor(this double value, int precision) {
83      var n = Math.Pow(10, precision);
84      return Math.Round(Math.Floor(value * n) / n, precision);
85    }
86
87    private static double Ceil(this double value, int precision) {
88      var n = Math.Pow(10, precision);
89      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);
95    }
96
97    private static bool IsAlmost(this double value, double other, double eps = 1e-12) {
98      return Math.Abs(value - other) < eps;
99    }
100  }
101}
Note: See TracBrowser for help on using the repository browser.