source: trunk/branches/HeuristicLab.Optimizer.Tools/3.3/MDSView.cs @ 7526

Last change on this file since 7526 was 7526, checked in by abeham, 8 years ago

#1780: imported branch

File size: 8.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 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.Drawing;
24using System.Globalization;
25using System.Text;
26using System.Text.RegularExpressions;
27using HeuristicLab.Analysis;
28using HeuristicLab.Data;
29using HeuristicLab.Data.Views;
30using HeuristicLab.MainForm;
31using HeuristicLab.MainForm.WindowsForms;
32
33namespace HeuristicLab.Optimizer.Tools {
34  [View("MDSView")]
35  [Content(typeof(IStringConvertibleMatrix), IsDefaultView = false)]
36  public partial class MDSView : StringConvertibleMatrixView {
37
38    public MDSView() {
39      InitializeComponent();
40    }
41
42    protected override void RegisterContentEvents() {
43      base.RegisterContentEvents();
44      Content.RowsChanged += new EventHandler(Content_Changed);
45      Content.ColumnsChanged += new EventHandler(Content_Changed);
46    }
47
48    protected override void DeregisterContentEvents() {
49      Content.RowsChanged -= new EventHandler(Content_Changed);
50      Content.ColumnsChanged -= new EventHandler(Content_Changed);
51      base.DeregisterContentEvents();
52    }
53
54    private void Content_Changed(object sender, EventArgs e) {
55      SetEnabledStateOfControls();
56      OnRedraw();
57    }
58
59    protected override void OnContentChanged() {
60      base.OnContentChanged();
61      OnRedraw();
62    }
63
64    protected override void SetEnabledStateOfControls() {
65      base.SetEnabledStateOfControls();
66      visualizeCheckBox.Enabled = Content != null && Content.Rows == Content.Columns && Content.Rows > 0;
67      mdsPictureBox.Visible = visualizeCheckBox.Enabled && visualizeCheckBox.Checked;
68    }
69
70    private void visualizeCheckBox_CheckedChanged(object sender, System.EventArgs e) {
71      mdsPictureBox.Visible = visualizeCheckBox.Checked;
72      OnRedraw();
73    }
74
75    private void OnRedraw() {
76      if (InvokeRequired) {
77        Invoke((Action)OnRedraw, null);
78      } else {
79        if (!mdsPictureBox.Visible) return;
80        GenerateImage();
81      }
82    }
83
84    private void GenerateImage() {
85      if (Content.Rows == Content.Columns && Content.Rows > 0
86       && mdsPictureBox.Width > 0 && mdsPictureBox.Height > 0) {
87        Bitmap newBitmap = GenerateDistanceImage();
88        if (newBitmap != null) {
89          var oldImage = mdsPictureBox.Image;
90          mdsPictureBox.Image = newBitmap;
91          if (oldImage != null) oldImage.Dispose();
92        }
93      }
94    }
95
96    private Bitmap GenerateDistanceImage() {
97      Bitmap newBitmap = new Bitmap(mdsPictureBox.Width, mdsPictureBox.Height);
98
99      DoubleMatrix coordinates;
100      double stress = double.NaN;
101      DoubleMatrix matrix = new DoubleMatrix(Content.Rows, Content.Columns, Content.ColumnNames, Content.RowNames);
102      try {
103        for (int i = 0; i < Content.Rows; i++)
104          for (int j = 0; j < Content.Columns; j++)
105            matrix[i, j] = double.Parse(Content.GetValue(i, j));
106        coordinates = MultidimensionalScaling.KruskalShepard(matrix);
107        stress = MultidimensionalScaling.CalculateNormalizedStress(matrix, coordinates);
108        statisticsTextBox.Text = "Stress: " + stress.ToString("0.00", CultureInfo.CurrentCulture.NumberFormat);
109      } catch {
110        WriteCenteredTextToBitmap(ref newBitmap, "Matrix is not symmetric");
111        statisticsTextBox.Text = "-";
112        return newBitmap;
113      }
114      double xMin = double.MaxValue, yMin = double.MaxValue, xMax = double.MinValue, yMax = double.MinValue;
115      double maxDistance = double.MinValue;
116      for (int i = 0; i < coordinates.Rows; i++) {
117        if (xMin > coordinates[i, 0]) xMin = coordinates[i, 0];
118        if (yMin > coordinates[i, 1]) yMin = coordinates[i, 1];
119        if (xMax < coordinates[i, 0]) xMax = coordinates[i, 0];
120        if (yMax < coordinates[i, 1]) yMax = coordinates[i, 1];
121
122        for (int j = i + 1; j < coordinates.Rows; j++) {
123          if (matrix[i, j] > maxDistance) maxDistance = matrix[i, j];
124          if (matrix[j, i] > maxDistance) maxDistance = matrix[j, i];
125        }
126      }
127
128      int border = 20;
129      double xStep = xMax != xMin ? (mdsPictureBox.Width - 2 * border) / (xMax - xMin) : 1;
130      double yStep = yMax != yMin ? (mdsPictureBox.Height - 2 * border) / (yMax - yMin) : 1;
131
132      Point[] points = new Point[coordinates.Rows];
133      for (int i = 0; i < coordinates.Rows; i++)
134        points[i] = new Point(border + ((int)((coordinates[i, 0] - xMin) * xStep)),
135                              newBitmap.Height - (border + ((int)((coordinates[i, 1] - yMin) * yStep))));
136
137      Random rand = new Random();
138      using (Graphics graphics = Graphics.FromImage(newBitmap)) {
139        graphics.Clear(Color.White);
140        graphics.DrawString("Showing entities spaced out according to their distances.", Font, Brushes.Black, 5, 2);
141
142        for (int i = 0; i < coordinates.Rows - 1; i++) {
143          for (int j = i + 1; j < coordinates.Rows; j++) {
144            Point start = points[i], end = points[j];
145            string caption = String.Empty;
146            double d = Math.Max(matrix[i, j], matrix[j, i]);
147            float width = (float)Math.Ceiling(5.0 * d / maxDistance);
148            if (d > 0) {
149              graphics.DrawLine(new Pen(Color.IndianRed, width), start, end);
150              if (matrix[i, j] != matrix[j, i])
151                caption = matrix[i, j].ToString(CultureInfo.InvariantCulture.NumberFormat)
152                  + " / " + matrix[j, i].ToString(CultureInfo.InvariantCulture.NumberFormat);
153              else
154                caption = matrix[i, j].ToString(CultureInfo.InvariantCulture.NumberFormat);
155            }
156            if (!String.IsNullOrEmpty(caption)) {
157              double r = rand.NextDouble();
158              while (r < 0.2 || r > 0.8) r = rand.NextDouble();
159              float x = (float)(start.X + (end.X - start.X) * r + 5);
160              float y = (float)(start.Y + (end.Y - start.Y) * r + 5);
161              graphics.DrawString(caption, Font, Brushes.Black, x, y);
162            }
163          }
164        }
165
166        for (int i = 0; i < points.Length; i++) {
167          Point p = new Point(points[i].X - 3, points[i].Y - 3);
168          graphics.FillRectangle(Brushes.Black, p.X, p.Y, 8, 8);
169          graphics.DrawString(i.ToString(), Font, Brushes.Black, p.X, p.Y + 10);
170        }
171      }
172      return newBitmap;
173    }
174
175    private void WriteCenteredTextToBitmap(ref Bitmap bitmap, string text) {
176      if (bitmap == null) return;
177      using (Graphics g = Graphics.FromImage(bitmap)) {
178        g.Clear(Color.White);
179
180        Font font = new Font(FontFamily.GenericSansSerif, 12, FontStyle.Regular);
181        SizeF strSize = g.MeasureString(text, font);
182        if (strSize.Width + 50 > mdsPictureBox.Width) {
183          var m = Regex.Match(text, @"\b\w+[.,]*\b*");
184          var builder = new StringBuilder();
185          while (m.Success) {
186            builder.Append(m.Value + " ");
187            Match next = m.NextMatch();
188            if (g.MeasureString(builder.ToString() + " " + next.Value, font).Width + 50 > mdsPictureBox.Width)
189              builder.AppendLine();
190            m = next;
191          }
192          builder.Remove(builder.Length - 1, 1);
193          text = builder.ToString();
194          strSize = g.MeasureString(text, font);
195        }
196        g.DrawString(text, font, Brushes.Black, (float)(mdsPictureBox.Width - strSize.Width) / 2.0f, (float)(mdsPictureBox.Height - strSize.Height) / 2.0f);
197      }
198    }
199  }
200}
Note: See TracBrowser for help on using the repository browser.