Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.CEDMA.Charting/BubbleChart.cs @ 840

Last change on this file since 840 was 573, checked in by gkronber, 16 years ago

minor code improvements

File size: 9.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2008 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.Text;
25using System.Drawing;
26using System.Linq;
27using HeuristicLab.Charting;
28using System.Windows.Forms;
29
30namespace HeuristicLab.CEDMA.Charting {
31  public class BubbleChart : Chart {
32    private const double maxXJitterPercent = 0.05;
33    private const double maxYJitterPercent = 0.05;
34    private const int minBubbleSize = 5;
35    private const int maxBubbleSize = 30;
36    private const int maxAlpha = 255;
37    private const int minAlpha = 50;
38    private static readonly Color defaultColor = Color.Blue;
39    private static readonly Color selectionColor = Color.Red;
40
41    private double xJitterFactor = 0.0;
42    private double yJitterFactor = 0.0;
43    private string xDimension;
44    private string yDimension;
45    private string sizeDimension;
46    private bool invertSize;
47    private double minX = double.PositiveInfinity;
48    private double minY = double.PositiveInfinity;
49    private double maxX = double.NegativeInfinity;
50    private double maxY = double.NegativeInfinity;
51    private List<Record> records;
52    private ResultList results;
53    private Dictionary<IPrimitive, Record> primitiveToRecordDictionary;
54    private Dictionary<Record, IPrimitive> recordToPrimitiveDictionary;
55    private Random random = new Random();
56    private Group points;
57
58    public BubbleChart(ResultList results, PointD lowerLeft, PointD upperRight)
59      : base(lowerLeft, upperRight) {
60      records = new List<Record>();
61      primitiveToRecordDictionary = new Dictionary<IPrimitive, Record>();
62      recordToPrimitiveDictionary = new Dictionary<Record, IPrimitive>();
63      this.results = results;
64      foreach(Record r in results.Records) {
65        records.Add(r);
66     }
67      results.OnRecordAdded += new EventHandler<RecordAddedEventArgs>(results_OnRecordAdded);
68      results.Changed += new EventHandler(results_Changed);
69    }
70
71    void results_Changed(object sender, EventArgs e) {
72      Repaint();
73      EnforceUpdate();
74    }
75
76    public BubbleChart(ResultList results, double x1, double y1, double x2, double y2)
77      : this(results, new PointD(x1, y1), new PointD(x2, y2)) {
78    }
79
80    void results_OnRecordAdded(object sender, RecordAddedEventArgs e) {
81      lock(records) {
82        records.Add(e.Record);
83      }
84    }
85
86    public void SetBubbleSizeDimension(string dimension, bool inverted) {
87      this.sizeDimension = dimension;
88      this.invertSize = inverted;
89      Repaint();
90      EnforceUpdate();
91    }
92
93    public void ShowXvsY(string xDimension, string yDimension) {
94      if(this.xDimension != xDimension || this.yDimension != yDimension) {
95        this.xDimension = xDimension;
96        this.yDimension = yDimension;
97        ResetViewSize();
98        Repaint();
99        ZoomToViewSize();
100      }
101    }
102
103    internal void SetJitter(double xJitterFactor, double yJitterFactor) {
104      this.xJitterFactor = xJitterFactor * maxXJitterPercent * Size.Width;
105      this.yJitterFactor = yJitterFactor * maxYJitterPercent * Size.Height;
106      Repaint();
107      EnforceUpdate();
108    }
109
110    private void Repaint() {
111      if(xDimension == null || yDimension == null) return;
112      lock(records) {
113        double maxSize = 1;
114        double minSize = 1;
115        if(sizeDimension != null) {
116          var sizes = records.Select(r => r.Get(sizeDimension)).Where(x => !double.IsInfinity(x) && x != double.MaxValue && x != double.MinValue).OrderBy(x=>x);
117          minSize = sizes.ElementAt((int)(sizes.Count() * 0.1));
118          maxSize = sizes.ElementAt((int)(sizes.Count() * 0.9));
119        }
120        UpdateEnabled = false;
121        Group.Clear();
122        primitiveToRecordDictionary.Clear();
123        recordToPrimitiveDictionary.Clear();
124        points = new Group(this);
125        Group.Add(new Axis(this, 0, 0, AxisType.Both));
126        UpdateViewSize(0, 0, 5);
127        foreach(Record r in records) {
128          double x = r.Get(xDimension) + r.Get(Record.X_JITTER) * xJitterFactor;
129          double y = r.Get(yDimension) + r.Get(Record.Y_JITTER) * yJitterFactor;
130          int size = CalculateSize(r.Get(sizeDimension), minSize, maxSize);
131
132          if(double.IsInfinity(x) || x == double.MaxValue || x == double.MinValue) x = double.NaN;
133          if(double.IsInfinity(y) || y == double.MaxValue || y == double.MinValue) y = double.NaN;
134          if(!double.IsNaN(x) && !double.IsNaN(y)) {
135            UpdateViewSize(x, y, size);
136            int alpha = CalculateAlpha(size);
137            Pen pen = new Pen(Color.FromArgb(alpha, r.Selected ? selectionColor : defaultColor));
138            Brush brush = pen.Brush;
139            FixedSizeCircle c = new FixedSizeCircle(this, x, y, size, pen, brush);
140            c.ToolTipText = r.GetToolTipText();
141            points.Add(c);
142            if(!r.Selected) c.IntoBackground();
143            primitiveToRecordDictionary[c] = r;
144            recordToPrimitiveDictionary[r] = c;
145          }
146        }
147        Group.Add(points);
148        UpdateEnabled = true;
149      }
150    }
151
152    private int CalculateSize(double size, double minSize, double maxSize) {
153      if(double.IsNaN(size) || double.IsInfinity(size) || size == double.MaxValue || size == double.MinValue) return minBubbleSize;
154      if(size > maxSize) size = maxSize;
155      if(size < minSize) size = minSize;
156      double sizeDifference = ((size - minSize) / (maxSize - minSize) * (maxBubbleSize - minBubbleSize));
157      if(invertSize) return maxBubbleSize - (int)sizeDifference;
158      else return minBubbleSize + (int)sizeDifference;
159    }
160
161    private int CalculateAlpha(int size) {
162      return maxAlpha - (int)((double)(size - minBubbleSize) / (double)(maxBubbleSize - minBubbleSize) * (double)(maxAlpha - minAlpha));
163    }
164
165    private void ZoomToViewSize() {
166      if(minX < maxX && minY < maxY) {
167        // enlarge view by 5% on each side
168        double width = maxX - minX;
169        double height = maxY - minY;
170        minX = minX - width * 0.05;
171        maxX = maxX + width * 0.05;
172        minY = minY - height * 0.05;
173        maxY = maxY + height * 0.05;
174        ZoomIn(minX, minY, maxX, maxY);
175      }
176    }
177
178    private void UpdateViewSize(double x, double y, double size) {
179      if(x - size < minX) minX = x - size;
180      if(x + size > maxX) maxX = x + size;
181      if(y - size < minY) minY = y + size;
182      if(y + size > maxY) maxY = y + size;
183    }
184
185    private void ResetViewSize() {
186      minX = double.PositiveInfinity;
187      maxX = double.NegativeInfinity;
188      minY = double.PositiveInfinity;
189      maxY = double.NegativeInfinity;
190    }
191
192    internal Record GetRecord(Point point) {
193      Record r = null;
194      IPrimitive p = points.GetPrimitive(TransformPixelToWorld(point));
195      if(p != null) {
196        primitiveToRecordDictionary.TryGetValue(p, out r);
197      }
198      return r;
199    }
200
201
202    public override void MouseDrag(Point start, Point end, MouseButtons button) {
203      if(button == MouseButtons.Left && Mode == ChartMode.Select) {
204        PointD a = TransformPixelToWorld(start);
205        PointD b = TransformPixelToWorld(end);
206        double minX = Math.Min(a.X, b.X);
207        double minY = Math.Min(a.Y, b.Y);
208        double maxX = Math.Max(a.X, b.X);
209        double maxY = Math.Max(a.Y, b.Y);
210        HeuristicLab.Charting.Rectangle rect = new HeuristicLab.Charting.Rectangle(this, minX, minY, maxX, maxY);
211
212        List<IPrimitive> primitives = new List<IPrimitive>();
213        primitives.AddRange(points.Primitives);
214
215        foreach(FixedSizeCircle p in primitives) {
216          if(rect.ContainsPoint(p.Point)) {
217            Record r;
218            primitiveToRecordDictionary.TryGetValue(p, out r);
219            if(r != null) r.ToggleSelected();
220          }
221        }
222        results.FireChanged();
223      } else {
224        base.MouseDrag(start, end, button);
225      }
226    }
227
228    public override void MouseClick(Point point, MouseButtons button) {
229      if(button == MouseButtons.Left) {
230        Record r = GetRecord(point);
231        if(r != null) r.ToggleSelected();
232        results.FireChanged();
233      } else {
234        base.MouseClick(point, button);
235      }
236    }
237
238    public override void MouseDoubleClick(Point point, MouseButtons button) {
239      if(button == MouseButtons.Left) {
240        Record r = GetRecord(point);
241        if(r != null) r.OpenModel();
242      } else {
243        base.MouseDoubleClick(point, button);
244      }
245    }
246  }
247}
Note: See TracBrowser for help on using the repository browser.