Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.QuadraticAssignment.Views/3.3/QAPVisualizationControl.cs @ 6583

Last change on this file since 6583 was 5933, checked in by abeham, 14 years ago

#1330

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