1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using TSP;
namespace Plotter
{
public class TourPlot
{
public string GraphName { get; set; }
public Tour Tour { get; set; }
public PictureBox Canvas { get; set; }
public Label WeightLabel { get; set; }
public Label RuntimeLabel { get; set; }
public TimeSpan Runtime { get; set; }
public TourPlot(string graphName, PictureBox canvas, Label weightLabel, Label runtimeLabel)
{
GraphName = graphName;
Canvas = canvas;
WeightLabel = weightLabel;
RuntimeLabel = runtimeLabel;
}
public void Save(string filename) =>
Save(filename, Canvas.Width, Canvas.Height);
public void Save(string filename, int width, int height)
{
Draw(300, 300).Save($"{filename}.png");
File.WriteAllText($"{filename}.txt", $"{this}");
}
public void Render()
{
Draw(Canvas.Width, Canvas.Height);
// copy the bitmap to the picturebox (double buffered)
Canvas.Image?.Dispose();
Canvas.Image = Draw(Canvas.Width, Canvas.Height);
// set runtime and weight labels
RuntimeLabel.Text = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
Runtime.Hours, Runtime.Minutes, Runtime.Seconds, Runtime.Milliseconds / 10);
WeightLabel.Text = Tour.Weight.ToString("F3");
}
public Bitmap Draw(int width, int height)
{
const int Offset = 25;
const int PlotSize = 100;
using Bitmap bmp = new(width, height);
using Graphics gfx = Graphics.FromImage(bmp);
using Pen pen = new(Color.Black);
using Pen gridPen = new(Color.LightGray);
using Font font = new("Arial", 16);
using Font gridFont = new("Arial", 10);
using SolidBrush brush = new(Color.Black);
using StringFormat stringFormat = new StringFormat();
gfx.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
gfx.Clear(Color.White);
for (int i = 0; i <= 100; i += 10)
{
Point left = ScaleLocation(new Coordinate(0, i));
Point right = ScaleLocation(new Coordinate(100, i));
gfx.DrawLine(gridPen, left, right);
stringFormat.Alignment = StringAlignment.Far;
stringFormat.LineAlignment = StringAlignment.Center;
gfx.DrawString(i.ToString(), gridFont, brush, left, stringFormat);
Point bottom = ScaleLocation(new Coordinate(i, 0));
Point top = ScaleLocation(new Coordinate(i, 100));
gfx.DrawLine(gridPen, bottom, top);
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Near;
gfx.DrawString(i.ToString(), gridFont, brush, bottom, stringFormat);
}
// iterate through each city and draw line from Location to parent.Location
foreach (City city in Tour.Cities.Where(city => city.Parent != -1))
{
City parent = Tour[city.Parent];
Point cityPoint = ScaleLocation(city.Location);
Point parentPoint = ScaleLocation(parent.Location);
gfx.DrawString(city.Id.ToString(), font, brush, cityPoint);
gfx.DrawLine(pen, cityPoint, parentPoint);
}
return (Bitmap)bmp.Clone();
// helper function to unify the scaling of images
Point ScaleLocation(Coordinate coordinate)
{
int x = width * (coordinate.X + Offset / 2) / (Offset + PlotSize);
int y = height * ((coordinate.Y - PlotSize) * -1 + Offset / 2) / (Offset + PlotSize);
return new Point(x, y);
}
}
public override string ToString() => $"{Tour}\n" +
$"runtime: {Runtime}";
}
}
|