Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.EvolutionaryTracking/HeuristicLab.EvolutionaryTracking.Views/3.4/GenealogyGraphChart.cs @ 8249

Last change on this file since 8249 was 8249, checked in by bburlacu, 12 years ago

#1772: Fixed small bug (correct counting of node ranks) in the SymbolicExpressionTreeGenealogyAnalyzer. Fixed node tool tips in the GenealogyGraphChart.

File size: 13.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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.Encodings.SymbolicExpressionTreeEncoding;
30using HeuristicLab.Visualization;
31
32namespace HeuristicLab.EvolutionaryTracking.Views {
33  public partial class GenealogyGraphChart : ChartControl {
34    private Dictionary<GenealogyGraphNode, List<VisualGenealogyGraphNode>> _visualNodeMap;
35    private Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc> _visualArcMap;
36    private const int Diameter = 20; // node diameter
37    private int x, y; // coordinates for positioning each node
38    private const int Cx = 30, Cy = 30; // position increments
39    private VisualGenealogyGraphNode _selectedGenealogyGraphNode;
40    public VisualGenealogyGraphNode SelectedGenealogyGraphNode { get { return _selectedGenealogyGraphNode; } }
41    private Visualization.Rectangle _targetRectangle; // provides a  rectangle mark of the currently selected genealogy graph node
42
43    private SymbolicExpressionTreeGenealogyGraph _graph;
44    public SymbolicExpressionTreeGenealogyGraph Graph {
45      get { return _graph; }
46      internal set {
47        _graph = value;
48        _visualNodeMap = new Dictionary<GenealogyGraphNode, List<VisualGenealogyGraphNode>>();
49        _visualArcMap = new Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc>();
50        DrawGraph();
51      }
52    }
53
54    public event MouseEventHandler GenealogyGraphNodeClicked;
55    private void OnGenealogyGraphNodeClicked(object sender, MouseEventArgs e) {
56      var clicked = GenealogyGraphNodeClicked;
57      if (clicked != null) clicked(sender, e);
58    }
59
60    public GenealogyGraphChart() {
61      Chart = new Chart(0, 0, PreferredSize.Width, PreferredSize.Height);
62      x = 0;
63      y = PreferredSize.Height + Cx + 2 * Diameter;
64    }
65
66    public void DrawGraph() {
67      if (_graph == null) return;
68      Chart.UpdateEnabled = false;
69
70      var genealogyGraphNodes = Graph.Values.ToList();
71      var layers = genealogyGraphNodes.GroupBy(n => Graph[n].Ranks[0]).OrderBy(g => g.Key).Select(g => new { Rank = g.Key, Nodes = g.ToList() }).ToList();
72      List<GenealogyGraphNode> currentLayer = null;
73      List<GenealogyGraphNode> previousLayer = null;
74
75      for (int i = 0; i != layers.Count(); ++i) {
76        var layer = layers[i];
77        double currentRank = layer.Rank;
78        if (i > 0 && currentRank % 1 == 0) {
79          previousLayer = currentLayer;
80          currentLayer = layer.Nodes;
81          int d = previousLayer.Count - currentLayer.Count;
82          if (d > 0)
83            currentLayer.AddRange(previousLayer.Take(d));
84        } else {
85          currentLayer = layer.Nodes;
86        }
87        currentLayer.Sort((b, a) => {
88          if (Graph[a].Quality.Equals(Graph[b].Quality)) {
89            if (Graph[a].IsElite) return 1;
90            if (Graph[b].IsElite) return -1;
91          }
92          return Graph[a].Quality.CompareTo(Graph[b].Quality);
93        }); // sort descending by quality (using comparison defined in GenealogyGraphNode class)
94
95        x = 0; // reset horizontal coordinate
96        // start laying out visual nodes
97        foreach (var node in currentLayer) {
98          var pen = new Pen(Color.LightGray);
99          var nl = Environment.NewLine;
100          var visualNode = new VisualGenealogyGraphNode(Chart, x, y, x + Diameter, y + Diameter, pen, null) {
101            Data = node,
102            ToolTipText = "Id: " + node.Label + nl +
103                          "Ranks: " + string.Join(",", Graph[node].Ranks.Select(r => r.ToString())) + nl +
104                          "Quality: " + String.Format("{0:0.0000}", Graph[node].Quality) + nl +
105                          "IsElite: " + Graph[node].IsElite
106          };
107          Chart.Group.Add(visualNode);
108          if (!_visualNodeMap.ContainsKey(node))
109            _visualNodeMap[node] = new List<VisualGenealogyGraphNode>();
110          _visualNodeMap[node].Add(visualNode);
111
112          x += Cx; // increment horizontal coordinate
113        }
114
115        y -= Cy; // decrement vertical coordinate (because the origin is upside down)
116      }
117      // add arcs separately (to avoid some ordering problems)
118      foreach (var node in _visualNodeMap.Keys) {
119        var visualNode = _visualNodeMap[node][0];
120        if (node.InEdges == null) continue;
121
122        foreach (var arc in node.InEdges) {
123          var visualParent = _visualNodeMap[arc.Target][0];
124          var pen = new Pen(Color.Transparent);
125          _visualArcMap[Tuple.Create(visualParent, visualNode)] = AddArc(Chart, visualParent, visualNode, pen);
126        }
127      }
128      // connect visual nodes representing the same elite individual with a line
129      foreach (var list in _visualNodeMap.Values.Where(l => l.Count > 1)) {
130        for (int i = 1; i != list.Count; ++i) {
131          var pen = new Pen(Color.LightGray);
132          _visualArcMap[Tuple.Create(list[i - 1], list[i])] = AddArc(Chart, list[i - 1], list[i], pen);
133        }
134      }
135
136      Chart.UpdateEnabled = true;
137      Chart.EnforceUpdate();
138    }
139
140    // add an arc between the source and the target nodes, using the specified pen color
141    // adds the arc to the arc lists of both nodes and to the primitive group of the chart
142    private static VisualGenealogyGraphArc AddArc(IChart chart, VisualGenealogyGraphNode source, VisualGenealogyGraphNode target, Pen pen, Brush brush = null) {
143      var arc = new VisualGenealogyGraphArc(chart, source, target, pen) { Brush = brush };
144      arc.UpdatePosition();
145      source.OutgoingArcs.Add(arc);
146      target.IncomingArcs.Add(arc);
147      chart.Group.Add(arc);
148      return arc;
149    }
150
151    protected override void pictureBox_MouseUp(object sender, MouseEventArgs e) {
152      if (Chart.Mode != ChartMode.Select)
153        base.pictureBox_MouseUp(sender, e);
154      else { // select mode
155        // get selected node
156        var visualNodes = Chart.GetAllPrimitives(e.Location).Where(p => p is VisualGenealogyGraphNode).ToList();
157        if (visualNodes.Count > 0) {
158          if (_selectedGenealogyGraphNode == visualNodes[0]) return;
159          _selectedGenealogyGraphNode = visualNodes[0] as VisualGenealogyGraphNode;
160          if (_selectedGenealogyGraphNode == null) return;
161          // new node has been selected, clean up
162          Chart.UpdateEnabled = false;
163          // clear colors
164          ClearAllNodes();
165          // use a rectangle to highlight the currently selected genealogy graph node
166          var center = _selectedGenealogyGraphNode.Center;
167          var size = _selectedGenealogyGraphNode.Size;
168          double x1 = center.X - size.Width / 2;
169          double x2 = x1 + size.Width;
170          double y1 = center.Y - size.Height / 2;
171          double y2 = y1 + size.Height;
172          if (_targetRectangle == null) {
173            _targetRectangle = new Visualization.Rectangle(Chart, x1, y1, x2, y2, new Pen(Color.Black), null);
174            Chart.Group.Add(_targetRectangle);
175          } else {
176            _targetRectangle.SetPosition(x1, y1, x2, y2);
177          }
178          var gNode = _selectedGenealogyGraphNode.Data; // genealogy graph node (representing an individual in the population)
179          double rank = Graph[gNode].Ranks[0];
180          // ancestors
181          var ancestors = gNode.Ancestors().Where(n => Graph[n].Ranks.Last() < rank).ToList();
182          ancestors.Add(gNode);
183          // descendants
184          var descendants = gNode.Descendants().Where(n => Graph[n].Ranks.Last() > rank).ToList();
185          descendants.Add(gNode);
186          // highlight ancestors
187          foreach (var node in ancestors.Select(n => _visualNodeMap[n][0])) {
188            node.Brush = new SolidBrush(Graph[node.Data].GetColor());
189            if (node.IncomingArcs != null)
190              foreach (var arc in node.IncomingArcs) {
191                // check if, in case of elites, the target node is the first visual representation of the elite
192                // (for purposes of display consistency)
193                bool isFirst = arc.Source == _visualNodeMap[arc.Source.Data][0];
194                if (arc.Source.Data == arc.Target.Data || !isFirst) continue;
195                var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
196                var end = new Point((int)arc.End.X, (int)arc.End.Y);
197                arc.Pen.Brush = new LinearGradientBrush(start, end, Graph[arc.Source.Data].GetColor(), Graph[arc.Target.Data].GetColor());
198              }
199          }
200          // highlight descendants
201          foreach (var node in descendants.Select(n => _visualNodeMap[n][0])) {
202            node.Brush = new SolidBrush(Graph[node.Data].GetColor());
203            if (node.OutgoingArcs != null)
204              foreach (var arc in node.OutgoingArcs) {
205                // check if, in case of elites, the target node is the first visual representation of the elite
206                // (for purposes of display consistency)
207                bool isFirst = arc.Target == _visualNodeMap[arc.Target.Data][0];
208                if (arc.Source.Data == arc.Target.Data || !isFirst) continue;
209                var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
210                var end = new Point((int)arc.End.X, (int)arc.End.Y);
211                arc.Pen.Brush = new LinearGradientBrush(start, end, Graph[arc.Source.Data].GetColor(), Graph[arc.Target.Data].GetColor());
212              }
213          }
214        } else {
215          _selectedGenealogyGraphNode = null;
216        }
217        // update
218        Chart.UpdateEnabled = true;
219        Chart.EnforceUpdate();
220        if (_selectedGenealogyGraphNode != null)
221          /* emit clicked event */
222          OnGenealogyGraphNodeClicked(_selectedGenealogyGraphNode, e);
223      }
224    }
225
226    public void ClearAllNodes() {
227      foreach (var primitive in Chart.Group.Primitives) {
228        primitive.Brush = null;
229        if (primitive is VisualGenealogyGraphArc) {
230          var arc = primitive as VisualGenealogyGraphArc;
231          if (arc.Source.Data != arc.Target.Data && primitive.Pen.Brush != null)
232            primitive.Pen.Brush = new SolidBrush(Color.Transparent);
233        }
234      }
235    }
236
237    public void HighlightLineage(IEnumerable<GenealogyGraphNode> nodes) {
238      foreach (var node in nodes.SelectMany(n => _visualNodeMap[n])) {
239        node.Brush = new SolidBrush(Graph[node.Data].GetColor());
240        if (node.IncomingArcs == null || node != _visualNodeMap[node.Data][0]) continue;
241        foreach (var arc in node.IncomingArcs) {
242          if (arc.Source.Data == node.Data) continue;
243          var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
244          var end = new Point((int)arc.End.X, (int)arc.End.Y);
245          arc.Pen.Brush = new LinearGradientBrush(start, end, Graph[arc.Source.Data].GetColor(), Graph[arc.Target.Data].GetColor());
246        }
247      }
248    }
249
250    public void HighlightNodes(IEnumerable<ISymbolicExpressionTree> trees, Color color) {
251      foreach (var visualNode in trees.Select(tree => _visualNodeMap[Graph.GetNode(tree)]).SelectMany(vList => vList))
252        visualNode.Brush = new SolidBrush(color);
253    }
254
255    public void HighlightInDegree() {
256      Chart.UpdateEnabled = false;
257      ClearAllNodes();
258      var graphNodes = Graph.Values.ToList();
259      double min = graphNodes.Min(x => x.InEdges == null ? 0 : x.InEdges.Count);
260      double max = graphNodes.Max(x => x.InEdges == null ? 0 : x.InEdges.Count);
261      foreach (var graphNode in graphNodes) {
262        var visualNode = _visualNodeMap[graphNode][0];
263        double deg = graphNode.InEdges == null ? 0 : graphNode.InEdges.Count;
264        var color = Color.FromArgb((int)(1 - deg / max) * 255, (int)(deg / max * 255), 100);
265        visualNode.Brush = new SolidBrush(color);
266      }
267      Chart.UpdateEnabled = true;
268      Chart.EnforceUpdate();
269    }
270
271    public void HighlightOutDegree() {
272      Chart.UpdateEnabled = false;
273      ClearAllNodes();
274      var graphNodes = Graph.Values.ToList();
275      double min = graphNodes.Min(x => x.OutEdges == null ? 0 : x.OutEdges.Count);
276      double max = graphNodes.Max(x => x.OutEdges == null ? 0 : x.OutEdges.Count);
277      foreach (var graphNode in graphNodes) {
278        var visualNode = _visualNodeMap[graphNode][0];
279        double deg = graphNode.OutEdges == null ? 0 : graphNode.OutEdges.Count;
280        int index = (int)(deg / max * ColorGradient.Colors.Count) - 1;
281        if (index < 0) index = 0;
282        visualNode.Brush = new SolidBrush(ColorGradient.Colors[index]);
283      }
284      Chart.UpdateEnabled = true;
285      Chart.EnforceUpdate();
286    }
287  }
288
289  internal static class Util {
290    public static Color GetColor(this NodeMetadata nm) {
291      var colorIndex = (int)(nm.Quality * ColorGradient.Colors.Count);
292      if (colorIndex >= ColorGradient.Colors.Count) --colorIndex;
293      return ColorGradient.Colors[colorIndex];
294    }
295  }
296}
Note: See TracBrowser for help on using the repository browser.