1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.ComponentModel;
|
---|
4 | using System.Drawing;
|
---|
5 | using System.Linq;
|
---|
6 | using System.Windows.Forms;
|
---|
7 |
|
---|
8 | namespace HeuristicLab.Analysis.FitnessLandscape.FDC {
|
---|
9 | public partial class HeatMapControl : Control {
|
---|
10 |
|
---|
11 | public const int N_BINS = 50;
|
---|
12 |
|
---|
13 | public HeatMapControl() {
|
---|
14 | InitializeComponent();
|
---|
15 | }
|
---|
16 |
|
---|
17 | private List<PointF> points = new List<PointF>();
|
---|
18 |
|
---|
19 | public void AddPoints(IEnumerable<PointF> points) {
|
---|
20 | this.points.AddRange(points);
|
---|
21 | ForceUpdate();
|
---|
22 | }
|
---|
23 |
|
---|
24 | public void Clear() {
|
---|
25 | points.Clear();
|
---|
26 | ForceUpdate();
|
---|
27 | }
|
---|
28 |
|
---|
29 | private Bitmap bitmap = null;
|
---|
30 |
|
---|
31 | protected override void OnPaint(PaintEventArgs pe) {
|
---|
32 | if (bitmap != null)
|
---|
33 | pe.Graphics.DrawImage(bitmap, new Rectangle(0, 0, Width, Height));
|
---|
34 | base.OnPaint(pe);
|
---|
35 | }
|
---|
36 |
|
---|
37 | public static RectangleF CreateBounds(IEnumerable<PointF> points, BackgroundWorker worker, DoWorkEventArgs a) {
|
---|
38 | if (points.Count() == 0)
|
---|
39 | return new RectangleF(0, 0, 1, 1);
|
---|
40 | double minX = points.Select(p => p.X).Min();
|
---|
41 | if (worker.CancellationPending) {
|
---|
42 | a.Cancel = true;
|
---|
43 | return new RectangleF(0, 0, 1, 1);
|
---|
44 | }
|
---|
45 | double maxX = points.Select(p => p.X).Max();
|
---|
46 | if (worker.CancellationPending) {
|
---|
47 | a.Cancel = true;
|
---|
48 | return new RectangleF(0, 0, 1, 1);
|
---|
49 | }
|
---|
50 | double minY = points.Select(p => p.Y).Min();
|
---|
51 | if (worker.CancellationPending) {
|
---|
52 | a.Cancel = true;
|
---|
53 | return new RectangleF(0, 0, 1, 1);
|
---|
54 | }
|
---|
55 | double maxY = points.Select(p => p.Y).Max();
|
---|
56 | if (worker.CancellationPending) {
|
---|
57 | a.Cancel = true;
|
---|
58 | return new RectangleF(0, 0, 1, 1);
|
---|
59 | }
|
---|
60 | return new RectangleF((float)minX, (float)minY, (float)(maxX - minX), (float)(maxY - minY));
|
---|
61 | }
|
---|
62 |
|
---|
63 | public static double[,] CreateHeapMap(IEnumerable<PointF> points, int hBins, int vBins, BackgroundWorker worker, DoWorkEventArgs a) {
|
---|
64 | RectangleF bounds = CreateBounds(points, worker, a);
|
---|
65 | MatrixBuffer<int> countMap = new MatrixBuffer<int>(hBins, vBins);
|
---|
66 | foreach (PointF p in points) {
|
---|
67 | if (worker.CancellationPending) {
|
---|
68 | a.Cancel = true;
|
---|
69 | return null;
|
---|
70 | }
|
---|
71 | int x = (int)Math.Round((hBins - 1) * (p.X - bounds.X) / bounds.Width);
|
---|
72 | int y = (int)Math.Round((vBins - 1) * (p.Y - bounds.Y) / bounds.Height);
|
---|
73 | countMap[x, y] += 1;
|
---|
74 | }
|
---|
75 | int maxCount = countMap.Max();
|
---|
76 | double[,] heatMap = new double[hBins, vBins];
|
---|
77 | if (maxCount > 0) {
|
---|
78 | for (int x = 0; x < hBins; x++) {
|
---|
79 | for (int y = 0; y < vBins; y++) {
|
---|
80 | if (worker.CancellationPending) {
|
---|
81 | a.Cancel = true;
|
---|
82 | return null;
|
---|
83 | }
|
---|
84 | heatMap[x, y] = 1.0 * countMap[x, y] / maxCount;
|
---|
85 | }
|
---|
86 | }
|
---|
87 | }
|
---|
88 | return heatMap;
|
---|
89 | }
|
---|
90 |
|
---|
91 | private static Bitmap CreateBitmap(IEnumerable<PointF> points, int width, int height, BackgroundWorker worker, DoWorkEventArgs a) {
|
---|
92 | ColorGradient gradient = new ColorGradient();
|
---|
93 | gradient[0.0] = Color.Blue;
|
---|
94 | gradient[0.5] = Color.Green;
|
---|
95 | gradient[1.0] = Color.Red;
|
---|
96 | var heatMap = CreateHeapMap(points, N_BINS, N_BINS, worker, a);
|
---|
97 | double binWidth = 1.0 * width / N_BINS;
|
---|
98 | double binHeight = 1.0 * height / N_BINS;
|
---|
99 | if (worker.CancellationPending) {
|
---|
100 | a.Cancel = true;
|
---|
101 | return new Bitmap(1, 1);
|
---|
102 | }
|
---|
103 | Bitmap bitmap = new Bitmap(width, height);
|
---|
104 | using (Graphics g = Graphics.FromImage(bitmap)) {
|
---|
105 | for (int x = 0; x < heatMap.GetLength(0); x++) {
|
---|
106 | for (int y = 0; y < heatMap.GetLength(1); y++) {
|
---|
107 | if (worker.CancellationPending) {
|
---|
108 | a.Cancel = true;
|
---|
109 | break;
|
---|
110 | }
|
---|
111 | using (Brush b = new SolidBrush(gradient[heatMap[x, y]])) {
|
---|
112 | g.FillRectangle(b, (float)(x * binWidth), (float)(y * binHeight), (float)binWidth, (float)binHeight);
|
---|
113 | }
|
---|
114 | }
|
---|
115 | }
|
---|
116 | g.Flush();
|
---|
117 | }
|
---|
118 | return bitmap;
|
---|
119 | }
|
---|
120 |
|
---|
121 | private void HeatMapControl_Resize(object sender, EventArgs e) {
|
---|
122 | if (bitmap == null || bitmap.Width < Width || bitmap.Height < bitmap.Height)
|
---|
123 | ForceUpdate();
|
---|
124 | }
|
---|
125 |
|
---|
126 | BackgroundWorker worker;
|
---|
127 | object workerLock = new object();
|
---|
128 |
|
---|
129 | public void ForceUpdate() {
|
---|
130 | lock (workerLock) {
|
---|
131 | if (worker != null)
|
---|
132 | worker.CancelAsync();
|
---|
133 | worker = new BackgroundWorker() {
|
---|
134 | WorkerSupportsCancellation = true
|
---|
135 | };
|
---|
136 | Bitmap bitmap = null;
|
---|
137 | IEnumerable<PointF> points = this.points.ToList();
|
---|
138 | int width, height;
|
---|
139 | width = height = Math.Min(Width, Height);
|
---|
140 | worker.DoWork += (s, a) => {
|
---|
141 | bitmap = CreateBitmap(points, width, height, worker, a);
|
---|
142 | };
|
---|
143 | worker.RunWorkerCompleted += (s, a) => {
|
---|
144 | if (!a.Cancelled) {
|
---|
145 | SetBitmap(bitmap);
|
---|
146 | lock (workerLock) {
|
---|
147 | this.worker = null;
|
---|
148 | }
|
---|
149 | }
|
---|
150 | };
|
---|
151 | worker.RunWorkerAsync();
|
---|
152 | }
|
---|
153 | }
|
---|
154 |
|
---|
155 | private void SetBitmap(Bitmap bitmap) {
|
---|
156 | if (InvokeRequired) {
|
---|
157 | Invoke(new Action<Bitmap>(SetBitmap));
|
---|
158 | } else {
|
---|
159 | this.bitmap = bitmap;
|
---|
160 | Invalidate();
|
---|
161 | }
|
---|
162 | }
|
---|
163 |
|
---|
164 | }
|
---|
165 |
|
---|
166 | public class MatrixBuffer<T> : IEnumerable<T> {
|
---|
167 |
|
---|
168 | private T[,] matrix;
|
---|
169 |
|
---|
170 | public MatrixBuffer(int x, int y) {
|
---|
171 | matrix = new T[x, y];
|
---|
172 | }
|
---|
173 |
|
---|
174 | public T this[int x, int y] {
|
---|
175 | get {
|
---|
176 | if (IsInside(x, y))
|
---|
177 | return matrix[x, y];
|
---|
178 | else
|
---|
179 | return default(T);
|
---|
180 | }
|
---|
181 | set {
|
---|
182 | if (IsInside(x, y))
|
---|
183 | matrix[x, y] = value;
|
---|
184 | }
|
---|
185 | }
|
---|
186 |
|
---|
187 | public bool IsInside(int x, int y) {
|
---|
188 | return x >= 0 && x < matrix.GetLength(0) && y >= 0 && y < matrix.GetLength(1);
|
---|
189 | }
|
---|
190 |
|
---|
191 |
|
---|
192 | #region IEnumerable<T> Members
|
---|
193 |
|
---|
194 | public IEnumerator<T> GetEnumerator() {
|
---|
195 | foreach (T e in matrix)
|
---|
196 | yield return e;
|
---|
197 | }
|
---|
198 |
|
---|
199 | #endregion
|
---|
200 |
|
---|
201 | #region IEnumerable Members
|
---|
202 |
|
---|
203 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
|
---|
204 | return matrix.GetEnumerator();
|
---|
205 | }
|
---|
206 |
|
---|
207 | #endregion
|
---|
208 | }
|
---|
209 | }
|
---|