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

Last change on this file since 3447 was 3447, checked in by mkommend, 11 years ago

added RowComparer for RunCollectionTabularView (ticket #970)

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