Free cookie consent management tool by TermsFeed Policy Generator

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

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

implemented first version of RunConstraints (ticket #970)

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