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

Last change on this file since 3524 was 3524, checked in by mkommend, 12 years ago

implemented reviewer comments (ticket #970)

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