source: trunk/sources/HeuristicLab.Visualization.ChartControlsExtensions/3.3/HistogramControl.cs @ 11608

Last change on this file since 11608 was 11608, checked in by ascheibe, 5 years ago

#2279 added support for multiple data rows in histogram control

File size: 6.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using System.Windows.Forms.DataVisualization.Charting;
28
29namespace HeuristicLab.Visualization.ChartControlsExtensions {
30  public partial class HistogramControl : UserControl {
31    protected static readonly string SeriesName = "Histogram";
32
33    protected Dictionary<string, List<double>> points;
34    public int NumberOfBins {
35      get { return (int)binsNumericUpDown.Value; }
36      set { binsNumericUpDown.Value = value; }
37    }
38
39    public int MinimumNumberOfBins {
40      get { return (int)binsNumericUpDown.Minimum; }
41      set { binsNumericUpDown.Minimum = value; }
42    }
43
44    public int MaximumNumberOfBins {
45      get { return (int)binsNumericUpDown.Maximum; }
46      set { binsNumericUpDown.Maximum = value; }
47    }
48
49    public int IncrementNumberOfBins {
50      get { return (int)binsNumericUpDown.Increment; }
51      set { binsNumericUpDown.Increment = value; }
52    }
53
54    public bool CalculateExactBins {
55      get { return exactCheckBox.Checked; }
56      set { exactCheckBox.Checked = value; }
57    }
58
59    public bool ShowExactCheckbox {
60      get { return exactCheckBox.Visible; }
61      set { exactCheckBox.Visible = value; }
62    }
63
64    public HistogramControl() {
65      InitializeComponent();
66      points = new Dictionary<string, List<double>>();
67      InitializeChart();
68    }
69
70    protected void InitNewRow(string name) {
71      if (!points.ContainsKey(name)) {
72        points.Add(name, new List<double>());
73      }
74    }
75
76    protected void InitSeries(string name) {
77      if (!chart.Series.Any(x => x.Name == name)) {
78        Series s = chart.Series.Add(name);
79        s.ChartType = SeriesChartType.Column;
80        s.BorderColor = Color.Black;
81        s.BorderWidth = 1;
82        s.BorderDashStyle = ChartDashStyle.Solid;
83      }
84    }
85
86    public void AddPoint(double point) {
87      InitNewRow(SeriesName);
88      InitSeries(SeriesName);
89      points[SeriesName].Add(point);
90      UpdateHistogram();
91    }
92
93    public void AddPoints(IEnumerable<double> points) {
94      InitNewRow(SeriesName);
95      InitSeries(SeriesName);
96      this.points[SeriesName].AddRange(points);
97      UpdateHistogram();
98    }
99
100    public void AddPoint(string name, double point, bool replace = false) {
101      InitNewRow(name);
102      InitSeries(name);
103      if (replace) {
104        points[name].Clear();
105      }
106      points[name].Add(point);
107      UpdateHistogram();
108    }
109
110    public void AddPoints(string name, IEnumerable<double> points, bool replace = false) {
111      InitNewRow(name);
112      InitSeries(name);
113      if (replace) {
114        this.points[name].Clear();
115      }
116      this.points[name].AddRange(points);
117      UpdateHistogram();
118    }
119
120    public void ClearPoints() {
121      points.Clear();
122      UpdateHistogram();
123    }
124
125    protected void InitializeChart() {
126      chart.Series.Clear();
127    }
128
129    protected void UpdateHistogram() {
130      if (InvokeRequired) {
131        Invoke((Action)UpdateHistogram, null);
132        return;
133      }
134
135      double overallMin = double.MaxValue;
136      double overallMax = double.MinValue;
137      int bins = (int)binsNumericUpDown.Value;
138
139      chart.Series.Clear();
140
141      foreach (var point in points) {
142        if (!point.Value.Any()) continue;
143
144        Series histogramSeries = new Series(point.Key);
145        chart.Series.Add(histogramSeries);
146
147        double minValue = point.Value.Min();
148        double maxValue = point.Value.Max();
149        double intervalWidth = (maxValue - minValue) / bins;
150        if (intervalWidth <= 0) continue;
151
152        if (!exactCheckBox.Checked) {
153          intervalWidth = HumanRoundRange(intervalWidth);
154          minValue = Math.Floor(minValue / intervalWidth) * intervalWidth;
155          maxValue = Math.Ceiling(maxValue / intervalWidth) * intervalWidth;
156        }
157
158        double current = minValue, intervalCenter = intervalWidth / 2.0;
159        int count = 0;
160        foreach (double v in point.Value.OrderBy(x => x)) {
161          while (v > current + intervalWidth) {
162            histogramSeries.Points.AddXY(current + intervalCenter, count);
163            current += intervalWidth;
164            count = 0;
165          }
166          count++;
167        }
168        histogramSeries.Points.AddXY(current + intervalCenter, count);
169        histogramSeries["PointWidth"] = "1";
170
171        overallMax = Math.Max(overallMax, maxValue);
172        overallMin = Math.Min(overallMin, minValue);
173      }
174
175      ChartArea chartArea = chart.ChartAreas[0];
176      chartArea.AxisY.Title = "Frequency";
177      chartArea.AxisX.Minimum = overallMin;
178      chartArea.AxisX.Maximum = overallMax;
179
180      double overallIntervalWidth = (overallMax - overallMin) / bins;
181
182      double axisInterval = overallIntervalWidth;
183      while ((overallMax - overallMin) / axisInterval > 10.0) {
184        axisInterval *= 2.0;
185      }
186      chartArea.AxisX.Interval = axisInterval;
187    }
188
189    protected double HumanRoundRange(double range) {
190      double base10 = Math.Pow(10.0, Math.Floor(Math.Log10(range)));
191      double rounding = range / base10;
192      if (rounding <= 1.5) rounding = 1;
193      else if (rounding <= 2.25) rounding = 2;
194      else if (rounding <= 3.75) rounding = 2.5;
195      else if (rounding <= 7.5) rounding = 5;
196      else rounding = 10;
197      return rounding * base10;
198    }
199
200    private void binsNumericUpDown_ValueChanged(object sender, EventArgs e) {
201      UpdateHistogram();
202    }
203
204    private void exactCheckBox_CheckedChanged(object sender, EventArgs e) {
205      UpdateHistogram();
206    }
207  }
208}
Note: See TracBrowser for help on using the repository browser.