Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 2325 was 2325, checked in by mkommend, 15 years ago

changed SparseMatrix.Matrix and MatrixRow to a generic type (ticket #701)

File size: 13.0 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;
29using HeuristicLab.PluginInfrastructure;
30using HeuristicLab.Core;
31using System.Diagnostics;
32using HeuristicLab.SparseMatrix;
33
34namespace HeuristicLab.CEDMA.Charting {
35  public abstract class BubbleChart : Chart {
36    private const double maxXJitterPercent = 0.05;
37    private const double maxYJitterPercent = 0.05;
38    private const int minBubbleSize = 5;
39    private const int maxBubbleSize = 30;
40    private const int maxAlpha = 255;
41    private const int minAlpha = 50;
42    private static readonly Color defaultColor = Color.Blue;
43    private static readonly Color selectionColor = Color.Red;
44
45    private double xJitterFactor = 0.0;
46    private double yJitterFactor = 0.0;
47    private string xDimension;
48    private string yDimension;
49    private string sizeDimension;
50    private bool invertSize;
51    private double minX = double.PositiveInfinity;
52    private double minY = double.PositiveInfinity;
53    private double maxX = double.NegativeInfinity;
54    private double maxY = double.NegativeInfinity;
55
56    protected Group points;
57    protected VisualMatrix matrix;
58    protected Dictionary<IPrimitive, VisualMatrixRow> primitiveToMatrixRowDictionary;
59
60    public BubbleChart(VisualMatrix matrix, PointD lowerLeft, PointD upperRight)
61      : base(lowerLeft, upperRight) {
62      primitiveToMatrixRowDictionary = new Dictionary<IPrimitive, VisualMatrixRow>();
63      this.matrix = matrix;
64    }
65
66    public BubbleChart(VisualMatrix matrix, double x1, double y1, double x2, double y2)
67      : this(matrix, new PointD(x1, y1), new PointD(x2, y2)) {
68    }
69
70    public void SetBubbleSizeDimension(string dimension, bool inverted) {
71      this.sizeDimension = dimension;
72      this.invertSize = inverted;
73      Refresh();
74    }
75
76    public void Refresh() {
77      Repaint();
78      EnforceUpdate();
79    }
80
81    public void ShowXvsY(string xDimension, string yDimension) {
82      if (this.xDimension != xDimension || this.yDimension != yDimension) {
83        this.xDimension = xDimension;
84        this.yDimension = yDimension;
85
86        ResetViewSize();
87        Repaint();
88        ZoomToViewSize();
89      }
90    }
91
92    public void SetJitter(double xJitterFactor, double yJitterFactor) {
93      this.xJitterFactor = xJitterFactor * maxXJitterPercent * Size.Width;
94      this.yJitterFactor = yJitterFactor * maxYJitterPercent * Size.Height;
95      Repaint();
96      EnforceUpdate();
97    }
98
99    public override void Render(Graphics graphics, int width, int height) {
100      graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
101      base.Render(graphics, width, height);
102    }
103
104    protected void Repaint() {
105      if (xDimension == null || yDimension == null) return;
106      double maxSize = 1;
107      double minSize = 1;
108      if (sizeDimension != null && matrix.OrdinalVariables.Contains(sizeDimension)) {
109        var sizes = matrix.Rows
110          .Select(x => Convert.ToDouble(x.Get(sizeDimension)))
111          .Where(size => !double.IsInfinity(size) && size != double.MaxValue && size != double.MinValue)
112          .OrderBy(r => r);
113        minSize = sizes.ElementAt((int)(sizes.Count() * 0.1));
114        maxSize = sizes.ElementAt((int)(sizes.Count() * 0.9));
115      } else {
116        minSize = 1;
117        maxSize = 1;
118      }
119      UpdateEnabled = false;
120      Group.Clear();
121      primitiveToMatrixRowDictionary.Clear();
122      points = new Group(this);
123      Group.Add(new Axis(this, 0, 0, AxisType.Both));
124      UpdateViewSize(0, 0, TransformPixelToWorld(new Size(5, 0)).Width);
125      foreach (VisualMatrixRow r in matrix.Rows.Where(x => x.Visible)) {
126        List<double> xs = new List<double>();
127        List<double> ys = new List<double>();
128        List<object> actualXValues = new List<object>();
129        List<object> actualYValues = new List<object>();
130        int size;
131        if (matrix.OrdinalVariables.Contains(xDimension) && r.Get(xDimension) != null) {
132          xs.Add(Convert.ToDouble(r.Get(xDimension)) + r.XJitter * xJitterFactor);
133          actualXValues.Add(r.Get(xDimension));
134        } else if (matrix.CategoricalVariables.Contains(xDimension) && r.Get(xDimension) != null) {
135          xs.Add(matrix.IndexOfCategoricalValue(xDimension, r.Get(xDimension)) + r.XJitter * xJitterFactor);
136          actualXValues.Add(r.Get(xDimension));
137        } else if (matrix.MultiDimensionalCategoricalVariables.Contains(xDimension)) {
138          var path = xDimension.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim());
139          IEnumerable<MatrixRow<string, object>> subRows = (IEnumerable<MatrixRow<string,object>>)r.Get(path.ElementAt(0));
140          foreach (MatrixRow<string, object> subRow in subRows) {
141            if (subRow.Get(path.ElementAt(1)) != null) {
142              xs.Add(matrix.IndexOfCategoricalValue(xDimension, subRow.Get(path.ElementAt(1))) + r.YJitter * xJitterFactor);
143              actualXValues.Add(subRow.Get(path.ElementAt(1)));
144            }
145          }
146        } else if (matrix.MultiDimensionalOrdinalVariables.Contains(xDimension)) {
147          var path = xDimension.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim());
148          IEnumerable<MatrixRow<string, object>> subRows = (IEnumerable<MatrixRow<string, object>>)r.Get(path.ElementAt(0));
149          foreach (MatrixRow<string,object> subRow in subRows) {
150            if (subRow.Get(path.ElementAt(1)) != null) {
151              xs.Add(Convert.ToDouble(subRow.Get(path.ElementAt(1))) + r.XJitter * xJitterFactor);
152              actualXValues.Add(subRow.Get(path.ElementAt(1)));
153            }
154          }
155        }
156        if (matrix.OrdinalVariables.Contains(yDimension) && r.Get(yDimension) != null) {
157          ys.Add(Convert.ToDouble(r.Get(yDimension)) + r.YJitter * yJitterFactor);
158          actualYValues.Add(r.Get(yDimension));
159        } else if (matrix.CategoricalVariables.Contains(yDimension) && r.Get(yDimension) != null) {
160          ys.Add(matrix.IndexOfCategoricalValue(yDimension, r.Get(yDimension)) + r.YJitter * yJitterFactor);
161          actualYValues.Add(r.Get(yDimension));
162        } else if (matrix.MultiDimensionalCategoricalVariables.Contains(yDimension)) {
163          var path = yDimension.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim());
164          IEnumerable<MatrixRow<string,object>> subRows = (IEnumerable<MatrixRow<string,object>>)r.Get(path.ElementAt(0));
165          foreach (MatrixRow<string,object> subRow in subRows) {
166            if (subRow.Get(path.ElementAt(1)) != null) {
167              ys.Add(matrix.IndexOfCategoricalValue(yDimension, subRow.Get(path.ElementAt(1))) + r.YJitter * yJitterFactor);
168              actualYValues.Add(subRow.Get(path.ElementAt(1)));
169            }
170          }
171        } else if (matrix.MultiDimensionalOrdinalVariables.Contains(yDimension)) {
172          var path = yDimension.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim());
173          IEnumerable<MatrixRow<string, object>> subRows = (IEnumerable<MatrixRow<string, object>>)r.Get(path.ElementAt(0));
174          foreach (MatrixRow<string,object> subRow in subRows) {
175            if (subRow.Get(path.ElementAt(1)) != null) {
176              ys.Add(Convert.ToDouble(subRow.Get(path.ElementAt(1))) + r.YJitter * yJitterFactor);
177              actualYValues.Add(subRow.Get(path.ElementAt(1)));
178            }
179          }
180        }
181        if (xs.Count() == 0) {
182          xs.Add(double.NaN);
183          actualXValues.Add("NaN");
184        }
185        if (ys.Count() == 0) {
186          ys.Add(double.NaN);
187          actualYValues.Add("NaN");
188        }
189        size = CalculateSize(Convert.ToDouble(r.Get(sizeDimension)), minSize, maxSize);
190        Debug.Assert(xs.Count() == ys.Count() || xs.Count() == 1 || ys.Count() == 1);
191        int n = Math.Max(xs.Count(), ys.Count());
192        for (int i = 0; i < n; i++) {
193          double x = xs[Math.Min(i, xs.Count() - 1)];
194          double y = ys[Math.Min(i, ys.Count() - 1)];
195          if (double.IsInfinity(x) || x == double.MaxValue || x == double.MinValue) x = double.NaN;
196          if (double.IsInfinity(y) || y == double.MaxValue || y == double.MinValue) y = double.NaN;
197          if (!double.IsNaN(x) && !double.IsNaN(y) && IsReasonablePoint(new PointD(x, y))) {
198            string actualXValue = actualXValues[Math.Min(i, actualXValues.Count() - 1)].ToString();
199            string actualYValue = actualYValues[Math.Min(i, actualYValues.Count() - 1)].ToString();
200            UpdateViewSize(x, y, TransformPixelToWorld(new Size(size, 0)).Width);
201            int alpha = CalculateAlpha(size);
202            Pen pen = new Pen(Color.FromArgb(alpha, r.Selected ? selectionColor : defaultColor));
203            Brush brush = pen.Brush;
204            FixedSizeCircle c = new FixedSizeCircle(this, x, y, size, pen, brush);
205            c.ToolTipText = xDimension + " = " + actualXValue + Environment.NewLine +
206              yDimension + " = " + actualYValue + Environment.NewLine +
207              r.GetToolTipText();
208            points.Add(c);
209            if (!r.Selected) c.IntoBackground();
210            primitiveToMatrixRowDictionary[c] = r;
211          }
212        }
213      }
214      Group.Add(points);
215      UpdateEnabled = true;
216    }
217
218    private bool IsReasonablePoint(PointD pointD) {
219      return pointD.X > LowerLeft.X && pointD.X < UpperRight.X && pointD.Y > LowerLeft.Y && pointD.Y < UpperRight.Y;
220    }
221
222    private int CalculateSize(double size, double minSize, double maxSize) {
223      if (double.IsNaN(size) || double.IsInfinity(size) || size == double.MaxValue || size == double.MinValue) return minBubbleSize;
224      if (size > maxSize) size = maxSize;
225      if (size < minSize) size = minSize;
226      if (Math.Abs(maxSize - minSize) < 1.0E-10) return minBubbleSize;
227      double sizeDifference = ((size - minSize) / (maxSize - minSize) * (maxBubbleSize - minBubbleSize));
228      if (invertSize) return maxBubbleSize - (int)sizeDifference;
229      else return minBubbleSize + (int)sizeDifference;
230    }
231
232    private int CalculateAlpha(int size) {
233      return (minAlpha + maxAlpha) / 2;
234    }
235
236    private void ZoomToViewSize() {
237      if (minX < maxX && minY < maxY) {
238        // enlarge view by 5% on each side
239        double width = maxX - minX;
240        double height = maxY - minY;
241        minX = minX - width * 0.05;
242        maxX = maxX + width * 0.05;
243        minY = minY - height * 0.05;
244        maxY = maxY + height * 0.05;
245        ZoomIn(minX, minY, maxX, maxY);
246      }
247    }
248
249    private void UpdateViewSize(double x, double y, double size) {
250      if (x - size < minX) minX = x - size;
251      if (x + size > maxX) maxX = x + size;
252      if (y - size < minY) minY = y + size;
253      if (y + size > maxY) maxY = y + size;
254    }
255
256    private void ResetViewSize() {
257      minX = double.PositiveInfinity;
258      maxX = double.NegativeInfinity;
259      minY = double.PositiveInfinity;
260      maxY = double.NegativeInfinity;
261    }
262
263    protected VisualMatrixRow GetMatrixRow(Point point) {
264      VisualMatrixRow r = null;
265      IPrimitive p = points.GetPrimitive(TransformPixelToWorld(point));
266      if (p != null) {
267        primitiveToMatrixRowDictionary.TryGetValue(p, out r);
268      }
269      return r;
270    }
271
272    public virtual void ToggleSelected() {
273      foreach (VisualMatrixRow row in matrix.Rows) {
274        row.ToggleSelected();
275      }
276      matrix.FireChanged();
277    }
278
279    public virtual void ClearSelection() {
280      foreach (VisualMatrixRow entry in matrix.Rows.Where(x => x.Selected)) {
281        entry.ToggleSelected();
282      }
283      matrix.FireChanged();
284    }
285
286    public virtual void ApplyFilter(Func<VisualMatrixRow, bool> filterPred) {
287      foreach (VisualMatrixRow r in matrix.Rows) {
288        if (filterPred(r)) {
289          r.Visible = false;
290          r.Selected = false;
291        }
292      }
293      matrix.FireChanged();
294    }
295
296    public virtual void ClearFilter() {
297      foreach (VisualMatrixRow r in matrix.Rows.Where(x => !x.Visible)) {
298        r.Visible = true;
299      }
300      matrix.FireChanged();
301    }
302  }
303}
Note: See TracBrowser for help on using the repository browser.