Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.EvolutionTracking/HeuristicLab.EvolutionTracking.Views/3.4/GenealogyGraphChart.cs @ 10833

Last change on this file since 10833 was 10833, checked in by bburlacu, 10 years ago

#1772: Fixed another minor display bug concerning elite individuals. Fixed bug when saving fragments from mutation. Displayed quality as well in the SymbolicExpressionTreeTile.

File size: 11.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 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.Collections.Generic;
24using System.Drawing;
25using System.Drawing.Drawing2D;
26using System.Linq;
27using System.Windows.Forms;
28using HeuristicLab.Common;
29using HeuristicLab.Visualization;
30
31namespace HeuristicLab.EvolutionTracking.Views {
32  public partial class GenealogyGraphChart : ChartControl {
33    private IGenealogyGraph genealogyGraph;
34
35    private const double XIncrement = 30;
36    private const double YIncrement = 30;
37    private const double Diameter = 20;
38
39    private readonly Brush defaultBrush;
40    private readonly Pen defaultPen;
41
42    public IGenealogyGraph GenealogyGraph {
43      get { return genealogyGraph; }
44      set {
45        if (value == null) return;
46        genealogyGraph = value;
47        Clear();
48        DrawGraph(XIncrement, YIncrement, Diameter);
49      }
50    }
51
52    public IGenealogyGraphNode SelectedGraphNode { get; private set; }
53
54    private void Clear() {
55      nodeMap = new Dictionary<IGenealogyGraphNode, VisualGenealogyGraphNode>();
56      arcMap = new Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc>();
57
58      Chart.Group.Clear();
59    }
60
61    private Dictionary<IGenealogyGraphNode, VisualGenealogyGraphNode> nodeMap;
62    private Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc> arcMap;
63
64    #region chart modes
65    public bool SimpleLineages { get; set; }
66    public bool LockGenealogy { get; set; }
67    public bool TraceFragments { get; set; }
68    #endregion
69
70    private Visualization.Rectangle TargetRectangle { get; set; }
71    private bool DrawInProgress { get; set; } // do not try to update the chart while the drawing is not finished
72    private VisualGenealogyGraphNode SelectedVisualNode { get; set; }
73
74    private VisualGenealogyGraphNode GetMappedNode(IGenealogyGraphNode node) {
75      VisualGenealogyGraphNode v;
76      nodeMap.TryGetValue(node, out v);
77      return v;
78    }
79
80    private VisualGenealogyGraphArc GetMappedArc(IGenealogyGraphNode source, IGenealogyGraphNode target) {
81      VisualGenealogyGraphNode visualSource, visualTarget;
82      nodeMap.TryGetValue(source, out visualSource);
83      nodeMap.TryGetValue(target, out visualTarget);
84
85      if (visualSource == null || visualTarget == null) return null;
86
87      VisualGenealogyGraphArc arc;
88      arcMap.TryGetValue(new Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>(visualSource, visualTarget), out arc);
89      return arc;
90    }
91
92    public GenealogyGraphChart()
93      : base() {
94      InitializeComponent();
95
96      defaultBrush = new SolidBrush(Color.Transparent);
97      defaultPen = new Pen(Color.DarkGray);
98    }
99
100    protected virtual void DrawGraph(double xIncrement, double yIncrement, double diameter) {
101      Chart.UpdateEnabled = false;
102      DrawInProgress = true;
103
104      var ranks = GenealogyGraph.Ranks.Select(t => new { Rank = t.Key, Nodes = t.Value }).OrderBy(p => p.Rank).ToList();
105      double x = 0;
106      double y = PreferredSize.Height + yIncrement + 2 * diameter;
107
108      foreach (var rank in ranks) {
109        var nodes = rank.Nodes.ToList();
110        nodes.Sort((a, b) => b.CompareTo(a)); // sort descending by quality
111        var nl = Environment.NewLine;
112
113        foreach (var node in nodes) {
114          var brush = new SolidBrush(node.GetColor());
115          var visualNode = new VisualGenealogyGraphNode(Chart, x, y, x + diameter, y + diameter, defaultPen, brush) {
116            Data = node,
117            ToolTipText = "Rank: " + node.Rank + nl +
118                          "Quality: " + String.Format("{0:0.0000}", node.Quality) + nl +
119                          "IsElite: " + node.IsElite
120          };
121          Chart.Group.Add(visualNode);
122          nodeMap.Add(node, visualNode);
123
124          x += xIncrement;
125        }
126        y -= yIncrement;
127        x = 0;
128      }
129
130      // add arcs
131      foreach (var node in GenealogyGraph.Nodes) {
132        if (!node.InArcs.Any()) continue;
133        var visualNode = GetMappedNode(node);
134
135        if (visualNode == null) continue;
136        foreach (var arc in node.InArcs) {
137          var parent = arc.Source;
138          var visualParent = GetMappedNode(parent);
139          if (visualParent == null) continue;
140          var pen = Pens.Transparent;
141          var visualArc = AddArc(Chart, visualParent, visualNode, pen);
142          visualArc.OneLayerDown(); // send it behind the visual nodes
143          if (!arcMap.ContainsKey(Tuple.Create(visualParent, visualNode))) {
144            arcMap.Add(Tuple.Create(visualParent, visualNode), visualArc);
145          }
146        }
147      }
148      // TODO: connect elites
149
150      Chart.UpdateEnabled = true;
151      Chart.EnforceUpdate();
152
153      DrawInProgress = false;
154    }
155
156    public event MouseEventHandler GenealogyGraphNodeClicked;
157    private void OnGenealogyGraphNodeClicked(object sender, MouseEventArgs e) {
158      var clicked = GenealogyGraphNodeClicked;
159      if (clicked != null) clicked(sender, e);
160    }
161
162    #region event handlers
163    protected override void pictureBox_MouseMove(object sender, MouseEventArgs e) {
164      if (!DrawInProgress) {
165        switch (e.Button) {
166          case MouseButtons.Left:
167            Chart.Mode = ChartMode.Select;
168            Cursor = Cursors.Default;
169            break;
170          case MouseButtons.Middle:
171            Chart.Mode = ChartMode.Move;
172            Cursor = Cursors.Hand;
173            break;
174        }
175      }
176      base.pictureBox_MouseMove(sender, e);
177    }
178    protected override void pictureBox_MouseUp(object sender, MouseEventArgs e) {
179      Cursor = Cursors.Default;
180      if (Chart.Mode == ChartMode.Move) {
181        Chart.Mode = ChartMode.Select;
182        return;
183      }
184      if (Chart.Mode != ChartMode.Select) {
185        base.pictureBox_MouseUp(sender, e);
186        return;
187      }
188      var visualNodes = Chart.GetAllPrimitives(e.Location).Where(p => p is VisualGenealogyGraphNode).ToList();
189      if (!visualNodes.Any()) {
190        SelectedVisualNode = null;
191        return;
192      }
193      if (SelectedVisualNode == visualNodes[0]) return;
194      SelectedVisualNode = visualNodes[0] as VisualGenealogyGraphNode;
195      if (SelectedVisualNode == null) return;
196      SelectedGraphNode = SelectedVisualNode.Data;
197
198      if (!LockGenealogy) {
199        // new node has been selected, clean up
200        Chart.UpdateEnabled = false;
201        if (ModifierKeys != Keys.Shift) {
202          // clear colors
203          ClearPrimitives();
204        }
205        // use a rectangle to highlight the currently selected genealogy graph node
206        var graphNode = SelectedVisualNode.Data;
207        var visualNode = GetMappedNode(graphNode);
208
209        DrawLineage(visualNode, n => SimpleLineages ? n.IncomingArcs.Take(1) : n.IncomingArcs, a => a.Source);
210        DrawLineage(visualNode, n => n.OutgoingArcs, a => a.Target);
211      }
212
213      MarkSelectedNode();
214
215      // update
216      Chart.UpdateEnabled = true;
217      Chart.EnforceUpdate();
218
219      if (SelectedVisualNode != null)
220        /* emit clicked event */
221        OnGenealogyGraphNodeClicked(SelectedVisualNode, e);
222
223      base.pictureBox_MouseUp(sender, e);
224    }
225    #endregion
226
227    private static void DrawLineage(VisualGenealogyGraphNode node, Func<VisualGenealogyGraphNode, IEnumerable<VisualGenealogyGraphArc>> arcSelector, Func<VisualGenealogyGraphArc, VisualGenealogyGraphNode> nodeSelector) {
228      var brush = (SolidBrush)node.Brush;
229      if (brush.Color != Color.Transparent) return; // this lineage was already drawn (avoid redrawing common ancestors)
230      brush.Color = node.Data.GetColor();
231      var arcs = arcSelector(node);
232      var pen = new Pen(Color.Transparent);
233      foreach (var arc in arcs) {
234        var source = arc.Source.Data;
235        var target = arc.Target.Data;
236        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
237        var end = new Point((int)arc.End.X, (int)arc.End.Y);
238        arc.Pen = pen;
239        arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
240        DrawLineage(nodeSelector(arc), arcSelector, nodeSelector);
241      }
242    }
243
244    void MarkSelectedNode() {
245      var center = SelectedVisualNode.Center;
246      var size = SelectedVisualNode.Size;
247      double x1 = center.X - size.Width / 2;
248      double x2 = x1 + size.Width;
249      double y1 = center.Y - size.Height / 2;
250      double y2 = y1 + size.Height;
251      if (TargetRectangle == null) {
252        TargetRectangle = new Visualization.Rectangle(Chart, x1, y1, x2, y2, new Pen(Color.Black), null);
253        Chart.Group.Add(TargetRectangle);
254      } else {
255        TargetRectangle.SetPosition(x1, y1, x2, y2);
256      }
257    }
258
259    private static VisualGenealogyGraphArc AddArc(IChart chart, VisualGenealogyGraphNode source, VisualGenealogyGraphNode target, Pen pen, Brush brush = null) {
260      var arc = new VisualGenealogyGraphArc(chart, source, target, pen) { Brush = brush };
261      arc.UpdatePosition();
262      source.OutgoingArcs.Add(arc);
263      target.IncomingArcs.Add(arc);
264      chart.Group.Add(arc);
265      return arc;
266    }
267
268    public virtual void ClearPrimitives() {
269      foreach (var primitive in Chart.Group.Primitives) {
270        if (primitive is VisualGenealogyGraphArc) {
271          primitive.Pen = Pens.Transparent;
272        } else if (primitive is VisualGenealogyGraphNode) {
273          primitive.Brush = new SolidBrush(Color.Transparent);
274          primitive.Pen = new Pen(Color.DarkGray);
275        }
276      }
277    }
278
279    public void HighlightNodes(IEnumerable<IGenealogyGraphNode> nodes) {
280      Chart.UpdateEnabled = false;
281      ClearPrimitives();
282      foreach (var node in nodes) {
283        var graphNode = GetMappedNode(node);
284        graphNode.Brush = new SolidBrush(node.GetColor());
285      }
286      Chart.UpdateEnabled = true;
287      Chart.EnforceUpdate();
288    }
289
290    public void HighlightAll() {
291      Chart.UpdateEnabled = false;
292      foreach (var visualNode in nodeMap.Values) {
293        visualNode.Brush = new SolidBrush(visualNode.Data.GetColor());
294      }
295      foreach (var arc in arcMap.Values) {
296        var source = arc.Source.Data;
297        var target = arc.Target.Data;
298        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
299        var end = new Point((int)arc.End.X, (int)arc.End.Y);
300        arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
301      }
302      Chart.UpdateEnabled = true;
303      Chart.EnforceUpdate();
304    }
305  }
306
307  internal static class Util {
308    public static Color GetColor(this IGenealogyGraphNode node) {
309      var colorIndex = (int)Math.Round(node.Quality * ColorGradient.Colors.Count);
310      if (colorIndex >= ColorGradient.Colors.Count) return ColorGradient.Colors.Last();
311      return ColorGradient.Colors[colorIndex];
312    }
313  }
314}
Note: See TracBrowser for help on using the repository browser.