Free cookie consent management tool by TermsFeed Policy Generator

source: branches/symbreg-factors-2650/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionParameterAnalysisView.cs @ 14825

Last change on this file since 14825 was 14751, checked in by gkronber, 8 years ago

#2650: merged r14597:14737 from trunk to branch

File size: 25.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 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.ComponentModel;
25using System.Data;
26using System.Linq;
27using System.Windows.Forms;
28using HeuristicLab.Analysis;
29using HeuristicLab.Collections;
30using HeuristicLab.Common;
31using HeuristicLab.Core.Views;
32using HeuristicLab.MainForm;
33using System.Windows.Forms.DataVisualization.Charting;
34using System.Text;
35using HeuristicLab.Data;
36using System.Drawing;
37using HeuristicLab.Core;
38
39namespace HeuristicLab.Optimization.Views {
40  [View("Parameter Analysis")]
41  [Content(typeof(RunCollection), false)]
42  public sealed partial class RunCollectionParameterAnalysisView : ItemView {
43    #region Colors
44    private static readonly Color[] colors = new[] {
45      Color.FromArgb(0x40, 0x6A, 0xB7),
46      Color.FromArgb(0xB1, 0x6D, 0x01),
47      Color.FromArgb(0x4E, 0x8A, 0x06),
48      Color.FromArgb(0x75, 0x50, 0x7B),
49      Color.FromArgb(0x72, 0x9F, 0xCF),
50      Color.FromArgb(0xA4, 0x00, 0x00),
51      Color.FromArgb(0xAD, 0x7F, 0xA8),
52      Color.FromArgb(0x29, 0x50, 0xCF),
53      Color.FromArgb(0x90, 0xB0, 0x60),
54      Color.FromArgb(0xF5, 0x89, 0x30),
55      Color.FromArgb(0x55, 0x57, 0x53),
56      Color.FromArgb(0xEF, 0x59, 0x59),
57      Color.FromArgb(0xED, 0xD4, 0x30),
58      Color.FromArgb(0x63, 0xC2, 0x16),
59    };
60    #endregion
61
62    private bool suppressUpdates = false;
63    private int stepSize = 1000;
64    private Dictionary<IRun, List<Tuple<int, double>>> runData;
65    private Dictionary<string, ParameterInfo> paramInfos;
66
67    public new RunCollection Content {
68      get { return (RunCollection)base.Content; }
69      set { base.Content = value; }
70    }
71
72    public RunCollectionParameterAnalysisView() {
73      InitializeComponent();
74      chart.CustomizeAllChartAreas();
75      stepSizeTextBox.Text = stepSize.ToString();
76      errorProvider.SetIconAlignment(stepSizeTextBox, ErrorIconAlignment.MiddleLeft);
77      errorProvider.SetIconPadding(stepSizeTextBox, 2);
78    }
79
80    #region Content Events
81    protected override void DeregisterContentEvents() {
82      Content.ItemsAdded -= Content_ItemsAdded;
83      Content.ItemsRemoved -= Content_ItemsRemoved;
84      Content.CollectionReset -= Content_CollectionReset;
85      Content.UpdateOfRunsInProgressChanged -= Content_UpdateOfRunsInProgressChanged;
86      DeregisterRunEvents(Content);
87      base.DeregisterContentEvents();
88    }
89    protected override void RegisterContentEvents() {
90      base.RegisterContentEvents();
91      Content.ItemsAdded += Content_ItemsAdded;
92      Content.ItemsRemoved += Content_ItemsRemoved;
93      Content.CollectionReset += Content_CollectionReset;
94      Content.UpdateOfRunsInProgressChanged += Content_UpdateOfRunsInProgressChanged;
95      RegisterRunEvents(Content);
96    }
97    private void DeregisterRunEvents(IEnumerable<IRun> runs) {
98      foreach (var run in runs)
99        run.PropertyChanged -= Run_PropertyChanged;
100    }
101    private void RegisterRunEvents(IEnumerable<IRun> runs) {
102      foreach (var run in runs)
103        run.PropertyChanged += Run_PropertyChanged;
104    }
105
106    private void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
107      RegisterRunEvents(e.Items);
108    }
109    private void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
110      DeregisterRunEvents(e.Items);
111    }
112    private void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
113      DeregisterRunEvents(e.OldItems);
114      RegisterRunEvents(e.Items);
115    }
116    private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
117      if (InvokeRequired)
118        Invoke((Action<object, EventArgs>)Content_UpdateOfRunsInProgressChanged, sender, e);
119      else {
120        suppressUpdates = Content.UpdateOfRunsInProgress;
121        if (suppressUpdates) return;
122        CollectRunData();
123        AnalyzeParameters();
124        UpdateChart();
125      }
126    }
127    private void Run_PropertyChanged(object sender, PropertyChangedEventArgs e) {
128      if (suppressUpdates) return;
129      if (InvokeRequired)
130        Invoke((Action<object, PropertyChangedEventArgs>)Run_PropertyChanged, sender, e);
131      else {
132        CollectRunData();
133        AnalyzeParameters();
134        UpdateChart();
135      }
136    }
137    #endregion
138
139    protected override void OnContentChanged() {
140      base.OnContentChanged();
141      CollectRunData();
142      AnalyzeParameters();
143      UpdateChart();
144    }
145    protected override void SetEnabledStateOfControls() {
146      base.SetEnabledStateOfControls();
147      parametersGroupBox.Enabled = Content != null;
148      groupsGroupBox.Enabled = Content != null;
149      stepSizeTextBox.Enabled = Content != null;
150      logScalingCheckBox.Enabled = Content != null;
151      dataRowsGroupBox.Enabled = Content != null;
152    }
153
154    private void AnalyzeParameters() {
155      paramInfos = new Dictionary<string, ParameterInfo>();
156      if (Content == null) return;
157
158      foreach (var run in runData.Keys) {
159        foreach (var param in run.Parameters) {
160          ParameterInfo info;
161          if (!paramInfos.TryGetValue(param.Key, out info))
162            paramInfos.Add(param.Key, new ParameterInfo(param.Key, param.Value.ToString(), run));
163          else
164            info.AddValue(param.Value.ToString(), run);
165        }
166      }
167
168      // remove irrelevant parameters
169      if (paramInfos.ContainsKey("Seed"))
170        paramInfos.Remove("Seed");
171
172      // remove all parameters which only have a single value
173      var singles = new List<string>();
174      foreach (var paramInfo in paramInfos.Values) {
175        if (paramInfo.Values.Count == 1)
176          singles.Add(paramInfo.Name);
177      }
178      foreach (var single in singles)
179        paramInfos.Remove(single);
180
181      // set color of parameter values
182      int i = 0;
183      foreach (var valueInfo in paramInfos.Values.SelectMany(x => x.Values.Values)) {
184        valueInfo.Color = colors[i];
185        i = (i + 1) % colors.Length;
186      }
187
188      // populate parametersTreeView
189      parametersTreeView.Nodes.Clear();
190      var paramsRoot = new TreeNode("All Runs (" + runData.Keys.Count + ")");
191      paramsRoot.Tag = runData.Keys.ToList();
192      foreach (var paramInfo in paramInfos.Values.OrderBy(x => x.Name)) {
193        var node = new TreeNode(paramInfo.Name + " (" + paramInfo.RunCount + ")");
194        foreach (var value in paramInfo.Values) {
195          var child = new TreeNode(value.Key + " (" + value.Value.RunCount + ")");
196          child.Tag = value.Value;
197          node.Nodes.Add(child);
198        }
199        node.Tag = paramInfo;
200        paramsRoot.Nodes.Add(node);
201      }
202      parametersTreeView.Nodes.Add(paramsRoot);
203      paramsRoot.Expand();
204      paramsRoot.Checked = true;
205      parametersTreeView.SelectedNode = paramsRoot;
206
207      // populate groupsTreeView
208      groupsTreeView.Nodes.Clear();
209      var groupsRoot = new TreeNode("All Runs (" + runData.Keys.Count + ")");
210      groupsRoot.Tag = runData.Keys.ToList();
211      groupsTreeView.Nodes.Add(groupsRoot);
212      groupsRoot.Expand();
213      groupsTreeView.SelectedNode = groupsRoot;
214    }
215    private void CollectRunData() {
216      runData = new Dictionary<IRun, List<Tuple<int, double>>>();
217      if (Content == null) return;
218
219      foreach (var run in Content) {
220        try {
221          IList<Tuple<double, double>> values = (run.Results["QualityPerEvaluations"] as IndexedDataTable<double>).Rows["First-hit Graph"].Values;
222          var bestKnown = (run.Results["BestKnownQuality"] as DoubleValue).Value;
223
224          var data = new List<Tuple<int, double>>();
225          int i = 0;
226          double qual = double.NaN;
227          foreach (var val in values) {
228            while (i * stepSize < val.Item1) {
229              if (!double.IsNaN(qual)) data.Add(Tuple.Create(i * stepSize, qual));
230              i++;
231            }
232            var diff = Math.Abs(bestKnown - val.Item2);
233            qual = bestKnown == 0 ? diff : diff / bestKnown;
234          }
235          runData.Add(run, data);
236        }
237        catch {
238        }
239      }
240    }
241
242    private void UpdateChart() {
243      if (suppressUpdates) return;
244
245      chart.Series.Clear();
246      chart.Legends[0].CustomItems.Clear();
247      chart.Titles[0].Text = string.Empty;
248
249      var checkedNodes = TraverseTreeNodes(parametersTreeView.Nodes)
250                           .Where(x => x.Checked)
251                           .Concat(
252                             TraverseTreeNodes(groupsTreeView.Nodes)
253                               .Where(x => x.Checked)
254                           );
255
256      foreach (var node in checkedNodes) {
257        if (node.Parent == null) {
258          var series = BuildSeries(node.Tag as IEnumerable<IRun>, colors[0]);
259          chart.Titles[0].Text = node.Text;
260          foreach (var s in series)
261            chart.Series.Add(s);
262          var legendItem = new LegendItem();
263          var legendItemInfo = new LegendItemInfo(colors[0], series);
264          legendItem.Color = legendItemInfo.Color;
265          legendItem.BorderColor = Color.Transparent;
266          legendItem.Name = node.Text;
267          legendItem.Tag = legendItemInfo;
268          chart.Legends[0].CustomItems.Add(legendItem);
269        } else if (node.Tag is ParameterInfo) {
270          var paramInfo = node.Tag as ParameterInfo;
271          chart.Titles[0].Text = paramInfo.Name + " (" + paramInfo.RunCount + ")";
272          foreach (var value in paramInfo.Values) {
273            var series = BuildSeries(value.Value.Runs, value.Value.Color);
274            foreach (var s in series)
275              chart.Series.Add(s);
276            var legendItem = new LegendItem();
277            var legendItemInfo = new LegendItemInfo(value.Value.Color, series);
278            legendItem.Color = legendItemInfo.Color;
279            legendItem.BorderColor = Color.Transparent;
280            legendItem.Name = value.Key + " (" + value.Value.RunCount + ")";
281            legendItem.Tag = legendItemInfo;
282            chart.Legends[0].CustomItems.Add(legendItem);
283          }
284        } else if (node.Tag is ParameterValueInfo) {
285          var valueInfo = node.Tag as ParameterValueInfo;
286          var series = BuildSeries(valueInfo.Runs, valueInfo.Color);
287          chart.Titles[0].Text = node.Parent.Text;
288          foreach (var s in series)
289            chart.Series.Add(s);
290          var legendItem = new LegendItem();
291          var legendItemInfo = new LegendItemInfo(valueInfo.Color, series);
292          legendItem.Color = legendItemInfo.Color;
293          legendItem.BorderColor = Color.Transparent;
294          legendItem.Name = node.Text;
295          legendItem.Tag = legendItemInfo;
296          chart.Legends[0].CustomItems.Add(legendItem);
297        } else if (node.Tag is GroupInfo) {
298          var groupInfo = node.Tag as GroupInfo;
299          if (groupInfo.IsParameter) {
300            chart.Titles[0].Text = groupInfo.Text + " (" + groupInfo.Runs.Count + ")";
301            foreach (TreeNode child in node.Nodes) {
302              var childInfo = child.Tag as GroupInfo;
303              var series = BuildSeries(childInfo.Runs, childInfo.Color);
304              foreach (var s in series)
305                chart.Series.Add(s);
306              var legendItem = new LegendItem();
307              var legendItemInfo = new LegendItemInfo(childInfo.Color, series);
308              legendItem.Color = legendItemInfo.Color;
309              legendItem.BorderColor = Color.Transparent;
310              legendItem.Name = childInfo.Text + " (" + childInfo.Runs.Count + ")";
311              legendItem.Tag = legendItemInfo;
312              chart.Legends[0].CustomItems.Add(legendItem);
313            }
314          } else {
315            var parentInfo = node.Parent.Tag as GroupInfo;
316            chart.Titles[0].Text = parentInfo.Text + " (" + parentInfo.Runs.Count + ")";
317            var series = BuildSeries(groupInfo.Runs, groupInfo.Color);
318            foreach (var s in series)
319              chart.Series.Add(s);
320            var legendItem = new LegendItem();
321            var legendItemInfo = new LegendItemInfo(groupInfo.Color, series);
322            legendItem.Color = legendItemInfo.Color;
323            legendItem.BorderColor = Color.Transparent;
324            legendItem.Name = groupInfo.Text + " (" + groupInfo.Runs.Count + ")";
325            legendItem.Tag = legendItemInfo;
326            chart.Legends[0].CustomItems.Add(legendItem);
327          }
328        }
329      }
330    }
331    private void UpdateSeriesVisibility() {
332      foreach (var legendItem in chart.Legends[0].CustomItems) {
333        var legendItemInfo = legendItem.Tag as LegendItemInfo;
334        foreach (var s in legendItemInfo.Series) {
335          if (legendItemInfo.SeriesVisible) {
336            var seriesInfo = s.Tag as SeriesInfo;
337            switch (seriesInfo.Type) {
338              case SeriesTypes.MinMax:
339                s.Color = minMaxCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
340                break;
341              case SeriesTypes.Quartiles:
342                s.Color = quartilesCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
343                break;
344              case SeriesTypes.Median:
345                s.Color = medianCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
346                break;
347              case SeriesTypes.Average:
348                s.Color = averageCheckBox.Checked ? seriesInfo.Color : Color.Transparent;
349                break;
350            }
351          } else {
352            s.Color = Color.Transparent;
353          }
354        }
355      }
356    }
357
358    private IEnumerable<Series> BuildSeries(IEnumerable<IRun> runs, Color color) {
359      var values = new Dictionary<int, List<double>>();
360      foreach (var run in runs) {
361        foreach (var step in runData[run]) {
362          List<double> vals;
363          if (!values.TryGetValue(step.Item1, out vals)) {
364            vals = new List<double>();
365            values.Add(step.Item1, vals);
366          }
367          vals.Add(step.Item2);
368        }
369      }
370
371      List<Series> series = new List<Series>();
372      var minMaxSeries = new Series();
373      var minMaxSeriesInfo = new SeriesInfo(Color.FromArgb(25, color), SeriesTypes.MinMax);
374      minMaxSeries.ChartType = SeriesChartType.Range;
375      minMaxSeries.Color = minMaxCheckBox.Checked ? minMaxSeriesInfo.Color : Color.Transparent;
376      minMaxSeries.IsVisibleInLegend = false;
377      minMaxSeries.Tag = minMaxSeriesInfo;
378      series.Add(minMaxSeries);
379
380      var quartilesSeries = new Series();
381      var quartilesSeriesInfo = new SeriesInfo(Color.FromArgb(50, color), SeriesTypes.Quartiles);
382      quartilesSeries.ChartType = SeriesChartType.Range;
383      quartilesSeries.Color = quartilesCheckBox.Checked ? quartilesSeriesInfo.Color : Color.Transparent;
384      quartilesSeries.IsVisibleInLegend = false;
385      quartilesSeries.Tag = quartilesSeriesInfo;
386      series.Add(quartilesSeries);
387
388      var medianSeries = new Series();
389      var medianSeriesInfo = new SeriesInfo(color, SeriesTypes.Median);
390      medianSeries.ChartType = SeriesChartType.FastLine;
391      medianSeries.Color = medianCheckBox.Checked ? medianSeriesInfo.Color : Color.Transparent;
392      medianSeries.BorderWidth = 3;
393      medianSeries.BorderDashStyle = ChartDashStyle.Solid;
394      medianSeries.IsVisibleInLegend = false;
395      medianSeries.Tag = medianSeriesInfo;
396      series.Add(medianSeries);
397
398      var averageSeries = new Series();
399      var averageSeriesInfo = new SeriesInfo(color, SeriesTypes.Average);
400      averageSeries.ChartType = SeriesChartType.FastLine;
401      averageSeries.Color = averageCheckBox.Checked ? averageSeriesInfo.Color : Color.Transparent;
402      averageSeries.BorderWidth = 3;
403      averageSeries.BorderDashStyle = ChartDashStyle.Dash;
404      averageSeries.IsVisibleInLegend = false;
405      averageSeries.Tag = averageSeriesInfo;
406      series.Add(averageSeries);
407
408      foreach (var point in values.OrderBy(x => x.Key)) {
409        if (point.Value.Count > 0) {
410          minMaxSeries.Points.Add(new DataPoint(point.Key, new double[] { point.Value.Min(), point.Value.Max() }));
411          quartilesSeries.Points.Add(new DataPoint(point.Key, new double[] { point.Value.Quantile(0.25), point.Value.Quantile(0.75) }));
412          medianSeries.Points.Add(new DataPoint(point.Key, point.Value.Median()));
413          averageSeries.Points.Add(new DataPoint(point.Key, point.Value.Average()));
414        }
415      }
416      return series;
417    }
418
419    #region Control Events
420    #region stepSizeTextBox
421    private void stepSizeTextBox_KeyDown(object sender, KeyEventArgs e) {
422      if ((e.KeyCode == Keys.Enter) || (e.KeyCode == Keys.Return))
423        stepSizeLabel.Select();  // select label to validate data
424
425      if (e.KeyCode == Keys.Escape) {
426        stepSizeTextBox.Text = stepSize.ToString();
427        stepSizeLabel.Select();  // select label to validate data
428      }
429    }
430    private void stepSizeTextBox_Validating(object sender, CancelEventArgs e) {
431      int val;
432      if (!int.TryParse(stepSizeTextBox.Text, out val) || val <= 0) {
433        e.Cancel = true;
434        errorProvider.SetError(stepSizeTextBox, "Invalid Value (Valid Value Format: \"[+]digits\")");
435        stepSizeTextBox.SelectAll();
436      }
437    }
438    private void stepSizeTextBox_Validated(object sender, EventArgs e) {
439      int val = int.Parse(stepSizeTextBox.Text);
440      errorProvider.SetError(stepSizeTextBox, string.Empty);
441      stepSizeTextBox.Text = val.ToString();
442      if (stepSize != val) {
443        stepSize = val;
444        CollectRunData();
445        UpdateChart();
446      }
447    }
448    #endregion
449    #region logScalingCheckBox
450    private void logScalingCheckBox_CheckedChanged(object sender, EventArgs e) {
451      chart.ChartAreas[0].AxisX.IsLogarithmic = logScalingCheckBox.Checked;
452    }
453    #endregion
454    #region chart
455    private void chart_MouseDown(object sender, MouseEventArgs e) {
456      HitTestResult result = chart.HitTest(e.X, e.Y);
457      if (result.ChartElementType == ChartElementType.LegendItem) {
458        var legendItemInfo = (result.Object as LegendItem).Tag as LegendItemInfo;
459        legendItemInfo.SeriesVisible = !legendItemInfo.SeriesVisible;
460        UpdateSeriesVisibility();
461      }
462    }
463    private void chart_MouseMove(object sender, MouseEventArgs e) {
464      HitTestResult result = chart.HitTest(e.X, e.Y);
465      switch (result.ChartElementType) {
466        case ChartElementType.LegendItem:
467          Cursor = Cursors.Hand;
468          break;
469        default:
470          Cursor = Cursors.Default;
471          break;
472      }
473    }
474    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
475      foreach (LegendItem legendItem in e.LegendItems) {
476        var legendItemInfo = legendItem.Tag as LegendItemInfo;
477        legendItem.Color = legendItemInfo.SeriesVisible ? legendItemInfo.Color : Color.Transparent;
478        foreach (LegendCell cell in legendItem.Cells) {
479          cell.ForeColor = legendItemInfo.SeriesVisible ? Color.Black : Color.Gray;
480        }
481      }
482    }
483    #endregion
484    #region parametersTreeView
485    private void parametersTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
486      addGroupButton.Enabled = (parametersTreeView.SelectedNode != null) && (parametersTreeView.SelectedNode.Parent != null);
487    }
488    private void parametersTreeView_AfterCheck(object sender, TreeViewEventArgs e) {
489      UpdateChart();
490    }
491    #endregion
492    #region groupsTreeView
493    private void groupsTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
494      removeGroupButton.Enabled = (groupsTreeView.SelectedNode != null) && (groupsTreeView.SelectedNode.Parent != null);
495    }
496    private void groupsTreeView_AfterCheck(object sender, TreeViewEventArgs e) {
497      UpdateChart();
498    }
499    #endregion
500    #region addGroupButton, removeGroupButton
501    private void addGroupButton_Click(object sender, EventArgs e) {
502      var group = groupsTreeView.SelectedNode;
503      var param = parametersTreeView.SelectedNode;
504      var groupRuns = group.Parent == null ? group.Tag as IEnumerable<IRun> : (group.Tag as GroupInfo).Runs;
505
506      if (param.Tag is ParameterInfo) {
507        var paramInfo = param.Tag as ParameterInfo;
508        var paramNode = new TreeNode();
509        foreach (var valueInfo in paramInfo.Values.Values) {
510          var valueRuns = groupRuns.Intersect(valueInfo.Runs);
511          var valueNode = new TreeNode(valueInfo.Value + " (" + valueRuns.Count() + ")");
512          valueNode.Tag = new GroupInfo(valueInfo.Value, valueInfo.Color, valueRuns, false);
513          paramNode.Nodes.Add(valueNode);
514        }
515        var paramRuns = groupRuns.Intersect(paramInfo.Runs);
516        paramNode.Text = paramInfo.Name + " (" + paramRuns.Count() + ")";
517        paramNode.Tag = new GroupInfo(paramInfo.Name, Color.Empty, paramRuns, true);
518        group.Nodes.Add(paramNode);
519      } else if (param.Tag is ParameterValueInfo) {
520        var paramInfo = param.Parent.Tag as ParameterInfo;
521        var valueInfo = param.Tag as ParameterValueInfo;
522        var valueRuns = groupRuns.Intersect(valueInfo.Runs);
523        var paramRuns = groupRuns.Intersect(paramInfo.Runs);
524        var paramNode = new TreeNode(paramInfo.Name + " (" + paramRuns.Count() + ")");
525        var valueNode = new TreeNode(valueInfo.Value + " (" + valueRuns.Count() + ")");
526        valueNode.Tag = new GroupInfo(valueInfo.Value, valueInfo.Color, valueRuns, false);
527        paramNode.Tag = new GroupInfo(paramInfo.Name, Color.Empty, paramRuns, true);
528        paramNode.Nodes.Add(valueNode);
529        group.Nodes.Add(paramNode);
530      }
531      groupsTreeView.Nodes[0].Expand();
532    }
533    private void removeGroupButton_Click(object sender, EventArgs e) {
534      if (groupsTreeView.SelectedNode != null)
535        groupsTreeView.SelectedNode.Remove();
536      UpdateChart();
537    }
538    #endregion
539    #region minMaxCheckBox, quartilesCheckBox, averageCheckBox, medianCheckBox
540    private void dataRowCheckBox_CheckedChanged(object sender, EventArgs e) {
541      UpdateSeriesVisibility();
542    }
543    #endregion
544    #endregion
545
546    #region Helpers
547    private IEnumerable<TreeNode> TraverseTreeNodes(TreeNodeCollection nodes) {
548      foreach (var node in nodes.OfType<TreeNode>()) {
549        yield return node;
550        foreach (var child in TraverseTreeNodes(node.Nodes))
551          yield return child;
552      }
553    }
554    #endregion
555
556    #region Inner Types
557    class ParameterInfo {
558      public string Name { get; private set; }
559      public Dictionary<string, ParameterValueInfo> Values { get; private set; }
560      public IEnumerable<IRun> Runs {
561        get { return Values.Values.SelectMany(x => x.Runs); }
562      }
563      public int RunCount {
564        get { return Values.Values.Select(x => x.RunCount).Sum(); }
565      }
566
567      public ParameterInfo(string name, string value, IRun run) {
568        Name = name;
569        Values = new Dictionary<string, ParameterValueInfo>();
570        AddValue(value, run);
571      }
572      public void AddValue(string value, IRun run) {
573        ParameterValueInfo valueInfo;
574        if (!Values.TryGetValue(value, out valueInfo)) {
575          Values.Add(value, new ParameterValueInfo(value, run));
576        } else {
577          valueInfo.Runs.Add(run);
578        }
579      }
580    }
581    class ParameterValueInfo {
582      public string Value { get; private set; }
583      public Color Color { get; set; }
584      public IList<IRun> Runs { get; private set; }
585      public int RunCount {
586        get { return Runs.Count; }
587      }
588
589      public ParameterValueInfo(string value, IRun run) {
590        Value = value;
591        Runs = new List<IRun>();
592        Runs.Add(run);
593      }
594      public ParameterValueInfo(string value, Color color, IRun run) : this(value, run) {
595        Color = color;
596      }
597    }
598    class GroupInfo {
599      public string Text { get; private set; }
600      public Color Color { get; private set; }
601      public IList<IRun> Runs { get; private set; }
602      public bool IsParameter { get; private set; }
603      public GroupInfo(string text, Color color, IEnumerable<IRun> runs, bool isParamter) {
604        Text = text;
605        Color = color;
606        Runs = runs.ToList();
607        IsParameter = isParamter;
608      }
609    }
610    enum SeriesTypes {
611      MinMax,
612      Quartiles,
613      Average,
614      Median
615    }
616    class SeriesInfo {
617      public Color Color { get; private set; }
618      public SeriesTypes Type { get; private set; }
619      public SeriesInfo(Color color, SeriesTypes type) {
620        Color = color;
621        Type = type;
622      }
623    }
624    class LegendItemInfo {
625      public Color Color { get; private set; }
626      public bool SeriesVisible { get; set; }
627      public IEnumerable<Series> Series { get; private set; }
628      public LegendItemInfo(Color color, IEnumerable<Series> series) {
629        Color = color;
630        SeriesVisible = true;
631        Series = series;
632      }
633    }
634    #endregion
635  }
636}
Note: See TracBrowser for help on using the repository browser.