Free cookie consent management tool by TermsFeed Policy Generator

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

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

added small fix for RunCollectionBubbleChart (ticket #970)

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