Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Modified genealogy graph chart and view to reflect changes in the class hierarchy.

File size: 13.6 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<IGenealogyGraphNode, 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<IGenealogyGraphNode, 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 layers = Graph.Nodes.GroupBy(n => n.Ranks[0]).OrderBy(g => g.Key).Select(g => new { Rank = g.Key, Nodes = g.ToList() }).ToList();
71      for (int i = 0; i != layers.Count; ++i) {
72        // sort descending by quality (using comparison defined in GenealogyGraphNode class)
73        layers[i].Nodes.Sort((b, a) => Graph.Compare(a, b));
74        x = 0; // reset horizontal coordinate
75        // start laying out visual nodes
76        foreach (var node in layers[i].Nodes) {
77          var pen = new Pen(Color.LightGray);
78          var nl = Environment.NewLine;
79          var visualNode = new VisualGenealogyGraphNode(Chart, x, y, x + Diameter, y + Diameter, pen, null) {
80            Data = node,
81            ToolTipText = "Id: " + node.Label + nl +
82                          "Ranks: " + string.Join(",", node.Ranks) + nl +
83                          "Quality: " + String.Format("{0:0.0000}", node.Quality) + nl +
84                          "IsElite: " + node.IsElite
85          };
86          Chart.Group.Add(visualNode);
87          if (!visualNodeMap.ContainsKey(node))
88            visualNodeMap[node] = visualNode;
89
90          x += Cx; // increment horizontal coordinate
91        }
92
93        y -= Cy; // decrement vertical coordinate (because the origin is upside down)
94
95        // connect elites from successive layers with visual arcs
96        if (i > 0) {
97          for (int m = 0; m != layers[i].Nodes.Count; ++m)
98            for (int n = 0; n != layers[i - 1].Nodes.Count; ++n) {
99              if (layers[i].Nodes[m].SymbolicExpressionTree == layers[i - 1].Nodes[n].SymbolicExpressionTree) {
100                var v1 = visualNodeMap[layers[i].Nodes[m]];
101                var v2 = visualNodeMap[layers[i - 1].Nodes[n]];
102                var pen = new Pen(Color.LightGray);
103                visualArcMap[Tuple.Create(v2, v1)] = AddArc(Chart, v2, v1, pen);
104              }
105            }
106        }
107      }
108      // add arcs separately (to avoid some ordering problems)
109      foreach (var node in visualNodeMap.Keys) {
110        var visualNode = visualNodeMap[node];
111        if (node.InEdges == null) continue;
112
113        foreach (var arc in node.InEdges) {
114          var visualParent = visualNodeMap[arc.Target];
115          var pen = new Pen(Color.Transparent);
116          visualArcMap[Tuple.Create(visualParent, visualNode)] = AddArc(Chart, visualParent, visualNode, pen);
117        }
118      }
119
120      Chart.UpdateEnabled = true;
121      Chart.EnforceUpdate();
122    }
123
124    // add an arc between the source and the target nodes, using the specified pen color
125    // adds the arc to the arc lists of both nodes and to the primitive group of the chart
126    private static VisualGenealogyGraphArc AddArc(IChart chart, VisualGenealogyGraphNode source, VisualGenealogyGraphNode target, Pen pen, Brush brush = null) {
127      var arc = new VisualGenealogyGraphArc(chart, source, target, pen) { Brush = brush };
128      arc.UpdatePosition();
129      source.OutgoingArcs.Add(arc);
130      target.IncomingArcs.Add(arc);
131      chart.Group.Add(arc);
132      return arc;
133    }
134
135    protected override void pictureBox_MouseUp(object sender, MouseEventArgs e) {
136      if (Chart.Mode != ChartMode.Select)
137        base.pictureBox_MouseUp(sender, e);
138      else { // select mode
139        // get selected node
140        var visualNodes = Chart.GetAllPrimitives(e.Location).Where(p => p is VisualGenealogyGraphNode).ToList();
141        if (visualNodes.Count > 0) {
142          if (selectedGenealogyGraphNode == visualNodes[0]) return;
143          selectedGenealogyGraphNode = visualNodes[0] as VisualGenealogyGraphNode;
144          if (selectedGenealogyGraphNode == null) return;
145          // new node has been selected, clean up
146          Chart.UpdateEnabled = false;
147          // clear colors
148          ClearAllNodes();
149          // use a rectangle to highlight the currently selected genealogy graph node
150          var center = selectedGenealogyGraphNode.Center;
151          var size = selectedGenealogyGraphNode.Size;
152          double x1 = center.X - size.Width / 2;
153          double x2 = x1 + size.Width;
154          double y1 = center.Y - size.Height / 2;
155          double y2 = y1 + size.Height;
156          if (targetRectangle == null) {
157            targetRectangle = new Visualization.Rectangle(Chart, x1, y1, x2, y2, new Pen(Color.Black), null);
158            Chart.Group.Add(targetRectangle);
159          } else {
160            targetRectangle.SetPosition(x1, y1, x2, y2);
161          }
162          var gNode = selectedGenealogyGraphNode.Data; // genealogy graph node (representing an individual in the population)
163          //          double rank = Graph[gNode].Ranks[0];
164          double rank = ((SymbolicExpressionGenealogyGraphNode)gNode).Ranks[0];
165          // ancestors
166          var ancestors = gNode.Ancestors().Where(n => ((SymbolicExpressionGenealogyGraphNode)n).Ranks.Last() < rank).ToList();
167          ancestors.Add(gNode);
168          // descendants
169          var descendants = gNode.Descendants().Where(n => ((SymbolicExpressionGenealogyGraphNode)n).Ranks.Last() > rank).ToList();
170          descendants.Add(gNode);
171          // highlight ancestors
172          foreach (var node in ancestors.Select(n => visualNodeMap[n])) {
173            node.Brush = new SolidBrush(((SymbolicExpressionGenealogyGraphNode)node.Data).GetColor());
174            if (node.IncomingArcs != null)
175              foreach (var arc in node.IncomingArcs) {
176                var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
177                var end = new Point((int)arc.End.X, (int)arc.End.Y);
178                var srcNode = arc.Source.Data as SymbolicExpressionGenealogyGraphNode;
179                var destNode = arc.Target.Data as SymbolicExpressionGenealogyGraphNode;
180                arc.Pen.Brush = new LinearGradientBrush(start, end, srcNode.GetColor(), destNode.GetColor());
181              }
182          }
183          // highlight descendants
184          foreach (var node in descendants.Select(n => visualNodeMap[n])) {
185            node.Brush = new SolidBrush((node.Data as SymbolicExpressionGenealogyGraphNode).GetColor());
186            if (node.OutgoingArcs != null)
187              foreach (var arc in node.OutgoingArcs) {
188                var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
189                var end = new Point((int)arc.End.X, (int)arc.End.Y);
190                var srcNode = arc.Source.Data as SymbolicExpressionGenealogyGraphNode;
191                var destNode = arc.Target.Data as SymbolicExpressionGenealogyGraphNode;
192                arc.Pen.Brush = new LinearGradientBrush(start, end, srcNode.GetColor(), destNode.GetColor());
193              }
194          }
195        } else {
196          selectedGenealogyGraphNode = null;
197        }
198        // update
199        Chart.UpdateEnabled = true;
200        Chart.EnforceUpdate();
201        if (selectedGenealogyGraphNode != null)
202          /* emit clicked event */
203          OnGenealogyGraphNodeClicked(selectedGenealogyGraphNode, e);
204      }
205    }
206
207    public void ClearAllNodes() {
208      foreach (var primitive in Chart.Group.Primitives) {
209        if (primitive is VisualGenealogyGraphArc) {
210          var arc = primitive as VisualGenealogyGraphArc;
211          var sourceData = (arc.Source.Data as SymbolicExpressionGenealogyGraphNode).SymbolicExpressionTree;
212          var targetData = (arc.Target.Data as SymbolicExpressionGenealogyGraphNode).SymbolicExpressionTree;
213          if (sourceData != targetData) {
214            primitive.Pen.Brush = new SolidBrush(Color.Transparent);
215          } else {
216            primitive.Pen.Brush = new SolidBrush(Color.LightGray);
217          }
218        } else {
219          primitive.Brush = null;
220        }
221      }
222    }
223
224    public void HighlightLineage(IEnumerable<GenealogyGraphNode> nodes) {
225      foreach (var node in nodes.Select(n => visualNodeMap[n])) {
226        node.Brush = new SolidBrush((node.Data as SymbolicExpressionGenealogyGraphNode).GetColor());
227        if (node.IncomingArcs == null || node != visualNodeMap[node.Data]) continue;
228        foreach (var arc in node.IncomingArcs) {
229          if (arc.Source.Data == node.Data) continue;
230          var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
231          var end = new Point((int)arc.End.X, (int)arc.End.Y);
232          var srcNode = arc.Source.Data as SymbolicExpressionGenealogyGraphNode;
233          var destNode = arc.Target.Data as SymbolicExpressionGenealogyGraphNode;
234          arc.Pen.Brush = new LinearGradientBrush(start, end, srcNode.GetColor(), destNode.GetColor());
235        }
236      }
237    }
238
239    // TODO: optimize and reduce complexity of this method
240    public void HighlightNodes(IEnumerable<ISymbolicExpressionTree> trees, Color color) {
241      foreach (var tree in trees)
242        foreach (var graphNode in Graph.Nodes) {
243          if (graphNode.SymbolicExpressionTree == tree) {
244            var visualNode = visualNodeMap[graphNode];
245            visualNode.Brush = new SolidBrush(color);
246          }
247        }
248    }
249
250    public void HighlightNode(SymbolicExpressionGenealogyGraphNode graphNode, Color color) {
251      visualNodeMap[graphNode].Brush = new SolidBrush(color);
252    }
253
254    public void HighlightInDegree() {
255      Chart.UpdateEnabled = false;
256      ClearAllNodes();
257      double max = Graph.Nodes.Max(x => x.InEdges == null ? 0 : x.InEdges.Count);
258      foreach (var graphNode in Graph.Nodes) {
259        var visualNode = visualNodeMap[graphNode];
260        double deg = graphNode.InEdges == null ? 0 : graphNode.InEdges.Count;
261        int index = (int)(deg / max * ColorGradient.Colors.Count);
262        if (index == ColorGradient.Colors.Count) --index;
263        var color = ColorGradient.Colors[index];
264        visualNode.Brush = new SolidBrush(color);
265      }
266      Chart.UpdateEnabled = true;
267      Chart.EnforceUpdate();
268    }
269
270    public void HighlightOutDegree() {
271      Chart.UpdateEnabled = false;
272      ClearAllNodes();
273      var graphNodes = Graph.Nodes;
274      double max = graphNodes.Max(x => x.OutEdges == null ? 0 : x.OutEdges.Count);
275      foreach (var graphNode in graphNodes) {
276        var visualNode = visualNodeMap[graphNode];
277        double deg = graphNode.OutEdges == null ? 0 : graphNode.OutEdges.Count;
278        int index = (int)(deg / max * ColorGradient.Colors.Count);
279        if (index == ColorGradient.Colors.Count) --index;
280        var color = ColorGradient.Colors[index];
281        visualNode.Brush = new SolidBrush(color);
282      }
283      Chart.UpdateEnabled = true;
284      Chart.EnforceUpdate();
285    }
286  }
287
288  internal static class Util {
289    public static Color GetColor(this SymbolicExpressionGenealogyGraphNode node) {
290      var colorIndex = (int)(node.Quality * ColorGradient.Colors.Count);
291      if (colorIndex >= ColorGradient.Colors.Count) --colorIndex;
292      return ColorGradient.Colors[colorIndex];
293    }
294  }
295}
Note: See TracBrowser for help on using the repository browser.