Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 3546 was 3546, checked in by mkommend, 14 years ago

corrected tabular view and bubble chart view (ticket #970)

File size: 19.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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.Drawing;
27using System.Linq;
28using System.Text;
29using System.Windows.Forms;
30using HeuristicLab.MainForm.WindowsForms;
31using HeuristicLab.MainForm;
32using System.Windows.Forms.DataVisualization.Charting;
33using HeuristicLab.Common;
34using HeuristicLab.Core;
35using HeuristicLab.Data;
36using System.Threading;
37
38namespace HeuristicLab.Optimization.Views {
39  [View("RunCollection BubbleChart")]
40  [Content(typeof(RunCollection), false)]
41  public partial class RunCollectionBubbleChartView : AsynchronousContentView {
42    private enum SizeDimension { Constant = 0 }
43    private enum AxisDimension { Index = 0 }
44
45    private Dictionary<int, Dictionary<object, double>> categoricalMapping;
46    private Dictionary<IRun, double> xJitter;
47    private Dictionary<IRun, double> yJitter;
48    private double xJitterFactor = 0.0;
49    private double yJitterFactor = 0.0;
50    private Random random;
51    private bool isSelecting = false;
52
53    public RunCollectionBubbleChartView() {
54      InitializeComponent();
55      Caption = "Run Collection Bubble Chart";
56
57      this.categoricalMapping = new Dictionary<int, Dictionary<object, double>>();
58      this.xJitter = new Dictionary<IRun, double>();
59      this.yJitter = new Dictionary<IRun, double>();
60      this.random = new Random();
61      this.colorDialog.Color = Color.Black;
62      this.colorButton.Image = this.GenerateImage(16, 16, this.colorDialog.Color);
63      this.isSelecting = false;
64
65
66      this.chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
67      this.chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
68      this.chart.ChartAreas[0].CursorX.Interval = 0;
69      this.chart.ChartAreas[0].CursorY.Interval = 0;
70      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = !this.isSelecting;
71      this.chart.ChartAreas[0].AxisX.IntervalAutoMode = IntervalAutoMode.VariableCount;
72      this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = !this.isSelecting;
73      this.chart.ChartAreas[0].AxisY.IntervalAutoMode = IntervalAutoMode.VariableCount;
74    }
75
76    public RunCollectionBubbleChartView(RunCollection content)
77      : this() {
78      Content = content;
79    }
80
81    public new RunCollection Content {
82      get { return (RunCollection)base.Content; }
83      set { base.Content = value; }
84    }
85
86    public IStringConvertibleMatrix Matrix {
87      get { return this.Content; }
88    }
89
90    protected override void RegisterContentEvents() {
91      base.RegisterContentEvents();
92      Content.Reset += new EventHandler(Content_Reset);
93      Content.ColumnNamesChanged += new EventHandler(Content_ColumnNamesChanged);
94      Content.ItemsAdded += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
95      Content.ItemsRemoved += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
96      Content.CollectionReset += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
97      RegisterRunEvents(Content);
98    }
99    protected virtual void RegisterRunEvents(IEnumerable<IRun> runs) {
100      foreach (IRun run in runs)
101        run.Changed += new EventHandler(run_Changed);
102    }
103    protected override void DeregisterContentEvents() {
104      base.DeregisterContentEvents();
105      Content.Reset -= new EventHandler(Content_Reset);
106      Content.ColumnNamesChanged -= new EventHandler(Content_ColumnNamesChanged);
107      Content.ItemsAdded -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
108      Content.ItemsRemoved -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
109      Content.CollectionReset -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
110      DeregisterRunEvents(Content);
111    }
112    protected virtual void DeregisterRunEvents(IEnumerable<IRun> runs) {
113      foreach (IRun run in runs)
114        run.Changed -= new EventHandler(run_Changed);
115    }
116
117    private void Content_CollectionReset(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
118      DeregisterRunEvents(e.OldItems);
119      RegisterRunEvents(e.Items);
120    }
121    private void Content_ItemsRemoved(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
122      DeregisterRunEvents(e.Items);
123    }
124    private void Content_ItemsAdded(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
125      RegisterRunEvents(e.Items);
126    }
127    private void run_Changed(object sender, EventArgs e) {
128      IRun run = (IRun)sender;
129      DataPoint point = this.chart.Series[0].Points.Where(p => p.Tag == run).SingleOrDefault();
130      if (point != null) {
131        point.Color = run.Color;
132        if (!run.Visible)
133          this.chart.Series[0].Points.Remove(point);
134      } else
135        AddDataPoint(run);
136    }
137
138    protected override void OnContentChanged() {
139      base.OnContentChanged();
140      this.categoricalMapping.Clear();
141      UpdateComboBoxes();
142      UpdateDataPoints();
143    }
144    private void Content_ColumnNamesChanged(object sender, EventArgs e) {
145      if (InvokeRequired)
146        Invoke(new EventHandler(Content_ColumnNamesChanged), sender, e);
147      else
148        UpdateComboBoxes();
149    }
150
151    private void UpdateComboBoxes() {
152      this.xAxisComboBox.Items.Clear();
153      this.yAxisComboBox.Items.Clear();
154      this.sizeComboBox.Items.Clear();
155      if (Content != null) {
156        string[] additionalAxisDimension = Enum.GetNames(typeof(AxisDimension));
157        this.xAxisComboBox.Items.AddRange(additionalAxisDimension);
158        this.xAxisComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
159        this.yAxisComboBox.Items.AddRange(additionalAxisDimension);
160        this.yAxisComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
161        string[] additionalSizeDimension = Enum.GetNames(typeof(SizeDimension));
162        this.sizeComboBox.Items.AddRange(additionalSizeDimension);
163        this.sizeComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
164        this.sizeComboBox.SelectedItem = SizeDimension.Constant.ToString();
165      }
166    }
167
168    private void Content_Reset(object sender, EventArgs e) {
169      if (InvokeRequired)
170        Invoke(new EventHandler(Content_Reset), sender, e);
171      else {
172        this.categoricalMapping.Clear();
173        UpdateDataPoints();
174      }
175    }
176
177    private void UpdateDataPoints() {
178      Series series = this.chart.Series[0];
179      series.Points.Clear();
180      if (Content != null) {
181        foreach (IRun run in this.Content)
182          this.AddDataPoint(run);
183
184        //check to correct max bubble size
185        if (this.chart.Series[0].Points.Select(p => p.YValues[1]).Distinct().Count() == 1)
186          this.chart.Series[0]["BubbleMaxSize"] = "2";
187        else
188          this.chart.Series[0]["BubbleMaxSize"] = "7";
189
190        if (this.chart.Series[0].Points.Count == 0)
191          noRunsLabel.Visible = true;
192        else
193          noRunsLabel.Visible = false;
194      }
195    }
196    private void AddDataPoint(IRun run) {
197      double? xValue;
198      double? yValue;
199      double? sizeValue;
200      Series series = this.chart.Series[0];
201      int row = this.Content.ToList().IndexOf(run);
202      xValue = GetValue(run, (string)xAxisComboBox.SelectedItem);
203      yValue = GetValue(run, (string)yAxisComboBox.SelectedItem);
204      sizeValue = GetValue(run, (string)sizeComboBox.SelectedItem);
205      if (xValue.HasValue && yValue.HasValue && sizeValue.HasValue) {
206        xValue = xValue.Value + xValue.Value * GetXJitter(run) * xJitterFactor;
207        yValue = yValue.Value + yValue.Value * GetYJitter(run) * yJitterFactor;
208        if (run.Visible) {
209          DataPoint point = new DataPoint(xValue.Value, new double[] { yValue.Value, sizeValue.Value });
210          point.Tag = run;
211          point.Color = run.Color;
212          series.Points.Add(point);
213        }
214      }
215    }
216    private double? GetValue(IRun run, string columnName) {
217      if (run == null || string.IsNullOrEmpty(columnName))
218        return null;
219
220      if (Enum.IsDefined(typeof(AxisDimension), columnName)) {
221        AxisDimension axisDimension = (AxisDimension)Enum.Parse(typeof(AxisDimension), columnName);
222        return GetValue(run, axisDimension);
223      } else if (Enum.IsDefined(typeof(SizeDimension), columnName)) {
224        SizeDimension sizeDimension = (SizeDimension)Enum.Parse(typeof(SizeDimension), columnName);
225        return GetValue(run, sizeDimension);
226      } else {
227        int columnIndex = Matrix.ColumnNames.ToList().IndexOf(columnName);
228        IItem value = Content.GetValue(run, columnIndex);
229        if (value == null)
230          return null;
231
232        DoubleValue doubleValue = value as DoubleValue;
233        IntValue intValue = value as IntValue;
234        double? ret = null;
235        if (doubleValue != null) {
236          if (!double.IsNaN(doubleValue.Value) && !double.IsInfinity(doubleValue.Value))
237            ret = doubleValue.Value;
238        } else if (intValue != null)
239          ret = intValue.Value;
240        else
241          ret = GetCategoricalValue(columnIndex, value.ToString());
242
243        return ret;
244      }
245    }
246    private double GetCategoricalValue(int dimension, string value) {
247      if (!this.categoricalMapping.ContainsKey(dimension))
248        this.categoricalMapping[dimension] = new Dictionary<object, double>();
249      if (!this.categoricalMapping[dimension].ContainsKey(value)) {
250        if (this.categoricalMapping[dimension].Values.Count == 0)
251          this.categoricalMapping[dimension][value] = 1.0;
252        else
253          this.categoricalMapping[dimension][value] = this.categoricalMapping[dimension].Values.Max() + 1.0;
254      }
255      return this.categoricalMapping[dimension][value];
256    }
257    private double GetValue(IRun run, AxisDimension axisDimension) {
258      double value = double.NaN;
259      switch (axisDimension) {
260        case AxisDimension.Index: {
261            value = Content.ToList().IndexOf(run);
262            break;
263          }
264        default: {
265            throw new ArgumentException("No handling strategy for " + axisDimension.ToString() + " is defined.");
266          }
267      }
268      return value;
269    }
270    private double GetValue(IRun run, SizeDimension sizeDimension) {
271      double value = double.NaN;
272      switch (sizeDimension) {
273        case SizeDimension.Constant: {
274            value = 2;
275            break;
276          }
277        default: {
278            throw new ArgumentException("No handling strategy for " + sizeDimension.ToString() + " is defined.");
279          }
280      }
281      return value;
282    }
283
284    #region drag and drop and tooltip
285    private IRun draggedRun;
286    private void chart_MouseDown(object sender, MouseEventArgs e) {
287      HitTestResult h = this.chart.HitTest(e.X, e.Y);
288      if (h.ChartElementType == ChartElementType.DataPoint) {
289        IRun run = (IRun)((DataPoint)h.Object).Tag;
290        if (e.Clicks >= 2) {
291          IContentView view = MainFormManager.CreateDefaultView(run);
292          view.ReadOnly = this.ReadOnly;
293          view.Locked = this.Locked;
294          view.Show();
295        } else
296          this.draggedRun = run;
297        this.chart.ChartAreas[0].CursorX.SetSelectionPosition(double.NaN, double.NaN);
298        this.chart.ChartAreas[0].CursorY.SetSelectionPosition(double.NaN, double.NaN);
299      }
300    }
301
302    private void chart_MouseUp(object sender, MouseEventArgs e) {
303      if (isSelecting) {
304        System.Windows.Forms.DataVisualization.Charting.Cursor xCursor = chart.ChartAreas[0].CursorX;
305        System.Windows.Forms.DataVisualization.Charting.Cursor yCursor = chart.ChartAreas[0].CursorY;
306
307        double minX = Math.Min(xCursor.SelectionStart, xCursor.SelectionEnd);
308        double maxX = Math.Max(xCursor.SelectionStart, xCursor.SelectionEnd);
309        double minY = Math.Min(yCursor.SelectionStart, yCursor.SelectionEnd);
310        double maxY = Math.Max(yCursor.SelectionStart, yCursor.SelectionEnd);
311
312        //check for click to select model
313        if (minX == maxX && minY == maxY) {
314          HitTestResult hitTest = chart.HitTest(e.X, e.Y);
315          if (hitTest.ChartElementType == ChartElementType.DataPoint) {
316            int pointIndex = hitTest.PointIndex;
317            IRun run = (IRun)this.chart.Series[0].Points[pointIndex].Tag;
318            run.Color = colorDialog.Color;
319          }
320        } else {
321          List<DataPoint> selectedPoints = new List<DataPoint>();
322          foreach (DataPoint p in this.chart.Series[0].Points) {
323            if (p.XValue >= minX && p.XValue < maxX &&
324              p.YValues[0] >= minY && p.YValues[0] < maxY) {
325              selectedPoints.Add(p);
326            }
327          }
328          foreach (DataPoint p in selectedPoints) {
329            IRun run = (IRun)p.Tag;
330            run.Color = colorDialog.Color;
331          }
332        }
333        this.chart.ChartAreas[0].CursorX.SetSelectionPosition(double.NaN, double.NaN);
334        this.chart.ChartAreas[0].CursorY.SetSelectionPosition(double.NaN, double.NaN);
335      }
336    }
337
338    private void chart_MouseMove(object sender, MouseEventArgs e) {
339      HitTestResult h = this.chart.HitTest(e.X, e.Y);
340      if (!Locked) {
341        if (this.draggedRun != null && h.ChartElementType != ChartElementType.DataPoint) {
342          DataObject data = new DataObject();
343          data.SetData("Type", draggedRun.GetType());
344          data.SetData("Value", draggedRun);
345          if (ReadOnly)
346            DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link);
347          else {
348            DragDropEffects result = DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link | DragDropEffects.Move);
349            if ((result & DragDropEffects.Move) == DragDropEffects.Move)
350              Content.Remove(draggedRun);
351          }
352          this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = !isSelecting;
353          this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = !isSelecting;
354          this.draggedRun = null;
355        }
356      }
357      string newTooltipText = string.Empty;
358      string oldTooltipText;
359      if (h.ChartElementType == ChartElementType.DataPoint) {
360        IRun run = (IRun)((DataPoint)h.Object).Tag;
361        newTooltipText = BuildTooltip(run);
362      }
363
364      oldTooltipText = this.tooltip.GetToolTip(chart);
365      if (newTooltipText != oldTooltipText)
366        this.tooltip.SetToolTip(chart, newTooltipText);
367    }
368
369    private string BuildTooltip(IRun run) {
370      string tooltip;
371      tooltip = run.Name + System.Environment.NewLine;
372
373      double? xValue = this.GetValue(run, (string)xAxisComboBox.SelectedItem);
374      double? yValue = this.GetValue(run, (string)yAxisComboBox.SelectedItem);
375      double? sizeValue = this.GetValue(run, (string)sizeComboBox.SelectedItem);
376
377      string xString = xValue == null ? string.Empty : xValue.Value.ToString();
378      string yString = yValue == null ? string.Empty : yValue.Value.ToString();
379      string sizeString = sizeValue == null ? string.Empty : sizeValue.Value.ToString();
380
381      tooltip += xAxisComboBox.SelectedItem + " : " + xString + Environment.NewLine;
382      tooltip += yAxisComboBox.SelectedItem + " : " + yString + Environment.NewLine;
383      tooltip += sizeComboBox.SelectedItem + " : " + sizeString + Environment.NewLine;
384
385      return tooltip;
386    }
387    #endregion
388
389    #region GUI events and updating
390    private double GetXJitter(IRun run) {
391      if (!this.xJitter.ContainsKey(run))
392        this.xJitter[run] = random.NextDouble() * 2.0 - 1.0;
393      return this.xJitter[run];
394    }
395    private double GetYJitter(IRun run) {
396      if (!this.yJitter.ContainsKey(run))
397        this.yJitter[run] = random.NextDouble() * 2.0 - 1.0;
398      return this.yJitter[run];
399    }
400    private void jitterTrackBar_ValueChanged(object sender, EventArgs e) {
401      this.xJitterFactor = xTrackBar.Value / 100.0;
402      this.yJitterFactor = yTrackBar.Value / 100.0;
403      this.UpdateDataPoints();
404    }
405
406    private void AxisComboBox_SelectedIndexChanged(object sender, EventArgs e) {
407      UpdateDataPoints();
408      UpdateAxisLabels();
409    }
410    private void UpdateAxisLabels() {
411      Axis xAxis = this.chart.ChartAreas[0].AxisX;
412      Axis yAxis = this.chart.ChartAreas[0].AxisY;
413      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
414      SetCustomAxisLabels(xAxis, xAxisComboBox.SelectedIndex - axisDimensionCount);
415      SetCustomAxisLabels(yAxis, yAxisComboBox.SelectedIndex - axisDimensionCount);
416    }
417    private void SetCustomAxisLabels(Axis axis, int dimension) {
418      axis.CustomLabels.Clear();
419      if (categoricalMapping.ContainsKey(dimension)) {
420        CustomLabel label = null;
421        foreach (var pair in categoricalMapping[dimension]) {
422          string labelText = pair.Key.ToString();
423          if (labelText.Length > 25)
424            labelText = labelText.Substring(0, 25) + " ... ";
425          label = axis.CustomLabels.Add(pair.Value - 0.5, pair.Value + 0.5, labelText);
426          label.GridTicks = GridTickTypes.TickMark;
427        }
428        axis.IsLabelAutoFit = false;
429        axis.LabelStyle.Enabled = true;
430        axis.LabelStyle.Angle = 0;
431        axis.LabelStyle.TruncatedLabels = true;
432      }
433    }
434
435    private void zoomButton_CheckedChanged(object sender, EventArgs e) {
436      this.isSelecting = selectButton.Checked;
437      this.colorButton.Enabled = this.isSelecting;
438      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = !isSelecting;
439      this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = !isSelecting;
440    }
441    private void colorButton_Click(object sender, EventArgs e) {
442      if (colorDialog.ShowDialog(this) == DialogResult.OK) {
443        this.colorButton.Image = this.GenerateImage(16, 16, this.colorDialog.Color);
444      }
445    }
446    private Image GenerateImage(int width, int height, Color fillColor) {
447      Image colorImage = new Bitmap(width, height);
448      using (Graphics gfx = Graphics.FromImage(colorImage)) {
449        using (SolidBrush brush = new SolidBrush(fillColor)) {
450          gfx.FillRectangle(brush, 0, 0, width, height);
451        }
452      }
453      return colorImage;
454    }
455    #endregion
456  }
457}
Note: See TracBrowser for help on using the repository browser.