Free cookie consent management tool by TermsFeed Policy Generator

source: branches/crossvalidation-2434/HeuristicLab.Analysis.Statistics.Views/3.3/SampleSizeInfluenceView.cs @ 13782

Last change on this file since 13782 was 12690, checked in by ascheibe, 9 years ago

#2270 fixed cross-threading issues in run collection views

File size: 24.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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;
28using HeuristicLab.Collections;
29using HeuristicLab.Common;
30using HeuristicLab.Core;
31using HeuristicLab.Data;
32using HeuristicLab.MainForm;
33using HeuristicLab.MainForm.WindowsForms;
34using HeuristicLab.Optimization;
35using HeuristicLab.PluginInfrastructure;
36using ViewEventArgs = System.Windows.Forms.DataVisualization.Charting.ViewEventArgs;
37
38namespace HeuristicLab.Analysis.Statistics.Views {
39  [View("Sample Size Influence", "HeuristicLab.Analysis.Statistics.Views.InfoResources.SampleSizeInfluenceInfo.rtf")]
40  [Content(typeof(RunCollection), false)]
41  public partial class SampleSizeInfluenceView : AsynchronousContentView {
42    private enum AxisDimension { Color = 0 }
43    private const string BoxPlotSeriesName = "BoxPlotSeries";
44    private const string BoxPlotChartAreaName = "BoxPlotChartArea";
45    private const string delimiter = ";";
46
47    private bool suppressUpdates;
48    private string yAxisValue;
49    private Dictionary<int, Dictionary<object, double>> categoricalMapping;
50    private SortedDictionary<double, Series> seriesCache;
51
52    public SampleSizeInfluenceView() {
53      InitializeComponent();
54      categoricalMapping = new Dictionary<int, Dictionary<object, double>>();
55      seriesCache = new SortedDictionary<double, Series>();
56      chart.ChartAreas[0].Visible = false;
57      chart.Series.Clear();
58      chart.ChartAreas.Add(BoxPlotChartAreaName);
59      chart.CustomizeAllChartAreas();
60      chart.ChartAreas[BoxPlotChartAreaName].Axes.ToList().ForEach(x => { x.ScaleView.Zoomable = true; x.ScaleView.MinSize = 0; });
61      chart.ChartAreas[BoxPlotChartAreaName].CursorX.Interval = 0.5;
62      chart.ChartAreas[BoxPlotChartAreaName].CursorY.Interval = 1e-5;
63    }
64
65    public new RunCollection Content {
66      get { return (RunCollection)base.Content; }
67      set { base.Content = value; }
68    }
69    public IStringConvertibleMatrix Matrix {
70      get { return Content; }
71    }
72
73    #region RunCollection and Run events
74    protected override void RegisterContentEvents() {
75      base.RegisterContentEvents();
76      Content.ColumnNamesChanged += Content_ColumnNamesChanged;
77      Content.ItemsAdded += Content_ItemsAdded;
78      Content.ItemsRemoved += Content_ItemsRemoved;
79      Content.CollectionReset += Content_CollectionReset;
80      Content.UpdateOfRunsInProgressChanged += Content_UpdateOfRunsInProgressChanged;
81      Content.OptimizerNameChanged += Content_AlgorithmNameChanged;
82      RegisterRunEvents(Content);
83    }
84    protected override void DeregisterContentEvents() {
85      base.DeregisterContentEvents();
86      Content.ColumnNamesChanged -= Content_ColumnNamesChanged;
87      Content.ItemsAdded -= Content_ItemsAdded;
88      Content.ItemsRemoved -= Content_ItemsRemoved;
89      Content.CollectionReset -= Content_CollectionReset;
90      Content.UpdateOfRunsInProgressChanged -= Content_UpdateOfRunsInProgressChanged;
91      Content.OptimizerNameChanged -= Content_AlgorithmNameChanged;
92      DeregisterRunEvents(Content);
93    }
94
95    protected virtual void RegisterRunEvents(IEnumerable<IRun> runs) {
96      foreach (IRun run in runs) {
97        RegisterRunParametersEvents(run);
98        RegisterRunResultsEvents(run);
99      }
100    }
101
102    protected virtual void DeregisterRunEvents(IEnumerable<IRun> runs) {
103      foreach (IRun run in runs) {
104        DeregisterRunParametersEvents(run);
105        DeregisterRunResultsEvents(run);
106      }
107    }
108
109    private void RegisterRunParametersEvents(IRun run) {
110      IObservableDictionary<string, IItem> dict = run.Parameters;
111      dict.ItemsAdded += run_Changed;
112      dict.ItemsRemoved += run_Changed;
113      dict.ItemsReplaced += run_Changed;
114      dict.CollectionReset += run_Changed;
115    }
116
117    private void RegisterRunResultsEvents(IRun run) {
118      IObservableDictionary<string, IItem> dict = run.Results;
119      dict.ItemsAdded += run_Changed;
120      dict.ItemsRemoved += run_Changed;
121      dict.ItemsReplaced += run_Changed;
122      dict.CollectionReset += run_Changed;
123    }
124
125    private void DeregisterRunParametersEvents(IRun run) {
126      IObservableDictionary<string, IItem> dict = run.Parameters;
127      dict.ItemsAdded -= run_Changed;
128      dict.ItemsRemoved -= run_Changed;
129      dict.ItemsReplaced -= run_Changed;
130      dict.CollectionReset -= run_Changed;
131    }
132
133    private void DeregisterRunResultsEvents(IRun run) {
134      IObservableDictionary<string, IItem> dict = run.Results;
135      dict.ItemsAdded -= run_Changed;
136      dict.ItemsRemoved -= run_Changed;
137      dict.ItemsReplaced -= run_Changed;
138      dict.CollectionReset -= run_Changed;
139    }
140
141    private void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
142      DeregisterRunEvents(e.OldItems);
143      RegisterRunEvents(e.Items);
144      if (!suppressUpdates) UpdateAll();
145    }
146    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
147      DeregisterRunEvents(e.Items);
148      if (!suppressUpdates) UpdateComboBoxes();
149    }
150    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
151      RegisterRunEvents(e.Items);
152      if (!suppressUpdates) UpdateComboBoxes();
153    }
154    private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
155      if (InvokeRequired)
156        Invoke(new EventHandler(Content_UpdateOfRunsInProgressChanged), sender, e);
157      else {
158        suppressUpdates = Content.UpdateOfRunsInProgress;
159        if (!suppressUpdates) UpdateAll();
160      }
161    }
162    private void Content_ColumnNamesChanged(object sender, EventArgs e) {
163      if (InvokeRequired)
164        Invoke(new EventHandler(Content_ColumnNamesChanged), sender, e);
165      else {
166        if (!suppressUpdates) UpdateComboBoxes();
167      }
168    }
169    private void run_Changed(object sender, EventArgs e) {
170      if (InvokeRequired)
171        Invoke(new EventHandler(run_Changed), sender, e);
172      else if (!suppressUpdates) UpdateDataPoints();
173    }
174
175    private void Content_AlgorithmNameChanged(object sender, EventArgs e) {
176      if (InvokeRequired)
177        Invoke(new EventHandler(Content_AlgorithmNameChanged), sender, e);
178      else if (!suppressUpdates) UpdateCaption();
179    }
180    #endregion
181
182    #region update comboboxes, datapoints, runs
183    protected override void OnContentChanged() {
184      base.OnContentChanged();
185      UpdateAll();
186    }
187
188    private void UpdateAll() {
189      categoricalMapping.Clear();
190      UpdateComboBoxes();
191      UpdateDataPoints();
192      UpdateCaption();
193    }
194
195    private void UpdateCaption() {
196      Caption = Content != null ? Content.OptimizerName + " Sample Size Influence" : ViewAttribute.GetViewName(GetType());
197    }
198
199    private void UpdateSampleSizes(bool forceUpdate = false) {
200      string selectedYAxis = (string)yAxisComboBox.SelectedItem;
201
202      if (selectedYAxis != null && (xAxisTextBox.Text.Trim() == string.Empty || forceUpdate)) {
203        xAxisTextBox.Clear();
204        List<double> values = new List<double>();
205        foreach (IRun run in Content.Where(x => x.Visible)) {
206          double? cv = GetValue(run, selectedYAxis);
207          if (cv.HasValue) {
208            values.Add(cv.Value);
209          }
210        }
211
212        if (values.Any()) {
213          if (hypergeometricCheckBox.Checked) {
214            xAxisTextBox.Text += values.Count() / 16 + delimiter + " ";
215            xAxisTextBox.Text += values.Count() / 8 + delimiter + " ";
216            xAxisTextBox.Text += values.Count() / 4;
217          } else {
218            xAxisTextBox.Text += values.Count() / 4 + delimiter + " ";
219            xAxisTextBox.Text += values.Count() / 2 + delimiter + " ";
220            xAxisTextBox.Text += values.Count() / 4 * 3 + delimiter + " ";
221            xAxisTextBox.Text += values.Count();
222          }
223        }
224      }
225    }
226
227    private void UpdateComboBoxes() {
228      if (InvokeRequired) {
229        Invoke((Action)UpdateComboBoxes);
230      } else {
231        string selectedYAxis = (string)yAxisComboBox.SelectedItem;
232        xAxisTextBox.Text = string.Empty;
233        yAxisComboBox.Items.Clear();
234        if (Content != null) {
235          string[] additionalAxisDimension = Enum.GetNames(typeof(AxisDimension));
236          UpdateSampleSizes();
237          yAxisComboBox.Items.AddRange(additionalAxisDimension);
238          yAxisComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
239
240          if (selectedYAxis != null && yAxisComboBox.Items.Contains(selectedYAxis)) {
241            yAxisComboBox.SelectedItem = selectedYAxis;
242            UpdateDataPoints();
243          }
244        }
245      }
246    }
247
248    private void UpdateDataPoints() {
249      chart.Series.Clear();
250      seriesCache.Clear();
251      if (Content != null) {
252        var usableRuns = Content.Where(r => r.Visible).ToList();
253        List<int> groupSizes = ParseGroupSizesFromText(xAxisTextBox.Text);
254
255        if (hypergeometricCheckBox.Checked) {
256          CalculateGroupsHypergeometric(usableRuns, groupSizes);
257        } else {
258          CalculateGroups(usableRuns, groupSizes);
259        }
260
261        foreach (Series s in seriesCache.Values)
262          chart.Series.Add(s);
263
264        UpdateStatistics();
265        if (seriesCache.Count > 0) {
266          Series boxPlotSeries = CreateBoxPlotSeries();
267          chart.Series.Add(boxPlotSeries);
268        }
269
270        UpdateAxisLabels();
271        if (groupSizes.Any())
272          AddSampleSizeText();
273      } else {
274        sampleSizeTextBox.Text = string.Empty;
275      }
276      UpdateNoRunsVisibleLabel();
277    }
278
279    private void CalculateGroups(List<IRun> usableRuns, List<int> groupSizes) {
280      Random rand = new Random();
281
282      foreach (int gs in groupSizes) {
283        int idx = gs;
284        List<IRun> runGroup = new List<IRun>();
285        if (idx > usableRuns.Count()) {
286          idx = usableRuns.Count();
287        }
288
289        for (int i = 0; i < idx; i++) {
290          int r = rand.Next(usableRuns.Count());
291          runGroup.Add(usableRuns[r]);
292        }
293        runGroup.ForEach(x => AddDataPoint(x, idx));
294      }
295    }
296
297    private void CalculateGroupsHypergeometric(List<IRun> usableRuns, List<int> groupSizes) {
298      Random rand = new Random();
299      var runs = new List<IRun>(usableRuns);
300
301      foreach (int gs in groupSizes) {
302        int idx = gs;
303        List<IRun> runGroup = new List<IRun>();
304        if (idx > runs.Count()) {
305          idx = runs.Count();
306        }
307
308        for (int i = 0; i < idx; i++) {
309          int r = rand.Next(runs.Count());
310          runGroup.Add(runs[r]);
311          runs.Remove(runs[r]);
312        }
313        runGroup.ForEach(x => AddDataPoint(x, idx));
314      }
315    }
316
317    private void AddSampleSizeText() {
318      sampleSizeTextBox.Text = string.Empty;
319      var usableRuns = Content.Where(r => r.Visible).ToList();
320
321      if (!yAxisComboBox.DroppedDown)
322        yAxisValue = (string)yAxisComboBox.SelectedItem;
323
324      List<double?> yValue = usableRuns.Select(x => GetValue(x, yAxisValue)).ToList();
325      if (yValue.Any(x => !x.HasValue)) return;
326
327      double estimatedSampleSize = SampleSizeDetermination.DetermineSampleSizeByEstimatingMean(yValue.Select(x => x.Value).ToArray());
328      sampleSizeTextBox.Text = estimatedSampleSize.ToString();
329    }
330
331    private List<int> ParseGroupSizesFromText(string groupsText, bool verbose = true) {
332      string[] gs = groupsText.Split(delimiter.ToCharArray());
333      List<int> vals = new List<int>();
334
335      foreach (string s in gs) {
336        string ns = s.Trim();
337
338        if (ns != string.Empty) {
339          int v = 0;
340          try {
341            v = int.Parse(ns);
342            vals.Add(v);
343          }
344          catch (Exception ex) {
345            if (verbose) {
346              ErrorHandling.ShowErrorDialog("Can't parse group sizes. Please only use numbers seperated by a " + delimiter + ". ", ex);
347            }
348          }
349        }
350      }
351      return vals;
352    }
353
354    private void UpdateStatistics() {
355      DoubleMatrix matrix = new DoubleMatrix(11, seriesCache.Count);
356      matrix.SortableView = false;
357      List<string> columnNames = new List<string>();
358      foreach (Series series in seriesCache.Values) {
359        DataPoint datapoint = series.Points.FirstOrDefault();
360        if (datapoint != null) {
361          IRun run = (IRun)datapoint.Tag;
362          string selectedAxis = xAxisTextBox.Text;
363          IItem value = null;
364
365          if (Enum.IsDefined(typeof(AxisDimension), selectedAxis)) {
366            AxisDimension axisDimension = (AxisDimension)Enum.Parse(typeof(AxisDimension), selectedAxis);
367            switch (axisDimension) {
368              case AxisDimension.Color: value = new StringValue(run.Color.ToString());
369                break;
370            }
371          }
372
373          string columnName = series.Name;
374          columnNames.Add(columnName);
375        }
376      }
377      matrix.ColumnNames = columnNames;
378      matrix.RowNames = new[] { "Count", "Minimum", "Maximum", "Average", "Median", "Standard Deviation", "Variance", "25th Percentile", "75th Percentile", "Lower Confidence Int.", "Upper Confidence Int." };
379
380      for (int i = 0; i < seriesCache.Count; i++) {
381        Series series = seriesCache.ElementAt(i).Value;
382        double[] seriesValues = series.Points.Select(p => p.YValues[0]).OrderBy(d => d).ToArray();
383        Tuple<double, double> confIntervals = seriesValues.ConfidenceIntervals(0.95);
384        matrix[0, i] = seriesValues.Length;
385        matrix[1, i] = seriesValues.Min();
386        matrix[2, i] = seriesValues.Max();
387        matrix[3, i] = seriesValues.Average();
388        matrix[4, i] = seriesValues.Median();
389        matrix[5, i] = seriesValues.StandardDeviation();
390        matrix[6, i] = seriesValues.Variance();
391        matrix[7, i] = seriesValues.Percentile(0.25);
392        matrix[8, i] = seriesValues.Percentile(0.75);
393        matrix[9, i] = confIntervals.Item1;
394        matrix[10, i] = confIntervals.Item2;
395      }
396      statisticsMatrixView.Content = matrix;
397    }
398
399    private Series CreateBoxPlotSeries() {
400      Series boxPlotSeries = new Series(BoxPlotSeriesName);
401      string seriesNames = string.Concat(seriesCache.Keys.Select(x => x.ToString() + ";").ToArray());
402      seriesNames = seriesNames.Remove(seriesNames.Length - 1); //delete last ; from string
403
404      boxPlotSeries.ChartArea = BoxPlotChartAreaName;
405      boxPlotSeries.ChartType = SeriesChartType.BoxPlot;
406      boxPlotSeries["BoxPlotSeries"] = seriesNames;
407      boxPlotSeries["BoxPlotShowUnusualValues"] = "true";
408      boxPlotSeries["PointWidth"] = "0.4";
409      boxPlotSeries.BackGradientStyle = GradientStyle.VerticalCenter;
410      boxPlotSeries.BackSecondaryColor = Color.FromArgb(130, 224, 64, 10);
411      boxPlotSeries.BorderColor = Color.FromArgb(64, 64, 64);
412      boxPlotSeries.Color = Color.FromArgb(224, 64, 10);
413
414      return boxPlotSeries;
415    }
416
417    private void AddDataPoint(IRun run, int idx) {
418      double xValue;
419      double? yValue;
420
421
422      if (!yAxisComboBox.DroppedDown)
423        yAxisValue = (string)yAxisComboBox.SelectedItem;
424
425      xValue = idx;
426      yValue = GetValue(run, yAxisValue);
427
428      if (yValue.HasValue) {
429        if (!seriesCache.ContainsKey(xValue))
430          seriesCache[xValue] = new Series(xValue.ToString());
431
432        Series series = seriesCache[xValue];
433        DataPoint point = new DataPoint(xValue, yValue.Value);
434        point.Tag = run;
435        series.Points.Add(point);
436      }
437    }
438    #endregion
439
440    #region get values from run
441    private double? GetValue(IRun run, string columnName) {
442      if (run == null || string.IsNullOrEmpty(columnName))
443        return null;
444
445      if (Enum.IsDefined(typeof(AxisDimension), columnName)) {
446        AxisDimension axisDimension = (AxisDimension)Enum.Parse(typeof(AxisDimension), columnName);
447        return GetValue(run, axisDimension);
448      }
449      int columnIndex = Matrix.ColumnNames.ToList().IndexOf(columnName);
450      IItem value = Content.GetValue(run, columnIndex);
451      if (value == null)
452        return null;
453
454      DoubleValue doubleValue = value as DoubleValue;
455      IntValue intValue = value as IntValue;
456      TimeSpanValue timeSpanValue = value as TimeSpanValue;
457      double? ret = null;
458      if (doubleValue != null) {
459        if (!double.IsNaN(doubleValue.Value) && !double.IsInfinity(doubleValue.Value))
460          ret = doubleValue.Value;
461      } else if (intValue != null)
462        ret = intValue.Value;
463      else if (timeSpanValue != null) {
464        ret = timeSpanValue.Value.TotalSeconds;
465      } else
466        ret = GetCategoricalValue(columnIndex, value.ToString());
467
468      return ret;
469    }
470    private double GetCategoricalValue(int dimension, string value) {
471      if (!categoricalMapping.ContainsKey(dimension)) {
472        categoricalMapping[dimension] = new Dictionary<object, double>();
473        var orderedCategories = Content.Where(r => r.Visible && Content.GetValue(r, dimension) != null).Select(r => Content.GetValue(r, dimension).ToString())
474                                          .Distinct().OrderBy(x => x, new NaturalStringComparer());
475        int count = 1;
476        foreach (var category in orderedCategories) {
477          categoricalMapping[dimension].Add(category, count);
478          count++;
479        }
480      }
481      return categoricalMapping[dimension][value];
482    }
483    private double GetValue(IRun run, AxisDimension axisDimension) {
484      double value = double.NaN;
485      switch (axisDimension) {
486        case AxisDimension.Color: {
487            value = GetCategoricalValue(-1, run.Color.ToString());
488            break;
489          }
490        default: {
491            throw new ArgumentException("No handling strategy for " + axisDimension + " is defined.");
492          }
493      }
494      return value;
495    }
496    #endregion
497
498    #region GUI events
499    private void UpdateNoRunsVisibleLabel() {
500      if (chart.Series.Count > 0) {
501        noRunsLabel.Visible = false;
502        showStatisticsCheckBox.Enabled = true;
503        splitContainer.Panel2Collapsed = !showStatisticsCheckBox.Checked;
504      } else {
505        noRunsLabel.Visible = true;
506        showStatisticsCheckBox.Enabled = false;
507        splitContainer.Panel2Collapsed = true;
508      }
509    }
510
511    private void RecalculateButton_Click(object sender, EventArgs e) {
512      UpdateDataPoints();
513    }
514
515    private void hypergeometricCheckBox_CheckedChanged(object sender, EventArgs e) {
516      UpdateSampleSizes(true);
517      UpdateDataPoints();
518    }
519
520    private void AxisComboBox_SelectedIndexChanged(object sender, EventArgs e) {
521      UpdateSampleSizes();
522      UpdateDataPoints();
523    }
524    private void UpdateAxisLabels() {
525      Axis xAxis = chart.ChartAreas[BoxPlotChartAreaName].AxisX;
526      Axis yAxis = chart.ChartAreas[BoxPlotChartAreaName].AxisY;
527      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
528
529      SetCustomAxisLabels(xAxis, -1);
530      SetCustomAxisLabels(yAxis, yAxisComboBox.SelectedIndex - axisDimensionCount);
531
532      xAxis.Title = "Group Size";
533      if (yAxisComboBox.SelectedItem != null)
534        yAxis.Title = yAxisComboBox.SelectedItem.ToString();
535    }
536
537    private void chart_AxisViewChanged(object sender, ViewEventArgs e) {
538      UpdateAxisLabels();
539    }
540
541    private void SetCustomAxisLabels(Axis axis, int dimension) {
542      axis.CustomLabels.Clear();
543      if (categoricalMapping.ContainsKey(dimension)) {
544        int position = 1;
545        foreach (var pair in categoricalMapping[dimension].Where(x => seriesCache.ContainsKey(x.Value))) {
546          string labelText = pair.Key.ToString();
547          CustomLabel label = new CustomLabel();
548          label.ToolTip = labelText;
549          if (labelText.Length > 25)
550            labelText = labelText.Substring(0, 25) + " ... ";
551          label.Text = labelText;
552          label.GridTicks = GridTickTypes.TickMark;
553          label.FromPosition = position - 0.5;
554          label.ToPosition = position + 0.5;
555          axis.CustomLabels.Add(label);
556          position++;
557        }
558      } else if (dimension > 0 && Content.GetValue(0, dimension) is TimeSpanValue) {
559        chart.ChartAreas[0].RecalculateAxesScale();
560        Axis correspondingAxis = chart.ChartAreas[0].Axes.Where(x => x.Name == axis.Name).SingleOrDefault();
561        if (correspondingAxis == null)
562          correspondingAxis = axis;
563        for (double i = correspondingAxis.Minimum; i <= correspondingAxis.Maximum; i += correspondingAxis.LabelStyle.Interval) {
564          TimeSpan time = TimeSpan.FromSeconds(i);
565          string x = string.Format("{0:00}:{1:00}:{2:00}", time.Hours, time.Minutes, time.Seconds);
566          axis.CustomLabels.Add(i - correspondingAxis.LabelStyle.Interval / 2, i + correspondingAxis.LabelStyle.Interval / 2, x);
567        }
568      } else if (chart.ChartAreas[BoxPlotChartAreaName].AxisX == axis) {
569        double position = 1.0;
570        foreach (Series series in chart.Series) {
571          if (series.Name != BoxPlotSeriesName) {
572            string labelText = series.Points[0].XValue.ToString();
573            CustomLabel label = new CustomLabel();
574            label.FromPosition = position - 0.5;
575            label.ToPosition = position + 0.5;
576            label.GridTicks = GridTickTypes.TickMark;
577            label.Text = labelText;
578            axis.CustomLabels.Add(label);
579            position++;
580          }
581        }
582      }
583    }
584
585    private void chart_MouseMove(object sender, MouseEventArgs e) {
586      string newTooltipText = string.Empty;
587      string oldTooltipText;
588      HitTestResult h = chart.HitTest(e.X, e.Y);
589      if (h.ChartElementType == ChartElementType.AxisLabels) {
590        newTooltipText = ((CustomLabel)h.Object).ToolTip;
591      }
592
593      oldTooltipText = tooltip.GetToolTip(chart);
594      if (newTooltipText != oldTooltipText)
595        tooltip.SetToolTip(chart, newTooltipText);
596    }
597    #endregion
598
599    private void showStatisticsCheckBox_CheckedChanged(object sender, EventArgs e) {
600      splitContainer.Panel2Collapsed = !showStatisticsCheckBox.Checked;
601    }
602
603    private void defineSampleSizeButton_Click(object sender, EventArgs e) {
604      int min = 0, max = 0, step = 1;
605      var groupSizes = ParseGroupSizesFromText(xAxisTextBox.Text);
606      if (groupSizes.Count() > 0) {
607        min = groupSizes.Min();
608        max = groupSizes.Max();
609      }
610
611      using (var dialog = new DefineArithmeticProgressionDialog(true, min, max, step)) {
612        if (dialog.ShowDialog(this) == DialogResult.OK) {
613          var values = dialog.Values;
614          string newVals = "";
615          foreach (int v in values) {
616            newVals += v + delimiter + " ";
617          }
618          xAxisTextBox.Text = newVals;
619        }
620      }
621    }
622
623    private void xAxisTextBox_TextChanged(object sender, EventArgs e) {
624      var result = ParseGroupSizesFromText(xAxisTextBox.Text, false);
625
626      if (seriesCache.Count() == result.Count()) {
627        bool changed = false;
628        int i = 0;
629        foreach (var gs in seriesCache.Keys) {
630          if (((int)gs) != result[i]) {
631            changed = true;
632            break;
633          }
634          i++;
635        }
636
637        if (changed) {
638          UpdateDataPoints();
639        }
640      } else {
641        UpdateDataPoints();
642      }
643    }
644  }
645}
Note: See TracBrowser for help on using the repository browser.