Free cookie consent management tool by TermsFeed Policy Generator

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

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

corrected RunCollectionConstraints and the according views (ticket#970)

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