source: trunk/sources/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionBubbleChartView.cs @ 8738

Last change on this file since 8738 was 8738, checked in by mkommend, 8 years ago

#1673: Added new property AlgorithmName to the RunCollection and synced the property with the name of the surrounding IOptimizer. The AlgorithmName is used by the RunCollectionViews as prefix for its caption if it was set.

File size: 29.3 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 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.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Data;
31using HeuristicLab.MainForm;
32using HeuristicLab.MainForm.WindowsForms;
33
34namespace HeuristicLab.Optimization.Views {
35  [View("RunCollection BubbleChart")]
36  [Content(typeof(RunCollection), false)]
37  public partial class RunCollectionBubbleChartView : AsynchronousContentView {
38    private enum SizeDimension { Constant = 0 }
39    private enum AxisDimension { Index = 0 }
40
41    private string xAxisValue;
42    private string yAxisValue;
43    private string sizeAxisValue;
44
45    private Dictionary<IRun, List<DataPoint>> runToDataPointMapping;
46    private Dictionary<int, Dictionary<object, double>> categoricalMapping;
47    private Dictionary<IRun, double> xJitter;
48    private Dictionary<IRun, double> yJitter;
49    private double xJitterFactor = 0.0;
50    private double yJitterFactor = 0.0;
51    private Random random;
52    private bool isSelecting = false;
53    private bool suppressUpdates = false;
54
55    public RunCollectionBubbleChartView() {
56      InitializeComponent();
57
58      chart.ContextMenuStrip.Items.Insert(0, hideRunToolStripMenuItem);
59      chart.ContextMenuStrip.Items.Insert(1, openBoxPlotViewToolStripMenuItem);
60      chart.ContextMenuStrip.Opening += new System.ComponentModel.CancelEventHandler(ContextMenuStrip_Opening);
61
62      runToDataPointMapping = new Dictionary<IRun, List<DataPoint>>();
63      categoricalMapping = new Dictionary<int, Dictionary<object, double>>();
64      xJitter = new Dictionary<IRun, double>();
65      yJitter = new Dictionary<IRun, double>();
66      random = new Random();
67
68      colorDialog.Color = Color.Black;
69      colorButton.Image = this.GenerateImage(16, 16, this.colorDialog.Color);
70      isSelecting = false;
71
72      chart.CustomizeAllChartAreas();
73      chart.ChartAreas[0].CursorX.Interval = 1;
74      chart.ChartAreas[0].CursorY.Interval = 1;
75      chart.ChartAreas[0].AxisX.ScaleView.Zoomable = !this.isSelecting;
76      chart.ChartAreas[0].AxisY.ScaleView.Zoomable = !this.isSelecting;
77    }
78
79    public new RunCollection Content {
80      get { return (RunCollection)base.Content; }
81      set { base.Content = value; }
82    }
83    public IStringConvertibleMatrix Matrix {
84      get { return this.Content; }
85    }
86
87    protected override void RegisterContentEvents() {
88      base.RegisterContentEvents();
89      Content.Reset += new EventHandler(Content_Reset);
90      Content.ColumnNamesChanged += new EventHandler(Content_ColumnNamesChanged);
91      Content.ItemsAdded += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
92      Content.ItemsRemoved += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
93      Content.CollectionReset += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
94      Content.UpdateOfRunsInProgressChanged += new EventHandler(Content_UpdateOfRunsInProgressChanged);
95      Content.AlgorithmNameChanged += new EventHandler(Content_AlgorithmNameChanged);
96      RegisterRunEvents(Content);
97    }
98    protected override void DeregisterContentEvents() {
99      base.DeregisterContentEvents();
100      Content.Reset -= new EventHandler(Content_Reset);
101      Content.ColumnNamesChanged -= new EventHandler(Content_ColumnNamesChanged);
102      Content.ItemsAdded -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
103      Content.ItemsRemoved -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
104      Content.CollectionReset -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
105      Content.UpdateOfRunsInProgressChanged -= new EventHandler(Content_UpdateOfRunsInProgressChanged);
106      Content.AlgorithmNameChanged -= new EventHandler(Content_AlgorithmNameChanged);
107      DeregisterRunEvents(Content);
108    }
109    protected virtual void RegisterRunEvents(IEnumerable<IRun> runs) {
110      foreach (IRun run in runs)
111        run.Changed += new EventHandler(run_Changed);
112    }
113    protected virtual void DeregisterRunEvents(IEnumerable<IRun> runs) {
114      foreach (IRun run in runs)
115        run.Changed -= new EventHandler(run_Changed);
116    }
117
118    private void Content_CollectionReset(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
119      DeregisterRunEvents(e.OldItems);
120      RegisterRunEvents(e.Items);
121    }
122    private void Content_ItemsRemoved(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
123      DeregisterRunEvents(e.Items);
124    }
125    private void Content_ItemsAdded(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
126      RegisterRunEvents(e.Items);
127    }
128    private void run_Changed(object sender, EventArgs e) {
129      if (InvokeRequired)
130        this.Invoke(new EventHandler(run_Changed), sender, e);
131      else {
132        IRun run = (IRun)sender;
133        UpdateRun(run);
134      }
135    }
136
137    private void UpdateRun(IRun run) {
138      if (!suppressUpdates) {
139        if (runToDataPointMapping.ContainsKey(run)) {
140          foreach (DataPoint point in runToDataPointMapping[run]) {
141            point.Color = run.Color;
142            if (!run.Visible) {
143              this.chart.Series[0].Points.Remove(point);
144              UpdateCursorInterval();
145              chart.ChartAreas[0].RecalculateAxesScale();
146            }
147          }
148          if (!run.Visible) runToDataPointMapping.Remove(run);
149        } else {
150          AddDataPoint(run);
151          UpdateCursorInterval();
152          chart.ChartAreas[0].RecalculateAxesScale();
153        }
154
155        if (this.chart.Series[0].Points.Count == 0)
156          noRunsLabel.Visible = true;
157        else
158          noRunsLabel.Visible = false;
159      }
160    }
161
162    protected override void OnContentChanged() {
163      base.OnContentChanged();
164      this.categoricalMapping.Clear();
165      UpdateComboBoxes();
166      UpdateDataPoints();
167      UpdateCaption();
168    }
169    private void Content_ColumnNamesChanged(object sender, EventArgs e) {
170      if (InvokeRequired)
171        Invoke(new EventHandler(Content_ColumnNamesChanged), sender, e);
172      else
173        UpdateComboBoxes();
174    }
175
176    private void UpdateCaption() {
177      Caption = Content != null ? Content.AlgorithmName + "Bubble Chart" : ViewAttribute.GetViewName(GetType());
178    }
179
180    private void UpdateComboBoxes() {
181      string selectedXAxis = (string)this.xAxisComboBox.SelectedItem;
182      string selectedYAxis = (string)this.yAxisComboBox.SelectedItem;
183      string selectedSizeAxis = (string)this.sizeComboBox.SelectedItem;
184      this.xAxisComboBox.Items.Clear();
185      this.yAxisComboBox.Items.Clear();
186      this.sizeComboBox.Items.Clear();
187      if (Content != null) {
188        string[] additionalAxisDimension = Enum.GetNames(typeof(AxisDimension));
189        this.xAxisComboBox.Items.AddRange(additionalAxisDimension);
190        this.xAxisComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
191        this.yAxisComboBox.Items.AddRange(additionalAxisDimension);
192        this.yAxisComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
193        string[] additionalSizeDimension = Enum.GetNames(typeof(SizeDimension));
194        this.sizeComboBox.Items.AddRange(additionalSizeDimension);
195        this.sizeComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
196        this.sizeComboBox.SelectedItem = SizeDimension.Constant.ToString();
197
198        bool changed = false;
199        if (selectedXAxis != null && xAxisComboBox.Items.Contains(selectedXAxis)) {
200          xAxisComboBox.SelectedItem = selectedXAxis;
201          changed = true;
202        }
203        if (selectedYAxis != null && yAxisComboBox.Items.Contains(selectedYAxis)) {
204          yAxisComboBox.SelectedItem = selectedYAxis;
205          changed = true;
206        }
207        if (selectedSizeAxis != null && sizeComboBox.Items.Contains(selectedSizeAxis)) {
208          sizeComboBox.SelectedItem = selectedSizeAxis;
209          changed = true;
210        }
211        if (changed)
212          UpdateDataPoints();
213      }
214    }
215
216
217    private void Content_UpdateOfRunsInProgressChanged(object sender, EventArgs e) {
218      if (InvokeRequired)
219        Invoke(new EventHandler(Content_UpdateOfRunsInProgressChanged), sender, e);
220      else {
221        suppressUpdates = Content.UpdateOfRunsInProgress;
222        if (!suppressUpdates) UpdateDataPoints();
223      }
224    }
225
226    private void Content_AlgorithmNameChanged(object sender, EventArgs e) {
227      if (InvokeRequired)
228        Invoke(new EventHandler(Content_AlgorithmNameChanged), sender, e);
229      else UpdateCaption();
230    }
231
232    private void Content_Reset(object sender, EventArgs e) {
233      if (InvokeRequired)
234        Invoke(new EventHandler(Content_Reset), sender, e);
235      else {
236        this.categoricalMapping.Clear();
237        UpdateDataPoints();
238      }
239    }
240
241    private void UpdateDataPoints() {
242      Series series = this.chart.Series[0];
243      series.Points.Clear();
244      runToDataPointMapping.Clear();
245
246      chart.ChartAreas[0].AxisX.IsMarginVisible = xAxisValue != AxisDimension.Index.ToString();
247      chart.ChartAreas[0].AxisY.IsMarginVisible = yAxisValue != AxisDimension.Index.ToString();
248
249      if (Content != null) {
250        foreach (IRun run in this.Content)
251          this.AddDataPoint(run);
252
253        if (this.chart.Series[0].Points.Count == 0)
254          noRunsLabel.Visible = true;
255        else {
256          noRunsLabel.Visible = false;
257          UpdateMarkerSizes();
258          UpdateCursorInterval();
259        }
260      }
261      var xAxis = chart.ChartAreas[0].AxisX;
262      var yAxis = chart.ChartAreas[0].AxisY;
263      xTrackBar.Value = 0;
264      yTrackBar.Value = 0;
265      SetAutomaticUpdateOfAxis(xAxis, true);
266      SetAutomaticUpdateOfAxis(yAxis, true);
267    }
268
269    private void UpdateMarkerSizes() {
270      double[] sizeValues = this.chart.Series[0].Points.Select(p => p.YValues[1]).ToArray();
271      double minSizeValue = sizeValues.Min();
272      double maxSizeValue = sizeValues.Max();
273
274      for (int i = 0; i < sizeValues.Length; i++) {
275        DataPoint point = this.chart.Series[0].Points[i];
276        double sizeRange = maxSizeValue - minSizeValue;
277        double relativeSize = (point.YValues[1] - minSizeValue);
278
279        if (sizeRange > double.Epsilon) relativeSize /= sizeRange;
280        else relativeSize = 1;
281
282        point.MarkerSize = (int)Math.Round((sizeTrackBar.Value - sizeTrackBar.Minimum) * relativeSize + sizeTrackBar.Minimum);
283      }
284    }
285
286    private void UpdateDataPointJitter() {
287      var xAxis = this.chart.ChartAreas[0].AxisX;
288      var yAxis = this.chart.ChartAreas[0].AxisY;
289      SetAutomaticUpdateOfAxis(xAxis, false);
290      SetAutomaticUpdateOfAxis(yAxis, false);
291
292      foreach (DataPoint point in chart.Series[0].Points) {
293        IRun run = (IRun)point.Tag;
294        double xValue = GetValue(run, xAxisValue).Value;
295        double yValue = GetValue(run, yAxisValue).Value;
296
297        if (!xJitterFactor.IsAlmost(0.0))
298          xValue += 0.1 * GetXJitter(run) * xJitterFactor * (xAxis.Maximum - xAxis.Minimum);
299        if (!yJitterFactor.IsAlmost(0.0))
300          yValue += 0.1 * GetYJitter(run) * yJitterFactor * (yAxis.Maximum - yAxis.Minimum);
301
302        point.XValue = xValue;
303        point.YValues[0] = yValue;
304      }
305    }
306
307    private void SetAutomaticUpdateOfAxis(Axis axis, bool enabled) {
308      if (enabled) {
309        axis.Maximum = double.NaN;
310        axis.Minimum = double.NaN;
311        axis.MajorGrid.Interval = double.NaN;
312        axis.MajorTickMark.Interval = double.NaN;
313        axis.LabelStyle.Interval = double.NaN;
314      } else {
315        axis.Minimum = axis.Minimum;
316        axis.Maximum = axis.Maximum;
317        axis.MajorGrid.Interval = axis.MajorGrid.Interval;
318        axis.MajorTickMark.Interval = axis.MajorTickMark.Interval;
319        axis.LabelStyle.Interval = axis.LabelStyle.Interval;
320      }
321
322    }
323
324    private void AddDataPoint(IRun run) {
325      double? xValue;
326      double? yValue;
327      double? sizeValue;
328      Series series = this.chart.Series[0];
329
330      xValue = GetValue(run, xAxisValue);
331      yValue = GetValue(run, yAxisValue);
332      sizeValue = GetValue(run, sizeAxisValue);
333
334      if (xValue.HasValue && yValue.HasValue && sizeValue.HasValue) {
335        xValue = xValue.Value;
336
337        yValue = yValue.Value;
338
339        if (run.Visible) {
340          DataPoint point = new DataPoint(xValue.Value, new double[] { yValue.Value, sizeValue.Value });
341          point.Tag = run;
342          point.Color = run.Color;
343          series.Points.Add(point);
344          if (!runToDataPointMapping.ContainsKey(run)) runToDataPointMapping.Add(run, new List<DataPoint>());
345          runToDataPointMapping[run].Add(point);
346        }
347      }
348    }
349    private double? GetValue(IRun run, string columnName) {
350      if (run == null || string.IsNullOrEmpty(columnName))
351        return null;
352
353      if (Enum.IsDefined(typeof(AxisDimension), columnName)) {
354        AxisDimension axisDimension = (AxisDimension)Enum.Parse(typeof(AxisDimension), columnName);
355        return GetValue(run, axisDimension);
356      } else if (Enum.IsDefined(typeof(SizeDimension), columnName)) {
357        SizeDimension sizeDimension = (SizeDimension)Enum.Parse(typeof(SizeDimension), columnName);
358        return GetValue(run, sizeDimension);
359      } else {
360        int columnIndex = Matrix.ColumnNames.ToList().IndexOf(columnName);
361        IItem value = Content.GetValue(run, columnIndex);
362        if (value == null)
363          return null;
364
365        DoubleValue doubleValue = value as DoubleValue;
366        IntValue intValue = value as IntValue;
367        TimeSpanValue timeSpanValue = value as TimeSpanValue;
368        double? ret = null;
369        if (doubleValue != null) {
370          if (!double.IsNaN(doubleValue.Value) && !double.IsInfinity(doubleValue.Value))
371            ret = doubleValue.Value;
372        } else if (intValue != null)
373          ret = intValue.Value;
374        else if (timeSpanValue != null) {
375          ret = timeSpanValue.Value.TotalSeconds;
376        } else
377          ret = GetCategoricalValue(columnIndex, value.ToString());
378
379        return ret;
380      }
381    }
382    private double GetCategoricalValue(int dimension, string value) {
383      if (!this.categoricalMapping.ContainsKey(dimension))
384        this.categoricalMapping[dimension] = new Dictionary<object, double>();
385      if (!this.categoricalMapping[dimension].ContainsKey(value)) {
386        if (this.categoricalMapping[dimension].Values.Count == 0)
387          this.categoricalMapping[dimension][value] = 1.0;
388        else
389          this.categoricalMapping[dimension][value] = this.categoricalMapping[dimension].Values.Max() + 1.0;
390      }
391      return this.categoricalMapping[dimension][value];
392    }
393    private double GetValue(IRun run, AxisDimension axisDimension) {
394      double value = double.NaN;
395      switch (axisDimension) {
396        case AxisDimension.Index: {
397            value = Content.ToList().IndexOf(run);
398            break;
399          }
400        default: {
401            throw new ArgumentException("No handling strategy for " + axisDimension.ToString() + " is defined.");
402          }
403      }
404      return value;
405    }
406    private double GetValue(IRun run, SizeDimension sizeDimension) {
407      double value = double.NaN;
408      switch (sizeDimension) {
409        case SizeDimension.Constant: {
410            value = 2;
411            break;
412          }
413        default: {
414            throw new ArgumentException("No handling strategy for " + sizeDimension.ToString() + " is defined.");
415          }
416      }
417      return value;
418    }
419    private void UpdateCursorInterval() {
420      Series series = chart.Series[0];
421      double[] xValues = (from point in series.Points
422                          where !point.IsEmpty
423                          select point.XValue)
424                    .DefaultIfEmpty(1.0)
425                    .ToArray();
426      double[] yValues = (from point in series.Points
427                          where !point.IsEmpty
428                          select point.YValues[0])
429                    .DefaultIfEmpty(1.0)
430                    .ToArray();
431
432      double xRange = xValues.Max() - xValues.Min();
433      double yRange = yValues.Max() - yValues.Min();
434      if (xRange.IsAlmost(0.0)) xRange = 1.0;
435      if (yRange.IsAlmost(0.0)) yRange = 1.0;
436      double xDigits = (int)Math.Log10(xRange) - 3;
437      double yDigits = (int)Math.Log10(yRange) - 3;
438      double xZoomInterval = Math.Pow(10, xDigits);
439      double yZoomInterval = Math.Pow(10, yDigits);
440      this.chart.ChartAreas[0].CursorX.Interval = xZoomInterval;
441      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
442
443      //code to handle TimeSpanValues correct
444      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
445      int columnIndex = xAxisComboBox.SelectedIndex - axisDimensionCount;
446      if (columnIndex >= 0 && Content.GetValue(0, columnIndex) is TimeSpanValue)
447        this.chart.ChartAreas[0].CursorX.Interval = 1;
448      columnIndex = yAxisComboBox.SelectedIndex - axisDimensionCount;
449      if (columnIndex >= 0 && Content.GetValue(0, columnIndex) is TimeSpanValue)
450        this.chart.ChartAreas[0].CursorY.Interval = 1;
451    }
452
453    #region Drag & drop and tooltip
454    private void chart_MouseDoubleClick(object sender, MouseEventArgs e) {
455      HitTestResult h = this.chart.HitTest(e.X, e.Y, ChartElementType.DataPoint);
456      if (h.ChartElementType == ChartElementType.DataPoint) {
457        IRun run = (IRun)((DataPoint)h.Object).Tag;
458        IContentView view = MainFormManager.MainForm.ShowContent(run);
459        if (view != null) {
460          view.ReadOnly = this.ReadOnly;
461          view.Locked = this.Locked;
462        }
463
464        this.chart.ChartAreas[0].CursorX.SelectionStart = this.chart.ChartAreas[0].CursorX.SelectionEnd;
465        this.chart.ChartAreas[0].CursorY.SelectionStart = this.chart.ChartAreas[0].CursorY.SelectionEnd;
466      }
467      UpdateAxisLabels();
468    }
469
470    private void chart_MouseUp(object sender, MouseEventArgs e) {
471      if (isSelecting) {
472        System.Windows.Forms.DataVisualization.Charting.Cursor xCursor = chart.ChartAreas[0].CursorX;
473        System.Windows.Forms.DataVisualization.Charting.Cursor yCursor = chart.ChartAreas[0].CursorY;
474
475        double minX = Math.Min(xCursor.SelectionStart, xCursor.SelectionEnd);
476        double maxX = Math.Max(xCursor.SelectionStart, xCursor.SelectionEnd);
477        double minY = Math.Min(yCursor.SelectionStart, yCursor.SelectionEnd);
478        double maxY = Math.Max(yCursor.SelectionStart, yCursor.SelectionEnd);
479
480        //check for click to select model
481        if (minX == maxX && minY == maxY) {
482          HitTestResult hitTest = chart.HitTest(e.X, e.Y);
483          if (hitTest.ChartElementType == ChartElementType.DataPoint) {
484            int pointIndex = hitTest.PointIndex;
485            IRun run = (IRun)this.chart.Series[0].Points[pointIndex].Tag;
486            run.Color = colorDialog.Color;
487          }
488        } else {
489          List<DataPoint> selectedPoints = new List<DataPoint>();
490          foreach (DataPoint p in this.chart.Series[0].Points) {
491            if (p.XValue >= minX && p.XValue < maxX &&
492              p.YValues[0] >= minY && p.YValues[0] < maxY) {
493              selectedPoints.Add(p);
494            }
495          }
496          foreach (DataPoint p in selectedPoints) {
497            IRun run = (IRun)p.Tag;
498            run.Color = colorDialog.Color;
499          }
500        }
501        this.chart.ChartAreas[0].CursorX.SelectionStart = this.chart.ChartAreas[0].CursorX.SelectionEnd;
502        this.chart.ChartAreas[0].CursorY.SelectionStart = this.chart.ChartAreas[0].CursorY.SelectionEnd;
503      }
504    }
505
506    private void chart_MouseMove(object sender, MouseEventArgs e) {
507      HitTestResult h = this.chart.HitTest(e.X, e.Y);
508      string newTooltipText = string.Empty;
509      string oldTooltipText;
510      if (h.ChartElementType == ChartElementType.DataPoint) {
511        IRun run = (IRun)((DataPoint)h.Object).Tag;
512        newTooltipText = BuildTooltip(run);
513      } else if (h.ChartElementType == ChartElementType.AxisLabels) {
514        newTooltipText = ((CustomLabel)h.Object).ToolTip;
515      }
516
517      oldTooltipText = this.tooltip.GetToolTip(chart);
518      if (newTooltipText != oldTooltipText)
519        this.tooltip.SetToolTip(chart, newTooltipText);
520    }
521
522    private string BuildTooltip(IRun run) {
523      string tooltip;
524      tooltip = run.Name + System.Environment.NewLine;
525
526      double? xValue = this.GetValue(run, (string)xAxisComboBox.SelectedItem);
527      double? yValue = this.GetValue(run, (string)yAxisComboBox.SelectedItem);
528      double? sizeValue = this.GetValue(run, (string)sizeComboBox.SelectedItem);
529
530      string xString = xValue == null ? string.Empty : xValue.Value.ToString();
531      string yString = yValue == null ? string.Empty : yValue.Value.ToString();
532      string sizeString = sizeValue == null ? string.Empty : sizeValue.Value.ToString();
533
534      //code to handle TimeSpanValues correct
535      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
536      int columnIndex = xAxisComboBox.SelectedIndex - axisDimensionCount;
537      if (xValue.HasValue && columnIndex > 0 && Content.GetValue(0, columnIndex) is TimeSpanValue) {
538        TimeSpan time = TimeSpan.FromSeconds(xValue.Value);
539        xString = string.Format("{0:00}:{1:00}:{2:00.00}", (int)time.TotalHours, time.Minutes, time.Seconds);
540      }
541      columnIndex = yAxisComboBox.SelectedIndex - axisDimensionCount;
542      if (yValue.HasValue && columnIndex > 0 && Content.GetValue(0, columnIndex) is TimeSpanValue) {
543        TimeSpan time = TimeSpan.FromSeconds(yValue.Value);
544        yString = string.Format("{0:00}:{1:00}:{2:00.00}", (int)time.TotalHours, time.Minutes, time.Seconds);
545      }
546
547      tooltip += xAxisComboBox.SelectedItem + " : " + xString + Environment.NewLine;
548      tooltip += yAxisComboBox.SelectedItem + " : " + yString + Environment.NewLine;
549      tooltip += sizeComboBox.SelectedItem + " : " + sizeString + Environment.NewLine;
550
551      return tooltip;
552    }
553    #endregion
554
555    #region GUI events and updating
556    private double GetXJitter(IRun run) {
557      if (!this.xJitter.ContainsKey(run))
558        this.xJitter[run] = random.NextDouble() * 2.0 - 1.0;
559      return this.xJitter[run];
560    }
561    private double GetYJitter(IRun run) {
562      if (!this.yJitter.ContainsKey(run))
563        this.yJitter[run] = random.NextDouble() * 2.0 - 1.0;
564      return this.yJitter[run];
565    }
566    private void jitterTrackBar_ValueChanged(object sender, EventArgs e) {
567      this.xJitterFactor = xTrackBar.Value / 100.0;
568      this.yJitterFactor = yTrackBar.Value / 100.0;
569      UpdateDataPointJitter();
570    }
571    private void sizeTrackBar_ValueChanged(object sender, EventArgs e) {
572      UpdateMarkerSizes();
573    }
574
575    private void AxisComboBox_SelectedValueChanged(object sender, EventArgs e) {
576      bool axisSelected = xAxisComboBox.SelectedIndex != -1 && yAxisComboBox.SelectedIndex != -1;
577      xTrackBar.Enabled = yTrackBar.Enabled = axisSelected;
578      colorXAxisButton.Enabled = colorYAxisButton.Enabled = axisSelected;
579
580      xAxisValue = (string)xAxisComboBox.SelectedItem;
581      yAxisValue = (string)yAxisComboBox.SelectedItem;
582      sizeAxisValue = (string)sizeComboBox.SelectedItem;
583
584      UpdateDataPoints();
585      UpdateAxisLabels();
586    }
587    private void UpdateAxisLabels() {
588      Axis xAxis = this.chart.ChartAreas[0].AxisX;
589      Axis yAxis = this.chart.ChartAreas[0].AxisY;
590      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
591      SetCustomAxisLabels(xAxis, xAxisComboBox.SelectedIndex - axisDimensionCount);
592      SetCustomAxisLabels(yAxis, yAxisComboBox.SelectedIndex - axisDimensionCount);
593      if (xAxisComboBox.SelectedItem != null)
594        xAxis.Title = xAxisComboBox.SelectedItem.ToString();
595      if (yAxisComboBox.SelectedItem != null)
596        yAxis.Title = yAxisComboBox.SelectedItem.ToString();
597    }
598
599    private void chart_AxisViewChanged(object sender, System.Windows.Forms.DataVisualization.Charting.ViewEventArgs e) {
600      this.UpdateAxisLabels();
601    }
602
603    private void SetCustomAxisLabels(Axis axis, int dimension) {
604      axis.CustomLabels.Clear();
605      if (categoricalMapping.ContainsKey(dimension)) {
606        foreach (var pair in categoricalMapping[dimension]) {
607          string labelText = pair.Key.ToString();
608          CustomLabel label = new CustomLabel();
609          label.ToolTip = labelText;
610          if (labelText.Length > 25)
611            labelText = labelText.Substring(0, 25) + " ... ";
612          label.Text = labelText;
613          label.GridTicks = GridTickTypes.TickMark;
614          label.FromPosition = pair.Value - 0.5;
615          label.ToPosition = pair.Value + 0.5;
616          axis.CustomLabels.Add(label);
617        }
618      } else if (dimension > 0 && Content.GetValue(0, dimension) is TimeSpanValue) {
619        this.chart.ChartAreas[0].RecalculateAxesScale();
620        for (double i = axis.Minimum; i <= axis.Maximum; i += axis.LabelStyle.Interval) {
621          TimeSpan time = TimeSpan.FromSeconds(i);
622          string x = string.Format("{0:00}:{1:00}:{2:00}", time.Hours, time.Minutes, time.Seconds);
623          axis.CustomLabels.Add(i - axis.LabelStyle.Interval / 2, i + axis.LabelStyle.Interval / 2, x);
624        }
625      }
626    }
627
628    private void zoomButton_CheckedChanged(object sender, EventArgs e) {
629      this.isSelecting = selectButton.Checked;
630      this.colorButton.Enabled = this.isSelecting;
631      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = !isSelecting;
632      this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = !isSelecting;
633    }
634    private void colorButton_Click(object sender, EventArgs e) {
635      if (colorDialog.ShowDialog(this) == DialogResult.OK) {
636        this.colorButton.Image = this.GenerateImage(16, 16, this.colorDialog.Color);
637      }
638    }
639    private Image GenerateImage(int width, int height, Color fillColor) {
640      Image colorImage = new Bitmap(width, height);
641      using (Graphics gfx = Graphics.FromImage(colorImage)) {
642        using (SolidBrush brush = new SolidBrush(fillColor)) {
643          gfx.FillRectangle(brush, 0, 0, width, height);
644        }
645      }
646      return colorImage;
647    }
648
649    private IRun runToHide = null;
650    private void ContextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e) {
651      var pos = Control.MousePosition;
652      var chartPos = chart.PointToClient(pos);
653
654      HitTestResult h = this.chart.HitTest(chartPos.X, chartPos.Y);
655      if (h.ChartElementType == ChartElementType.DataPoint) {
656        runToHide = (IRun)((DataPoint)h.Object).Tag;
657        hideRunToolStripMenuItem.Visible = true;
658      } else {
659        runToHide = null;
660        hideRunToolStripMenuItem.Visible = false;
661      }
662
663    }
664    private void hideRunToolStripMenuItem_Click(object sender, EventArgs e) {
665      var constraint = Content.Constraints.OfType<RunCollectionContentConstraint>().Where(c => c.Active).FirstOrDefault();
666      if (constraint == null) {
667        constraint = new RunCollectionContentConstraint();
668        Content.Constraints.Add(constraint);
669        constraint.Active = true;
670      }
671      constraint.ConstraintData.Add(runToHide);
672    }
673
674    private void openBoxPlotViewToolStripMenuItem_Click(object sender, EventArgs e) {
675      RunCollectionBoxPlotView boxplotView = new RunCollectionBoxPlotView();
676      boxplotView.Content = this.Content;
677      boxplotView.xAxisComboBox.SelectedItem = xAxisComboBox.SelectedItem;
678      boxplotView.yAxisComboBox.SelectedItem = yAxisComboBox.SelectedItem;
679      boxplotView.Show();
680    }
681    #endregion
682
683    #region Automatic coloring
684    private void colorXAxisButton_Click(object sender, EventArgs e) {
685      ColorRuns(xAxisValue);
686    }
687
688    private void colorYAxisButton_Click(object sender, EventArgs e) {
689      ColorRuns(yAxisValue);
690    }
691
692    private void ColorRuns(string axisValue) {
693      var runs = Content.Where(r => r.Visible).Select(r => new { Run = r, Value = GetValue(r, axisValue) }).Where(r => r.Value.HasValue);
694      double minValue = runs.Min(r => r.Value.Value);
695      double maxValue = runs.Max(r => r.Value.Value);
696      double range = maxValue - minValue;
697
698      foreach (var r in runs) {
699        int colorIndex = 0;
700        if (!range.IsAlmost(0)) colorIndex = (int)((ColorGradient.Colors.Count - 1) * (r.Value.Value - minValue) / (range));
701        r.Run.Color = ColorGradient.Colors[colorIndex];
702      }
703    }
704    #endregion
705  }
706}
Note: See TracBrowser for help on using the repository browser.