Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2521_ProblemRefactoring/HeuristicLab.Problems.QuadraticAssignment.Views/3.3/QAPVisualizationControl.cs @ 17946

Last change on this file since 17946 was 17252, checked in by abeham, 5 years ago

#2521: made QAP problem data readonly

  • Also refactored instance providers slightly to specify file extension for importing and exporting data and fixing dialogs
File size: 21.0 KB
RevLine 
[5932]1#region License Information
2/* HeuristicLab
[17226]3 * Copyright (C) Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[5932]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 System.Windows.Forms;
28using HeuristicLab.Analysis;
29using HeuristicLab.Common;
30using HeuristicLab.Common.Resources;
31using HeuristicLab.Data;
32using HeuristicLab.Encodings.PermutationEncoding;
33
34namespace HeuristicLab.Problems.QuadraticAssignment.Views {
35  public partial class QAPVisualizationControl : UserControl {
36    private Bitmap bitmap;
37    private Bitmap defaultBitmap;
38    private bool showingMessage;
39
40    #region Properties
41    private DoubleMatrix distances;
42    public DoubleMatrix Distances {
43      get { return distances; }
44      set {
45        distances = value;
46        OnRedraw();
47      }
48    }
49
50    private DoubleMatrix weights;
51    public DoubleMatrix Weights {
52      get { return weights; }
53      set {
54        weights = value;
55        OnRedraw();
56      }
57    }
58
59    private Permutation assignment;
60    public Permutation Assignment {
61      get { return assignment; }
62      set {
63        DeregisterAssignmentEvents();
64        assignment = value;
65        RegisterAssignmentEvents();
66        OnRedraw();
67      }
68    }
69    #endregion
70
71    #region Event Handling
72
73    private void DeregisterAssignmentEvents() {
74      if (Assignment != null) {
75        Assignment.Reset -= new EventHandler(RedrawNecessary);
76        Assignment.ItemChanged -= new EventHandler<EventArgs<int>>(RedrawNecessary);
77      }
78    }
79
80    private void RegisterAssignmentEvents() {
81      if (Assignment != null) {
82        Assignment.Reset += new EventHandler(RedrawNecessary);
83        Assignment.ItemChanged += new EventHandler<EventArgs<int>>(RedrawNecessary);
84      }
85    }
86
87    private void redrawButton_Click(object sender, EventArgs e) {
88      OnRedraw();
89    }
90
91    private void radio_CheckedChanged(object sender, EventArgs e) {
92      RadioButton rb = (sender as RadioButton);
93      if (rb != null && !rb.Checked) return;
94      else OnRedraw();
95    }
96
97    private void RedrawNecessary(object sender, EventArgs e) {
98      MarkRedrawNecessary();
99    }
100
101    private void RedrawNecessary(object sender, EventArgs<int, int> e) {
102      MarkRedrawNecessary();
103    }
104
105    private void pictureBox_SizeChanged(object sender, EventArgs e) {
106      SetupDefaultBitmap();
107      if (!showingMessage) MarkRedrawNecessary();
108      else OnRedraw();
109    }
110    #endregion
111
112    public QAPVisualizationControl() {
113      InitializeComponent();
114      showingMessage = false;
115      redrawButton.Text = String.Empty;
116      redrawButton.Image = VSImageLibrary.Refresh;
117      SetupDefaultBitmap();
118    }
119
120    private void SetupDefaultBitmap() {
121      if (defaultBitmap != null) {
122        defaultBitmap.Dispose();
123        defaultBitmap = null;
124      }
125      if (pictureBox.Width > 0 && pictureBox.Height > 0) {
126        defaultBitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
127        WriteCenteredTextToBitmap(ref defaultBitmap, "No visualization available");
128      }
129    }
130
131    private void WriteCenteredTextToBitmap(ref Bitmap bitmap, string text) {
132      if (bitmap == null) return;
133      using (Graphics g = Graphics.FromImage(bitmap)) {
[7092]134        g.Clear(Color.White);
[5932]135
136        Font font = new Font(FontFamily.GenericSansSerif, 12, FontStyle.Regular);
137        SizeF strSize = g.MeasureString(text, font);
138        if (strSize.Width + 50 > pictureBox.Width) {
139          Match m = Regex.Match(text, @"\b\w+[.,]*\b*");
140          StringBuilder builder = new StringBuilder();
141          while (m.Success) {
142            builder.Append(m.Value + " ");
143            Match next = m.NextMatch();
144            if (g.MeasureString(builder.ToString() + " " + next.Value, font).Width + 50 > pictureBox.Width)
145              builder.AppendLine();
146            m = next;
147          }
148          builder.Remove(builder.Length - 1, 1);
149          text = builder.ToString();
150          strSize = g.MeasureString(text, font);
151        }
152        g.DrawString(text, font, Brushes.Black, (float)(pictureBox.Width - strSize.Width) / 2.0f, (float)(pictureBox.Height - strSize.Height) / 2.0f);
153      }
154    }
155
156    private void OnRedraw() {
157      if (InvokeRequired) {
158        Invoke((Action)OnRedraw, null);
159      } else {
160        GenerateImage();
161      }
162    }
163
164    private void GenerateImage() {
165      if (pictureBox.Width > 0 && pictureBox.Height > 0) {
166        Bitmap newBitmap = null;
167        stressLabel.Text = "-";
[7416]168        stressLabel.ForeColor = Color.Black;
[5932]169        if (distancesRadioButton.Checked && Distances != null && Distances.Rows > 0
170          && Distances.Rows == Distances.Columns) {
[7416]171          if (Distances.Rows > 50) {
[5932]172            newBitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
173            WriteCenteredTextToBitmap(ref newBitmap, "Problem dimension is too large for visualization.");
174            showingMessage = true;
175          } else newBitmap = GenerateDistanceImage();
176        } else if (weightsRadioButton.Checked && Weights != null && Weights.Rows > 0
177          && Weights.Rows == Weights.Columns) {
[7416]178          if (Weights.Rows > 50) {
[5932]179            newBitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
180            WriteCenteredTextToBitmap(ref newBitmap, "Problem dimension is too large for visualization.");
181            showingMessage = true;
182          } else newBitmap = GenerateWeightsImage();
183        } else if (assignmentRadioButton.Checked
184          && Assignment != null && Assignment.Length > 0
185          && Weights != null && Weights.Rows > 0
186          && Distances != null && Distances.Rows > 0
187          && Weights.Rows == Weights.Columns
188          && Distances.Rows == Distances.Columns
189          && Assignment.Length == Weights.Rows
190          && Assignment.Length == Distances.Rows
191          && Assignment.Validate()) {
[7416]192          if (Assignment.Length > 50) {
[5932]193            newBitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
194            WriteCenteredTextToBitmap(ref newBitmap, "Problem dimension is too large for visualization.");
195            showingMessage = true;
196          } else newBitmap = GenerateAssignmentImage();
197        }
198
199        pictureBox.Image = newBitmap != null ? newBitmap : defaultBitmap;
200        if (bitmap != null) bitmap.Dispose();
201        if (newBitmap != null) bitmap = newBitmap;
202        else {
203          bitmap = null;
204          showingMessage = true;
205        }
206      }
207    }
208
209    private void MarkRedrawNecessary() {
210      if (pictureBox.Width > 0 && pictureBox.Height > 0) {
211        Bitmap newBitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
212        stressLabel.Text = "-";
[7416]213        stressLabel.ForeColor = Color.Black;
[5932]214        WriteCenteredTextToBitmap(ref newBitmap, "Please refresh view.");
215        showingMessage = false; // we're showing a message, but we should be showing the visualization, so this is false
216
217        pictureBox.Image = newBitmap != null ? newBitmap : defaultBitmap;
218        if (bitmap != null) bitmap.Dispose();
219        if (newBitmap != null) bitmap = newBitmap;
220        else bitmap = null;
221      }
222    }
223
224    #region Draw distances
225    private Bitmap GenerateDistanceImage() {
226      if ((pictureBox.Width > 0) && (pictureBox.Height > 0)) {
227        Bitmap newBitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
228
229        DoubleMatrix coordinates;
230        double stress = double.NaN;
231        try {
232          coordinates = MultidimensionalScaling.KruskalShepard(distances);
233          stress = MultidimensionalScaling.CalculateNormalizedStress(distances, coordinates);
234          stressLabel.Text = stress.ToString("0.00", CultureInfo.CurrentCulture.NumberFormat);
[7416]235          if (stress < 0.1) stressLabel.ForeColor = Color.DarkGreen;
236          else if (stress < 0.2) stressLabel.ForeColor = Color.DarkOrange;
237          else stressLabel.ForeColor = Color.DarkRed;
[5932]238        } catch {
239          WriteCenteredTextToBitmap(ref newBitmap, "Distance matrix is not symmetric");
240          showingMessage = true;
241          stressLabel.Text = "-";
[7416]242          stressLabel.ForeColor = Color.Black;
[5932]243          return newBitmap;
244        }
245        double xMin = double.MaxValue, yMin = double.MaxValue, xMax = double.MinValue, yMax = double.MinValue;
246        double maxDistance = double.MinValue;
247        for (int i = 0; i < coordinates.Rows; i++) {
248          if (xMin > coordinates[i, 0]) xMin = coordinates[i, 0];
249          if (yMin > coordinates[i, 1]) yMin = coordinates[i, 1];
250          if (xMax < coordinates[i, 0]) xMax = coordinates[i, 0];
251          if (yMax < coordinates[i, 1]) yMax = coordinates[i, 1];
252
253          for (int j = i + 1; j < coordinates.Rows; j++) {
254            if (distances[i, j] > maxDistance) maxDistance = distances[i, j];
255            if (distances[j, i] > maxDistance) maxDistance = distances[j, i];
256          }
257        }
258
259        int border = 20;
260        double xStep = xMax != xMin ? (pictureBox.Width - 2 * border) / (xMax - xMin) : 1;
261        double yStep = yMax != yMin ? (pictureBox.Height - 2 * border) / (yMax - yMin) : 1;
262
263        Point[] points = new Point[coordinates.Rows];
264        for (int i = 0; i < coordinates.Rows; i++)
265          points[i] = new Point(border + ((int)((coordinates[i, 0] - xMin) * xStep)),
266                                newBitmap.Height - (border + ((int)((coordinates[i, 1] - yMin) * yStep))));
267
268        Random rand = new Random();
269        using (Graphics graphics = Graphics.FromImage(newBitmap)) {
[7092]270          graphics.Clear(Color.White);
[7769]271          graphics.DrawString("Showing locations layed out according to their distances", Font, Brushes.Black, 5, 2);
[5932]272
273          for (int i = 0; i < coordinates.Rows - 1; i++) {
274            for (int j = i + 1; j < coordinates.Rows; j++) {
275              Point start = points[i], end = points[j];
276              string caption = String.Empty;
277              double d = Math.Max(distances[i, j], distances[j, i]);
278              float width = (float)Math.Ceiling(5.0 * d / maxDistance);
279              if (d > 0) {
280                graphics.DrawLine(new Pen(Color.IndianRed, width), start, end);
281                if (distances[i, j] != distances[j, i])
282                  caption = distances[i, j].ToString(CultureInfo.InvariantCulture.NumberFormat)
283                    + " / " + distances[j, i].ToString(CultureInfo.InvariantCulture.NumberFormat);
284                else
285                  caption = distances[i, j].ToString(CultureInfo.InvariantCulture.NumberFormat);
286              }
287              if (!String.IsNullOrEmpty(caption)) {
288                double r = rand.NextDouble();
289                while (r < 0.2 || r > 0.8) r = rand.NextDouble();
290                float x = (float)(start.X + (end.X - start.X) * r + 5);
291                float y = (float)(start.Y + (end.Y - start.Y) * r + 5);
292                graphics.DrawString(caption, Font, Brushes.Black, x, y);
293              }
294            }
295          }
296
297          for (int i = 0; i < points.Length; i++) {
298            Point p = new Point(points[i].X - 3, points[i].Y - 3);
299            graphics.FillRectangle(Brushes.Black, p.X, p.Y, 8, 8);
300            graphics.DrawString(i.ToString(), Font, Brushes.Black, p.X, p.Y + 10);
301          }
302        }
303        showingMessage = false;
304        return newBitmap;
305      }
306      return null;
307    }
308    #endregion
309
310    #region Draw weights
311    private Bitmap GenerateWeightsImage() {
312      if ((pictureBox.Width > 0) && (pictureBox.Height > 0)) {
313        Bitmap newBitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
314
315        double maxWeight = double.MinValue;
316        for (int i = 0; i < weights.Rows; i++)
317          for (int j = i + 1; j < weights.Rows; j++) {
[7416]318            if (weights[i, j] > maxWeight) maxWeight = weights[i, j];
319            if (weights[j, i] > maxWeight) maxWeight = weights[j, i];
[5932]320          }
321
322        DoubleMatrix distances = new DoubleMatrix(weights.Rows, weights.Columns);
323        for (int i = 0; i < distances.Rows; i++)
324          for (int j = 0; j < distances.Columns; j++) {
[7416]325            if (weights[i, j] == 0) distances[i, j] = double.NaN;
326            else distances[i, j] = maxWeight / weights[i, j];
[5932]327          }
328
329        DoubleMatrix coordinates;
330        double stress = double.NaN;
331        try {
332          coordinates = MultidimensionalScaling.KruskalShepard(distances);
333          stress = MultidimensionalScaling.CalculateNormalizedStress(distances, coordinates);
334          stressLabel.Text = stress.ToString("0.00", CultureInfo.CurrentCulture.NumberFormat);
[7416]335          if (stress < 0.1) stressLabel.ForeColor = Color.DarkGreen;
336          else if (stress < 0.2) stressLabel.ForeColor = Color.DarkOrange;
337          else stressLabel.ForeColor = Color.DarkRed;
[5932]338        } catch {
339          WriteCenteredTextToBitmap(ref newBitmap, "Weights matrix is not symmetric");
340          showingMessage = true;
341          stressLabel.Text = "-";
[7416]342          stressLabel.ForeColor = Color.Black;
[5932]343          return newBitmap;
344        }
345        double xMin = double.MaxValue, yMin = double.MaxValue, xMax = double.MinValue, yMax = double.MinValue;
346        for (int i = 0; i < coordinates.Rows; i++) {
347          if (xMin > coordinates[i, 0]) xMin = coordinates[i, 0];
348          if (yMin > coordinates[i, 1]) yMin = coordinates[i, 1];
349          if (xMax < coordinates[i, 0]) xMax = coordinates[i, 0];
350          if (yMax < coordinates[i, 1]) yMax = coordinates[i, 1];
351        }
352
353        int border = 20;
354        double xStep = xMax != xMin ? (pictureBox.Width - 2 * border) / (xMax - xMin) : 1;
355        double yStep = yMax != yMin ? (pictureBox.Height - 2 * border) / (yMax - yMin) : 1;
356
357        Point[] points = new Point[coordinates.Rows];
358        for (int i = 0; i < coordinates.Rows; i++)
359          points[i] = new Point(border + ((int)((coordinates[i, 0] - xMin) * xStep)),
360                                newBitmap.Height - (border + ((int)((coordinates[i, 1] - yMin) * yStep))));
361
362        Random rand = new Random();
363        using (Graphics graphics = Graphics.FromImage(newBitmap)) {
[7092]364          graphics.Clear(Color.White);
[7769]365          graphics.DrawString("Showing facilities layed out according to their weights", Font, Brushes.Black, 5, 2);
[5932]366
367          for (int i = 0; i < coordinates.Rows - 1; i++) {
368            for (int j = i + 1; j < coordinates.Rows; j++) {
369              Point start = points[i], end = points[j];
370              string caption = String.Empty;
371              double d = Math.Max(distances[i, j], distances[j, i]);
372              double w = weights[i, j];
373              if (w > 0) {
374                float width = (float)Math.Ceiling(3.0 * w / maxWeight);
375                graphics.DrawLine(new Pen(Color.MediumBlue, width), start, end);
376                caption = w.ToString(CultureInfo.InvariantCulture.NumberFormat);
377              }
378              if (!String.IsNullOrEmpty(caption)) {
379                double r = rand.NextDouble();
380                while (r < 0.2 || r > 0.8) r = rand.NextDouble();
381                float x = (float)(start.X + (end.X - start.X) * r + 5);
382                float y = (float)(start.Y + (end.Y - start.Y) * r + 5);
383                graphics.DrawString(caption, Font, Brushes.Black, x, y);
384              }
385            }
386          }
387          for (int i = 0; i < points.Length; i++) {
388            Point p = new Point(points[i].X - 3, points[i].Y - 3);
389            graphics.FillRectangle(Brushes.Black, p.X, p.Y, 8, 8);
390            graphics.DrawString(i.ToString(), Font, Brushes.Black, p.X, p.Y + 10);
391          }
392        }
393        showingMessage = false;
394        return newBitmap;
395      }
396      return null;
397    }
398    #endregion
399
400    #region Draw assignment
401    private Bitmap GenerateAssignmentImage() {
402      if ((pictureBox.Width > 0) && (pictureBox.Height > 0)) {
403        Bitmap newBitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
404
405        for (int i = 0; i < distances.Rows; i++) {
406          for (int j = i + 1; j < distances.Rows; j++) {
407            if (distances[i, j] != distances[j, i]) {
408              WriteCenteredTextToBitmap(ref newBitmap, "Distance matrix is not symmetric");
409              stressLabel.Text = "-";
410              showingMessage = true;
411              return newBitmap;
412            }
413            if (weights[i, j] != weights[j, i]) {
414              WriteCenteredTextToBitmap(ref newBitmap, "Weights matrix is not symmetric");
415              stressLabel.Text = "-";
416              showingMessage = true;
417              return newBitmap;
418            }
419          }
420        }
421
422        DoubleMatrix coordinates = null;
423        double stress = double.NaN;
424        try {
425          coordinates = MultidimensionalScaling.KruskalShepard(distances);
426          stress = MultidimensionalScaling.CalculateNormalizedStress(distances, coordinates);
427          stressLabel.Text = stress.ToString("0.00", CultureInfo.CurrentCulture.NumberFormat);
[7416]428          if (stress < 0.1) stressLabel.ForeColor = Color.DarkGreen;
429          else if (stress < 0.2) stressLabel.ForeColor = Color.DarkOrange;
430          else stressLabel.ForeColor = Color.DarkRed;
[5932]431        } catch {
432          WriteCenteredTextToBitmap(ref newBitmap, "Unknown error");
433          showingMessage = true;
434          stressLabel.Text = "-";
[7416]435          stressLabel.ForeColor = Color.Black;
[5932]436          return newBitmap;
437        }
438
439        double xMin = double.MaxValue, yMin = double.MaxValue, xMax = double.MinValue, yMax = double.MinValue;
440        double maxWeight = double.MinValue;
441        for (int i = 0; i < coordinates.Rows; i++) {
442          if (xMin > coordinates[i, 0]) xMin = coordinates[i, 0];
443          if (yMin > coordinates[i, 1]) yMin = coordinates[i, 1];
444          if (xMax < coordinates[i, 0]) xMax = coordinates[i, 0];
445          if (yMax < coordinates[i, 1]) yMax = coordinates[i, 1];
446
447          for (int j = i + 1; j < coordinates.Rows; j++) {
448            if (weights[i, j] > maxWeight) maxWeight = weights[i, j];
449          }
450        }
451
452        int border = 20;
453        double xStep = xMax != xMin ? (pictureBox.Width - 2 * border) / (xMax - xMin) : 1;
454        double yStep = yMax != yMin ? (pictureBox.Height - 2 * border) / (yMax - yMin) : 1;
455
456        Point[] points = new Point[coordinates.Rows];
457        for (int i = 0; i < coordinates.Rows; i++)
458          points[i] = new Point(border + ((int)((coordinates[i, 0] - xMin) * xStep)),
459                                newBitmap.Height - (border + ((int)((coordinates[i, 1] - yMin) * yStep))));
460
461        Random rand = new Random();
462        using (Graphics graphics = Graphics.FromImage(newBitmap)) {
[7092]463          graphics.Clear(Color.White);
[5932]464          for (int i = 0; i < assignment.Length - 1; i++) {
465            for (int j = i + 1; j < assignment.Length; j++) {
466              Point start, end;
467              string caption = String.Empty;
468              double d = distances[i, j];
469              start = points[assignment[i]];
470              end = points[assignment[j]];
471              double w = weights[i, j];
472              if (w > 0) {
473                float width = (float)Math.Ceiling(4.0 * w / maxWeight);
474                graphics.DrawLine(new Pen(Color.MediumBlue, width), start, end);
475                caption = w.ToString(CultureInfo.InvariantCulture.NumberFormat);
476              }
477              if (!String.IsNullOrEmpty(caption)) {
478                double r = rand.NextDouble();
479                while (r < 0.2 || r > 0.8) r = rand.NextDouble();
480                float x = (float)(start.X + (end.X - start.X) * r + 5);
481                float y = (float)(start.Y + (end.Y - start.Y) * r + 5);
482                graphics.DrawString(caption, Font, Brushes.Black, x, y);
483              }
484            }
485          }
486
487          for (int i = 0; i < points.Length; i++) {
488            Point p = new Point(points[i].X - 3, points[i].Y - 3);
489            graphics.FillRectangle(Brushes.Black, p.X, p.Y, 8, 8);
490            graphics.DrawString(i.ToString(), Font, Brushes.Black, p.X, p.Y + 10);
491          }
492        }
493        showingMessage = false;
494        return newBitmap;
495      }
496      return null;
497    }
498    #endregion
499
500    private void CustomDispose(bool disposing) {
501      DeregisterAssignmentEvents();
502      if (bitmap != null) bitmap.Dispose();
503      bitmap = null;
504      if (defaultBitmap != null) {
505        defaultBitmap.Dispose();
506        defaultBitmap = null;
507      }
508    }
509  }
510}
Note: See TracBrowser for help on using the repository browser.