Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 9120 was 9068, checked in by sforsten, 12 years ago

#1993:

  • added code again which was deleted in r8835 and r8832 (SetAutomaticUpdateOfAxis())
  • added call chart.Refresh() in UpdateDataPoints to make sure that everything is recalculated, also the axis. So the fields of the axis don't stay double.NaN and HitTest doesn't throw an exception.
File size: 29.7 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.OptimizerNameChanged += 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.OptimizerNameChanged -= 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.OptimizerName + " 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      xTrackBar.Value = 0;
262      yTrackBar.Value = 0;
263
264      //needed to set axis back to automatic and refresh them, otherwise their values may remain NaN
265      var xAxis = chart.ChartAreas[0].AxisX;
266      var yAxis = chart.ChartAreas[0].AxisY;
267      SetAutomaticUpdateOfAxis(xAxis, true);
268      SetAutomaticUpdateOfAxis(yAxis, true);
269      chart.Refresh();
270    }
271
272    private void UpdateMarkerSizes() {
273      double[] sizeValues = this.chart.Series[0].Points.Select(p => p.YValues[1]).ToArray();
274      double minSizeValue = sizeValues.Min();
275      double maxSizeValue = sizeValues.Max();
276
277      for (int i = 0; i < sizeValues.Length; i++) {
278        DataPoint point = this.chart.Series[0].Points[i];
279        double sizeRange = maxSizeValue - minSizeValue;
280        double relativeSize = (point.YValues[1] - minSizeValue);
281
282        if (sizeRange > double.Epsilon) relativeSize /= sizeRange;
283        else relativeSize = 1;
284
285        point.MarkerSize = (int)Math.Round((sizeTrackBar.Value - sizeTrackBar.Minimum) * relativeSize + sizeTrackBar.Minimum);
286      }
287    }
288
289    private void UpdateDataPointJitter() {
290      var xAxis = this.chart.ChartAreas[0].AxisX;
291      var yAxis = this.chart.ChartAreas[0].AxisY;
292
293      SetAutomaticUpdateOfAxis(xAxis, false);
294      SetAutomaticUpdateOfAxis(yAxis, false);
295
296      double xAxisRange = xAxis.Maximum - xAxis.Minimum;
297      double yAxisRange = yAxis.Maximum - yAxis.Minimum;
298
299      foreach (DataPoint point in chart.Series[0].Points) {
300        IRun run = (IRun)point.Tag;
301        double xValue = GetValue(run, xAxisValue).Value;
302        double yValue = GetValue(run, yAxisValue).Value;
303
304        if (!xJitterFactor.IsAlmost(0.0))
305          xValue += 0.1 * GetXJitter(run) * xJitterFactor * (xAxisRange);
306        if (!yJitterFactor.IsAlmost(0.0))
307          yValue += 0.1 * GetYJitter(run) * yJitterFactor * (yAxisRange);
308
309        point.XValue = xValue;
310        point.YValues[0] = yValue;
311      }
312
313    }
314
315    // sets an axis to automatic or restrains it to its current values
316    // this is used that none of the set values is changed when jitter is applied, so that the chart stays the same
317    private void SetAutomaticUpdateOfAxis(Axis axis, bool enabled) {
318      if (enabled) {
319        axis.Maximum = double.NaN;
320        axis.Minimum = double.NaN;
321        axis.MajorGrid.Interval = double.NaN;
322        axis.MajorTickMark.Interval = double.NaN;
323        axis.LabelStyle.Interval = double.NaN;
324      } else {
325        axis.Minimum = axis.Minimum;
326        axis.Maximum = axis.Maximum;
327        axis.MajorGrid.Interval = axis.MajorGrid.Interval;
328        axis.MajorTickMark.Interval = axis.MajorTickMark.Interval;
329        axis.LabelStyle.Interval = axis.LabelStyle.Interval;
330      }
331    }
332
333    private void AddDataPoint(IRun run) {
334      double? xValue;
335      double? yValue;
336      double? sizeValue;
337      Series series = this.chart.Series[0];
338
339      xValue = GetValue(run, xAxisValue);
340      yValue = GetValue(run, yAxisValue);
341      sizeValue = GetValue(run, sizeAxisValue);
342
343      if (xValue.HasValue && yValue.HasValue && sizeValue.HasValue) {
344        xValue = xValue.Value;
345
346        yValue = yValue.Value;
347
348        if (run.Visible) {
349          DataPoint point = new DataPoint(xValue.Value, new double[] { yValue.Value, sizeValue.Value });
350          point.Tag = run;
351          point.Color = run.Color;
352          series.Points.Add(point);
353          if (!runToDataPointMapping.ContainsKey(run)) runToDataPointMapping.Add(run, new List<DataPoint>());
354          runToDataPointMapping[run].Add(point);
355        }
356      }
357    }
358    private double? GetValue(IRun run, string columnName) {
359      if (run == null || string.IsNullOrEmpty(columnName))
360        return null;
361
362      if (Enum.IsDefined(typeof(AxisDimension), columnName)) {
363        AxisDimension axisDimension = (AxisDimension)Enum.Parse(typeof(AxisDimension), columnName);
364        return GetValue(run, axisDimension);
365      } else if (Enum.IsDefined(typeof(SizeDimension), columnName)) {
366        SizeDimension sizeDimension = (SizeDimension)Enum.Parse(typeof(SizeDimension), columnName);
367        return GetValue(run, sizeDimension);
368      } else {
369        int columnIndex = Matrix.ColumnNames.ToList().IndexOf(columnName);
370        IItem value = Content.GetValue(run, columnIndex);
371        if (value == null)
372          return null;
373
374        DoubleValue doubleValue = value as DoubleValue;
375        IntValue intValue = value as IntValue;
376        TimeSpanValue timeSpanValue = value as TimeSpanValue;
377        double? ret = null;
378        if (doubleValue != null) {
379          if (!double.IsNaN(doubleValue.Value) && !double.IsInfinity(doubleValue.Value))
380            ret = doubleValue.Value;
381        } else if (intValue != null)
382          ret = intValue.Value;
383        else if (timeSpanValue != null) {
384          ret = timeSpanValue.Value.TotalSeconds;
385        } else
386          ret = GetCategoricalValue(columnIndex, value.ToString());
387
388        return ret;
389      }
390    }
391    private double GetCategoricalValue(int dimension, string value) {
392      if (!this.categoricalMapping.ContainsKey(dimension))
393        this.categoricalMapping[dimension] = new Dictionary<object, double>();
394      if (!this.categoricalMapping[dimension].ContainsKey(value)) {
395        if (this.categoricalMapping[dimension].Values.Count == 0)
396          this.categoricalMapping[dimension][value] = 1.0;
397        else
398          this.categoricalMapping[dimension][value] = this.categoricalMapping[dimension].Values.Max() + 1.0;
399      }
400      return this.categoricalMapping[dimension][value];
401    }
402    private double GetValue(IRun run, AxisDimension axisDimension) {
403      double value = double.NaN;
404      switch (axisDimension) {
405        case AxisDimension.Index: {
406            value = Content.ToList().IndexOf(run);
407            break;
408          }
409        default: {
410            throw new ArgumentException("No handling strategy for " + axisDimension.ToString() + " is defined.");
411          }
412      }
413      return value;
414    }
415    private double GetValue(IRun run, SizeDimension sizeDimension) {
416      double value = double.NaN;
417      switch (sizeDimension) {
418        case SizeDimension.Constant: {
419            value = 2;
420            break;
421          }
422        default: {
423            throw new ArgumentException("No handling strategy for " + sizeDimension.ToString() + " is defined.");
424          }
425      }
426      return value;
427    }
428    private void UpdateCursorInterval() {
429      Series series = chart.Series[0];
430      double[] xValues = (from point in series.Points
431                          where !point.IsEmpty
432                          select point.XValue)
433                    .DefaultIfEmpty(1.0)
434                    .ToArray();
435      double[] yValues = (from point in series.Points
436                          where !point.IsEmpty
437                          select point.YValues[0])
438                    .DefaultIfEmpty(1.0)
439                    .ToArray();
440
441      double xRange = xValues.Max() - xValues.Min();
442      double yRange = yValues.Max() - yValues.Min();
443      if (xRange.IsAlmost(0.0)) xRange = 1.0;
444      if (yRange.IsAlmost(0.0)) yRange = 1.0;
445      double xDigits = (int)Math.Log10(xRange) - 3;
446      double yDigits = (int)Math.Log10(yRange) - 3;
447      double xZoomInterval = Math.Pow(10, xDigits);
448      double yZoomInterval = Math.Pow(10, yDigits);
449      this.chart.ChartAreas[0].CursorX.Interval = xZoomInterval;
450      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
451
452      //code to handle TimeSpanValues correct
453      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
454      int columnIndex = xAxisComboBox.SelectedIndex - axisDimensionCount;
455      if (columnIndex >= 0 && Content.GetValue(0, columnIndex) is TimeSpanValue)
456        this.chart.ChartAreas[0].CursorX.Interval = 1;
457      columnIndex = yAxisComboBox.SelectedIndex - axisDimensionCount;
458      if (columnIndex >= 0 && Content.GetValue(0, columnIndex) is TimeSpanValue)
459        this.chart.ChartAreas[0].CursorY.Interval = 1;
460    }
461
462    #region Drag & drop and tooltip
463    private void chart_MouseDoubleClick(object sender, MouseEventArgs e) {
464      HitTestResult h = this.chart.HitTest(e.X, e.Y, ChartElementType.DataPoint);
465      if (h.ChartElementType == ChartElementType.DataPoint) {
466        IRun run = (IRun)((DataPoint)h.Object).Tag;
467        IContentView view = MainFormManager.MainForm.ShowContent(run);
468        if (view != null) {
469          view.ReadOnly = this.ReadOnly;
470          view.Locked = this.Locked;
471        }
472
473        this.chart.ChartAreas[0].CursorX.SelectionStart = this.chart.ChartAreas[0].CursorX.SelectionEnd;
474        this.chart.ChartAreas[0].CursorY.SelectionStart = this.chart.ChartAreas[0].CursorY.SelectionEnd;
475      }
476      UpdateAxisLabels();
477    }
478
479    private void chart_MouseUp(object sender, MouseEventArgs e) {
480      if (isSelecting) {
481        System.Windows.Forms.DataVisualization.Charting.Cursor xCursor = chart.ChartAreas[0].CursorX;
482        System.Windows.Forms.DataVisualization.Charting.Cursor yCursor = chart.ChartAreas[0].CursorY;
483
484        double minX = Math.Min(xCursor.SelectionStart, xCursor.SelectionEnd);
485        double maxX = Math.Max(xCursor.SelectionStart, xCursor.SelectionEnd);
486        double minY = Math.Min(yCursor.SelectionStart, yCursor.SelectionEnd);
487        double maxY = Math.Max(yCursor.SelectionStart, yCursor.SelectionEnd);
488
489        //check for click to select model
490        if (minX == maxX && minY == maxY) {
491          HitTestResult hitTest = chart.HitTest(e.X, e.Y);
492          if (hitTest.ChartElementType == ChartElementType.DataPoint) {
493            int pointIndex = hitTest.PointIndex;
494            IRun run = (IRun)this.chart.Series[0].Points[pointIndex].Tag;
495            run.Color = colorDialog.Color;
496          }
497        } else {
498          List<DataPoint> selectedPoints = new List<DataPoint>();
499          foreach (DataPoint p in this.chart.Series[0].Points) {
500            if (p.XValue >= minX && p.XValue < maxX &&
501              p.YValues[0] >= minY && p.YValues[0] < maxY) {
502              selectedPoints.Add(p);
503            }
504          }
505          foreach (DataPoint p in selectedPoints) {
506            IRun run = (IRun)p.Tag;
507            run.Color = colorDialog.Color;
508          }
509        }
510        this.chart.ChartAreas[0].CursorX.SelectionStart = this.chart.ChartAreas[0].CursorX.SelectionEnd;
511        this.chart.ChartAreas[0].CursorY.SelectionStart = this.chart.ChartAreas[0].CursorY.SelectionEnd;
512      }
513    }
514
515    private void chart_MouseMove(object sender, MouseEventArgs e) {
516      HitTestResult h = this.chart.HitTest(e.X, e.Y);
517      string newTooltipText = string.Empty;
518      string oldTooltipText;
519      if (h.ChartElementType == ChartElementType.DataPoint) {
520        IRun run = (IRun)((DataPoint)h.Object).Tag;
521        newTooltipText = BuildTooltip(run);
522      } else if (h.ChartElementType == ChartElementType.AxisLabels) {
523        newTooltipText = ((CustomLabel)h.Object).ToolTip;
524      }
525
526      oldTooltipText = this.tooltip.GetToolTip(chart);
527      if (newTooltipText != oldTooltipText)
528        this.tooltip.SetToolTip(chart, newTooltipText);
529    }
530
531    private string BuildTooltip(IRun run) {
532      string tooltip;
533      tooltip = run.Name + System.Environment.NewLine;
534
535      double? xValue = this.GetValue(run, (string)xAxisComboBox.SelectedItem);
536      double? yValue = this.GetValue(run, (string)yAxisComboBox.SelectedItem);
537      double? sizeValue = this.GetValue(run, (string)sizeComboBox.SelectedItem);
538
539      string xString = xValue == null ? string.Empty : xValue.Value.ToString();
540      string yString = yValue == null ? string.Empty : yValue.Value.ToString();
541      string sizeString = sizeValue == null ? string.Empty : sizeValue.Value.ToString();
542
543      //code to handle TimeSpanValues correct
544      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
545      int columnIndex = xAxisComboBox.SelectedIndex - axisDimensionCount;
546      if (xValue.HasValue && columnIndex > 0 && Content.GetValue(0, columnIndex) is TimeSpanValue) {
547        TimeSpan time = TimeSpan.FromSeconds(xValue.Value);
548        xString = string.Format("{0:00}:{1:00}:{2:00.00}", (int)time.TotalHours, time.Minutes, time.Seconds);
549      }
550      columnIndex = yAxisComboBox.SelectedIndex - axisDimensionCount;
551      if (yValue.HasValue && columnIndex > 0 && Content.GetValue(0, columnIndex) is TimeSpanValue) {
552        TimeSpan time = TimeSpan.FromSeconds(yValue.Value);
553        yString = string.Format("{0:00}:{1:00}:{2:00.00}", (int)time.TotalHours, time.Minutes, time.Seconds);
554      }
555
556      tooltip += xAxisComboBox.SelectedItem + " : " + xString + Environment.NewLine;
557      tooltip += yAxisComboBox.SelectedItem + " : " + yString + Environment.NewLine;
558      tooltip += sizeComboBox.SelectedItem + " : " + sizeString + Environment.NewLine;
559
560      return tooltip;
561    }
562    #endregion
563
564    #region GUI events and updating
565    private double GetXJitter(IRun run) {
566      if (!this.xJitter.ContainsKey(run))
567        this.xJitter[run] = random.NextDouble() * 2.0 - 1.0;
568      return this.xJitter[run];
569    }
570    private double GetYJitter(IRun run) {
571      if (!this.yJitter.ContainsKey(run))
572        this.yJitter[run] = random.NextDouble() * 2.0 - 1.0;
573      return this.yJitter[run];
574    }
575    private void jitterTrackBar_ValueChanged(object sender, EventArgs e) {
576      this.xJitterFactor = xTrackBar.Value / 100.0;
577      this.yJitterFactor = yTrackBar.Value / 100.0;
578      UpdateDataPointJitter();
579    }
580    private void sizeTrackBar_ValueChanged(object sender, EventArgs e) {
581      UpdateMarkerSizes();
582    }
583
584    private void AxisComboBox_SelectedValueChanged(object sender, EventArgs e) {
585      bool axisSelected = xAxisComboBox.SelectedIndex != -1 && yAxisComboBox.SelectedIndex != -1;
586      xTrackBar.Enabled = yTrackBar.Enabled = axisSelected;
587      colorXAxisButton.Enabled = colorYAxisButton.Enabled = axisSelected;
588
589      xAxisValue = (string)xAxisComboBox.SelectedItem;
590      yAxisValue = (string)yAxisComboBox.SelectedItem;
591      sizeAxisValue = (string)sizeComboBox.SelectedItem;
592
593      UpdateDataPoints();
594      UpdateAxisLabels();
595    }
596    private void UpdateAxisLabels() {
597      Axis xAxis = this.chart.ChartAreas[0].AxisX;
598      Axis yAxis = this.chart.ChartAreas[0].AxisY;
599      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
600      SetCustomAxisLabels(xAxis, xAxisComboBox.SelectedIndex - axisDimensionCount);
601      SetCustomAxisLabels(yAxis, yAxisComboBox.SelectedIndex - axisDimensionCount);
602      if (xAxisComboBox.SelectedItem != null)
603        xAxis.Title = xAxisComboBox.SelectedItem.ToString();
604      if (yAxisComboBox.SelectedItem != null)
605        yAxis.Title = yAxisComboBox.SelectedItem.ToString();
606    }
607
608    private void chart_AxisViewChanged(object sender, System.Windows.Forms.DataVisualization.Charting.ViewEventArgs e) {
609      this.UpdateAxisLabels();
610    }
611
612    private void SetCustomAxisLabels(Axis axis, int dimension) {
613      axis.CustomLabels.Clear();
614      if (categoricalMapping.ContainsKey(dimension)) {
615        foreach (var pair in categoricalMapping[dimension]) {
616          string labelText = pair.Key.ToString();
617          CustomLabel label = new CustomLabel();
618          label.ToolTip = labelText;
619          if (labelText.Length > 25)
620            labelText = labelText.Substring(0, 25) + " ... ";
621          label.Text = labelText;
622          label.GridTicks = GridTickTypes.TickMark;
623          label.FromPosition = pair.Value - 0.5;
624          label.ToPosition = pair.Value + 0.5;
625          axis.CustomLabels.Add(label);
626        }
627      } else if (dimension > 0 && Content.GetValue(0, dimension) is TimeSpanValue) {
628        this.chart.ChartAreas[0].RecalculateAxesScale();
629        for (double i = axis.Minimum; i <= axis.Maximum; i += axis.LabelStyle.Interval) {
630          TimeSpan time = TimeSpan.FromSeconds(i);
631          string x = string.Format("{0:00}:{1:00}:{2:00}", time.Hours, time.Minutes, time.Seconds);
632          axis.CustomLabels.Add(i - axis.LabelStyle.Interval / 2, i + axis.LabelStyle.Interval / 2, x);
633        }
634      }
635    }
636
637    private void zoomButton_CheckedChanged(object sender, EventArgs e) {
638      this.isSelecting = selectButton.Checked;
639      this.colorButton.Enabled = this.isSelecting;
640      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = !isSelecting;
641      this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = !isSelecting;
642    }
643    private void colorButton_Click(object sender, EventArgs e) {
644      if (colorDialog.ShowDialog(this) == DialogResult.OK) {
645        this.colorButton.Image = this.GenerateImage(16, 16, this.colorDialog.Color);
646      }
647    }
648    private Image GenerateImage(int width, int height, Color fillColor) {
649      Image colorImage = new Bitmap(width, height);
650      using (Graphics gfx = Graphics.FromImage(colorImage)) {
651        using (SolidBrush brush = new SolidBrush(fillColor)) {
652          gfx.FillRectangle(brush, 0, 0, width, height);
653        }
654      }
655      return colorImage;
656    }
657
658    private IRun runToHide = null;
659    private void ContextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e) {
660      var pos = Control.MousePosition;
661      var chartPos = chart.PointToClient(pos);
662
663      HitTestResult h = this.chart.HitTest(chartPos.X, chartPos.Y);
664      if (h.ChartElementType == ChartElementType.DataPoint) {
665        runToHide = (IRun)((DataPoint)h.Object).Tag;
666        hideRunToolStripMenuItem.Visible = true;
667      } else {
668        runToHide = null;
669        hideRunToolStripMenuItem.Visible = false;
670      }
671
672    }
673    private void hideRunToolStripMenuItem_Click(object sender, EventArgs e) {
674      var constraint = Content.Constraints.OfType<RunCollectionContentConstraint>().Where(c => c.Active).FirstOrDefault();
675      if (constraint == null) {
676        constraint = new RunCollectionContentConstraint();
677        Content.Constraints.Add(constraint);
678        constraint.Active = true;
679      }
680      constraint.ConstraintData.Add(runToHide);
681    }
682
683    private void openBoxPlotViewToolStripMenuItem_Click(object sender, EventArgs e) {
684      RunCollectionBoxPlotView boxplotView = new RunCollectionBoxPlotView();
685      boxplotView.Content = this.Content;
686      boxplotView.xAxisComboBox.SelectedItem = xAxisComboBox.SelectedItem;
687      boxplotView.yAxisComboBox.SelectedItem = yAxisComboBox.SelectedItem;
688      boxplotView.Show();
689    }
690    #endregion
691
692    #region Automatic coloring
693    private void colorXAxisButton_Click(object sender, EventArgs e) {
694      ColorRuns(xAxisValue);
695    }
696
697    private void colorYAxisButton_Click(object sender, EventArgs e) {
698      ColorRuns(yAxisValue);
699    }
700
701    private void ColorRuns(string axisValue) {
702      var runs = Content.Where(r => r.Visible).Select(r => new { Run = r, Value = GetValue(r, axisValue) }).Where(r => r.Value.HasValue);
703      double minValue = runs.Min(r => r.Value.Value);
704      double maxValue = runs.Max(r => r.Value.Value);
705      double range = maxValue - minValue;
706
707      foreach (var r in runs) {
708        int colorIndex = 0;
709        if (!range.IsAlmost(0)) colorIndex = (int)((ColorGradient.Colors.Count - 1) * (r.Value.Value - minValue) / (range));
710        r.Run.Color = ColorGradient.Colors[colorIndex];
711      }
712    }
713    #endregion
714  }
715}
Note: See TracBrowser for help on using the repository browser.