Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Implemented new View, improved functionality (tracking of fragments and operators)

File size: 12.2 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.Encodings.SymbolicExpressionTreeEncoding;
29using HeuristicLab.Visualization;
30
31namespace HeuristicLab.EvolutionaryTracking.Views {
32  public partial class GenealogyGraphChart : ChartControl {
33    private Dictionary<GenealogyGraphNode, List<VisualGenealogyGraphNode>> _visualNodeMap;
34    private Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc> _visualArcMap;
35    private const int Diameter = 20;
36    private int x, y;
37    private const int Cx = 30, Cy = 30;
38
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      var layers = Graph.Values.GroupBy(node => node.Rank).OrderBy(g => g.Key);
70      // show elites in every graph level
71      List<GenealogyGraphNode> currLayer = null, prevLayer = null;
72      double currRank = 0.0;
73      int count = layers.Count();
74      for (int i = 0; i != count; ++i) {
75        // propagate elites from successive layers
76        if (currRank == (int)currRank) prevLayer = currLayer;
77        currRank = layers.ElementAt(i).Key;
78        currLayer = layers.ElementAt(i).ToList();
79        if (i > 0 && currRank == (int)currRank) {
80          // this code injects elites that propagate from the previous generation into the current graph layer, so that they can be represented as visual nodes
81          int prevCount = prevLayer.Count(node => node.IsElite);
82          int currCount = currLayer.Count(node => node.IsElite);
83          for (int j = currCount; j < prevCount; ++j) currLayer.Add(prevLayer.ElementAt(j));
84        }
85        currLayer.Sort(); // uses the CompareTo() method inside the GenealogyGraphNode class
86
87        foreach (var node in currLayer) {
88          var pen = new Pen(Color.LightGray);
89          var nl = Environment.NewLine;
90          var visualNode = new VisualGenealogyGraphNode(Chart, x, y, x + Diameter, y + Diameter, pen, null) {
91            Data = node, ToolTipText = "Id: " + node.Label + nl + "Rank: " + node.Rank + nl + "Quality: " + String.Format("{0:0.0000}", node.Quality) + nl + "IsElite: " + node.IsElite
92          };
93          Chart.Group.Add(visualNode);
94          if (!_visualNodeMap.ContainsKey(node)) _visualNodeMap[node] = new List<VisualGenealogyGraphNode>();
95          _visualNodeMap[node].Add(visualNode);
96          // connect visual nodes that actually represent the same individual in the genealogy graph (this applies for elites) with a dashed line
97          if (_visualNodeMap[node].Count > 1) {
98            for (int c = _visualNodeMap[node].Count; c >= 2; --c) {
99              var n1 = _visualNodeMap[node][c - 1];
100              var n2 = _visualNodeMap[node][c - 2];
101              var visualArc = AddArc(Chart, n1, n2, new Pen(Color.LightGray) { DashStyle = DashStyle.Dash });
102              _visualArcMap[Tuple.Create(n1, n2)] = visualArc;
103            }
104          }
105          x += Cx; // increment horizontal coordinate
106          if (node.InEdges == null) continue;
107          // add visual edges
108          foreach (var arc in node.InEdges) {
109            var parent = arc.Target;
110            // find the visual parent node that is closest to the current node in terms of vertical distance
111            var visualParent = _visualNodeMap[parent].Where(p => p.Center.Y > visualNode.Center.Y).OrderByDescending(p => p.Center.Y).Last();
112            DashStyle style;
113            if (visualParent.Data == visualNode.Data) {
114              style = DashStyle.Dash;
115            } else {
116              style = arc.OperatorType == 0 ? DashStyle.Solid : DashStyle.Dash;
117            }
118            var arcPen = new Pen(Color.Transparent) { DashStyle = style };
119            _visualArcMap[Tuple.Create(visualParent, visualNode)] = AddArc(Chart, visualParent, visualNode, arcPen);
120          }
121        }
122        y -= Cy; // decrement vertical coordinate (because the origin is upside down)
123        x = 0; // reset horizontal coordinate
124      }
125
126      Chart.UpdateEnabled = true;
127      Chart.EnforceUpdate();
128    }
129
130    // add an arc between the source and the target nodes, using the specified pen color
131    // adds the arc to the arc lists of both nodes and to the primitive group of the chart
132    private static VisualGenealogyGraphArc AddArc(IChart chart, VisualGenealogyGraphNode source, VisualGenealogyGraphNode target, Pen pen, Brush brush = null) {
133      var arc = new VisualGenealogyGraphArc(chart, source, target, pen) { Brush = brush };
134      arc.UpdatePosition();
135      source.OutgoingArcs.Add(arc);
136      target.IncomingArcs.Add(arc);
137      chart.Group.Add(arc);
138      return arc;
139    }
140
141    protected override void pictureBox_MouseUp(object sender, MouseEventArgs e) {
142      if (Chart.Mode != ChartMode.Select)
143        base.pictureBox_MouseUp(sender, e);
144      else { // select mode
145        Chart.UpdateEnabled = false;
146        // clear colors
147        foreach (var primitive in Chart.Group.Primitives) {
148          if (primitive is VisualGenealogyGraphNode) {
149            var visualNode = primitive as VisualGenealogyGraphNode;
150            visualNode.Brush = null;
151          } else if (primitive is VisualGenealogyGraphArc) {
152            var visualArc = primitive as VisualGenealogyGraphArc;
153            if (visualArc.Source.Data != visualArc.Target.Data) visualArc.Pen.Brush = new SolidBrush(Color.Transparent);
154          }
155        }
156        // get selected node
157        var visualNodes = Chart.GetAllPrimitives(e.Location).Where(p => p is VisualGenealogyGraphNode).ToList();
158        if (visualNodes.Count > 0) {
159          _selectedGenealogyGraphNode = visualNodes[0] as VisualGenealogyGraphNode;
160          // color selected node and its genealogy
161          if (_selectedGenealogyGraphNode == null) return;
162          var gNode = _selectedGenealogyGraphNode.Data; // genealogy graph node (representing an individual in the population)
163          double rank = gNode.Rank;
164          var ancestors = gNode.Ancestors().Where(a => a.Rank < rank).ToList();
165          ancestors.Add(gNode);
166          // use special highlighting for the currently selected genealogy graph node
167          var center = _selectedGenealogyGraphNode.Center;
168          var size = _selectedGenealogyGraphNode.Size;
169          double x1 = center.X - size.Width / 2, x2 = x1 + size.Width;
170          double y1 = center.Y - size.Height / 2, y2 = y1 + size.Height;
171          if (_targetRectangle != null) _targetRectangle.SetPosition(x1, y1, x2, y2);
172          else {
173            _targetRectangle = new Visualization.Rectangle(Chart, x1, y1, x2, y2, new Pen(Color.Black), null);
174            Chart.Group.Add(_targetRectangle);
175          }
176          // highlight selected node and its ancestry
177          foreach (var node in ancestors.SelectMany(n => _visualNodeMap[n])) {
178            node.Brush = new SolidBrush(node.ToColor());
179            if (node.IncomingArcs != null && node == _visualNodeMap[node.Data].First())
180              foreach (var arc in node.IncomingArcs) {
181                if (arc.Source.Data == node.Data) continue;
182                var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
183                var end = new Point((int)arc.End.X, (int)arc.End.Y);
184                arc.Pen.Brush = new LinearGradientBrush(start, end, arc.Source.ToColor(), arc.Target.ToColor());
185                if (node.Data.CutpointIndex == -1) { // if the cut index wasn't computed yet
186                  var parent = arc.Source.Data.Data as ISymbolicExpressionTree;
187                  var child = node.Data.Data as ISymbolicExpressionTree;
188                  node.Data.CutpointIndex = GetCutIndex(parent, child);
189                }
190              }
191          }
192          // highlight the descendants
193          var descendants = gNode.Descendants().Where(a => a.Rank > rank).ToList();
194          descendants.Add(gNode);
195          foreach (var node in descendants.SelectMany(n => _visualNodeMap[n])) {
196            node.Brush = new SolidBrush(node.ToColor());
197            foreach (var arc in node.IncomingArcs.Where(a => descendants.Contains(a.Source.Data))) {
198              if (arc.Source.Data == node.Data) continue;
199              var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
200              var end = new Point((int)arc.End.X, (int)arc.End.Y);
201              arc.Pen.Brush = new LinearGradientBrush(start, end, arc.Source.ToColor(), arc.Target.ToColor());
202            }
203          }
204        } else {
205          _selectedGenealogyGraphNode = null;
206        }
207        // update
208        Chart.UpdateEnabled = true;
209        Chart.EnforceUpdate();
210        if (_selectedGenealogyGraphNode != null) /* emit clicked event */ OnGenealogyGraphNodeClicked(_selectedGenealogyGraphNode, e);
211      }
212    }
213
214    public void ClearAllNodes() {
215      Chart.UpdateEnabled = false;
216      foreach (var node in Chart.Group.Primitives.Where(p => p is VisualGenealogyGraphNode).Cast<VisualGenealogyGraphNode>()) {
217        node.Brush = null;
218        if (node.IncomingArcs != null)
219          foreach (var arc in node.IncomingArcs)
220            if (arc.Source.Data != node.Data) arc.Pen = new Pen(Color.Transparent);
221      }
222      Chart.UpdateEnabled = true;
223      Chart.EnforceUpdate();
224    }
225
226    public void HighlightNodes(IEnumerable<ISymbolicExpressionTree> trees, Color color) {
227      Chart.UpdateEnabled = false;
228      foreach (var visualNode in trees.Select(tree => _visualNodeMap[Graph.GetNode(tree)]).SelectMany(vList => vList))
229        visualNode.Brush = new SolidBrush(color);
230
231      Chart.UpdateEnabled = true;
232      Chart.EnforceUpdate();
233    }
234
235    // maybe this method does't belong here
236    // TODO: find a good location for this kind of functionality
237    private static int GetCutIndex(ISymbolicExpressionTree parent, ISymbolicExpressionTree child) {
238      var e1 = parent.IterateNodesPostfix().GetEnumerator();
239      var e2 = child.IterateNodesPostfix().GetEnumerator();
240      int pos = -1;
241      var comparer = new SymbolicExpressionTreeGenealogyGraph.SymbolicExpressionTreeNodeComparer(0);
242      while (e1.MoveNext() && e2.MoveNext() && comparer.Equals(e1.Current, e2.Current)) ++pos;
243      return pos;
244    }
245  }
246}
Note: See TracBrowser for help on using the repository browser.