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

Last change on this file since 7259 was 7259, checked in by swagner, 9 years ago

Updated year of copyrights to 2012 (#1716)

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