Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 12002 was 9963, checked in by bburlacu, 11 years ago

#1772: Merged changes from the trunk and other branches. Added new ExtendedSymbolicExpressionTreeCanvas control for the visual exploration of tree genealogies. Reorganized some files and folders.

File size: 15.9 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<string, VisualGenealogyGraphNode> visualNodeMap; // map the uid of the genealogy graph vertex with a visual node (in this way each node is uniquely determined)
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    public bool SimpleLineages { get; set; }
44    public bool LockGenealogy { get; set; }
45
46    private bool drawing;
47
48    private SymbolicExpressionTreeGenealogyGraph graph;
49    public SymbolicExpressionTreeGenealogyGraph Graph {
50      get { return graph; }
51      internal set {
52        graph = value;
53        if (graph == null) return;
54
55        visualNodeMap = new Dictionary<string, VisualGenealogyGraphNode>();
56        visualArcMap = new Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc>();
57
58        Chart.Group.Clear();
59        DrawGraph();
60      }
61    }
62
63    private VisualGenealogyGraphNode GetVisualGenealogyGraphNode(IVertex node) {
64      VisualGenealogyGraphNode visualNode;
65      visualNodeMap.TryGetValue(node.Id, out visualNode);
66      return visualNode;
67    }
68
69    private VisualGenealogyGraphArc GetVisualGenealogyGraphArc(IVertex source, IVertex target) {
70      VisualGenealogyGraphNode visualSource;
71      VisualGenealogyGraphNode visualTarget;
72      visualNodeMap.TryGetValue(source.Id, out visualSource);
73      visualNodeMap.TryGetValue(target.Id, out visualTarget);
74      if (visualSource != null && visualTarget != null) {
75        VisualGenealogyGraphArc visualArc;
76        visualArcMap.TryGetValue(
77          new Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>(visualSource, visualTarget), out visualArc);
78        if (visualArc != null) return visualArc;
79      }
80      return null;
81    }
82
83    public event MouseEventHandler GenealogyGraphNodeClicked;
84    private void OnGenealogyGraphNodeClicked(object sender, MouseEventArgs e) {
85      var clicked = GenealogyGraphNodeClicked;
86      if (clicked != null) clicked(sender, e);
87    }
88
89    public GenealogyGraphChart() {
90      //      InitializeComponent();
91      Chart = new Chart(0, 0, PreferredSize.Width, PreferredSize.Height);
92      x = 0;
93      y = PreferredSize.Height + Cx + 2 * Diameter;
94      Chart.Mode = ChartMode.Select;
95    }
96
97    public void DrawGraph() {
98      if (graph == null) return;
99      Chart.UpdateEnabled = false;
100
101      drawing = true;
102
103      var layers = Graph.Nodes.GroupBy(n => n.Rank).OrderBy(g => g.Key).Select(g => new { Rank = g.Key, Nodes = g.ToList() }).ToList();
104
105      y = PreferredSize.Height + Cx + 2 * Diameter;
106
107      for (int i = 0; i != layers.Count; ++i) {
108        x = 0;
109        layers[i].Nodes.Sort((b, a) => Graph.Compare(a, b));
110        double rank = Math.Floor(layers[i].Rank);
111        if (layers[i].Rank.IsAlmost(rank)) {
112          var visualTextNode = new VisualGenealogyGraphTextLabel(Chart, x, y + 2, x + Diameter, y + Diameter) {
113            FontBrush = new SolidBrush(Color.Black),
114            Font = new Font("Arial", Diameter - 4, FontStyle.Regular, GraphicsUnit.Pixel),
115            Text = String.Format("{0:0}", rank)
116          };
117          Chart.Group.Add(visualTextNode);
118        }
119
120        // sort descending by quality (using comparison defined in GenealogyGraphNode class)
121        x += (int)Math.Floor(1.5 * Cx); // reset horizontal coordinate
122
123        foreach (var node in layers[i].Nodes) {
124          var pen = new Pen(Color.LightGray);
125          var nl = Environment.NewLine;
126          var visualNode = new VisualGenealogyGraphNode(Chart, x, y, x + Diameter, y + Diameter, pen, null) {
127            Data = node,
128            ToolTipText = "Rank: " + node.Rank + nl +
129                          "Quality: " + String.Format("{0:0.0000}", node.Quality) + nl +
130                          "IsElite: " + node.IsElite
131          };
132          Chart.Group.Add(visualNode);
133          visualNodeMap.Add(node.Id, visualNode);
134
135          x += Cx; // increment horizontal coordinate
136        }
137
138        y -= Cy; // decrement vertical coordinate (because the origin is upside down)
139
140        // connect elites from successive layers with visual arcs
141        if (i == 0) continue;
142        var currLayer = layers[i].Nodes;
143        var prevLayer = layers[i - 1].Nodes;
144        foreach (var n in currLayer) {
145          foreach (var m in prevLayer) {
146            if (n.SymbolicExpressionTree != m.SymbolicExpressionTree) continue;
147            var v1 = GetVisualGenealogyGraphNode(n);
148            var v2 = GetVisualGenealogyGraphNode(m);
149
150            var pen = new Pen(Color.LightGray);
151            visualArcMap[Tuple.Create(v2, v1)] = AddArc(Chart, v2, v1, pen);
152          }
153        }
154      }
155      // add arcs separately (to avoid some ordering problems)
156      foreach (SymbolicExpressionTreeGenealogyGraphNode node in Graph.Nodes) {
157        VisualGenealogyGraphNode visualNode = GetVisualGenealogyGraphNode(node);
158        if (node.InEdges == null) continue;
159
160        foreach (Arc arc in node.InEdges) {
161          var visualParent = GetVisualGenealogyGraphNode(arc.Source);
162          var pen = new Pen(Color.Transparent);
163          visualArcMap[Tuple.Create(visualParent, visualNode)] = AddArc(Chart, visualParent, visualNode, pen);
164        }
165      }
166
167      // highlight the most recent common ancestor ("eve" individual)
168      //      var eve = graph.MostRecentCommonAncestor();
169      //      if (eve != null) {
170      //        var visualGraphNode = GetVisualGenealogyGraphNode(eve);
171
172      //        var brush = new SolidBrush(Color.BlueViolet);
173      //        visualGraphNode.FontBrush = brush;
174      //      }
175
176      Chart.UpdateEnabled = true;
177      Chart.EnforceUpdate();
178
179      drawing = false;
180    }
181
182    // add an arc between the source and the target nodes, using the specified pen color
183    // adds the arc to the arc lists of both nodes and to the primitive group of the chart
184    private static VisualGenealogyGraphArc AddArc(IChart chart, VisualGenealogyGraphNode source, VisualGenealogyGraphNode target, Pen pen, Brush brush = null) {
185      var arc = new VisualGenealogyGraphArc(chart, source, target, pen) { Brush = brush };
186      arc.UpdatePosition();
187      source.OutgoingArcs.Add(arc);
188      target.IncomingArcs.Add(arc);
189      chart.Group.Add(arc);
190      return arc;
191    }
192
193    protected override void pictureBox_MouseMove(object sender, MouseEventArgs e) {
194      if (!drawing) {
195        switch (e.Button) {
196          case MouseButtons.Left:
197            Chart.Mode = ChartMode.Select;
198            Cursor = Cursors.Default;
199            break;
200          case MouseButtons.Middle:
201            Chart.Mode = ChartMode.Move;
202            Cursor = Cursors.Hand;
203            break;
204        }
205        base.pictureBox_MouseMove(sender, e);
206      }
207    }
208
209    protected override void pictureBox_MouseUp(object sender, MouseEventArgs e) {
210      Cursor = Cursors.Default;
211      if (Chart.Mode == ChartMode.Move) {
212        Chart.Mode = ChartMode.Select;
213        return;
214      }
215
216      if (Chart.Mode != ChartMode.Select) {
217        base.pictureBox_MouseUp(sender, e);
218        return;
219      }
220
221      var visualNodes = Chart.GetAllPrimitives(e.Location).Where(p => p is VisualGenealogyGraphNode).ToList();
222      if (visualNodes.Count <= 0) {
223        selectedGenealogyGraphNode = null;
224        return;
225      }
226      if (selectedGenealogyGraphNode == visualNodes[0]) return;
227      selectedGenealogyGraphNode = visualNodes[0] as VisualGenealogyGraphNode;
228      if (selectedGenealogyGraphNode == null) return;
229
230      if (!LockGenealogy) {
231        // new node has been selected, clean up
232        Chart.UpdateEnabled = false;
233        if (ModifierKeys != Keys.Shift)
234          // clear colors
235          ClearAllNodes();
236        // use a rectangle to highlight the currently selected genealogy graph node
237        var gNode = selectedGenealogyGraphNode.Data;
238        var visualNode = GetVisualGenealogyGraphNode(gNode);
239
240        DrawLineage(visualNode, n => SimpleLineages ? n.IncomingArcs.Take(1) : n.IncomingArcs, a => a.Source);
241        visualNode.Brush = null;
242        DrawLineage(visualNode, n => n.OutgoingArcs, a => a.Target);
243      }
244
245      MarkSelectedNode();
246
247      // update
248      Chart.UpdateEnabled = true;
249      Chart.EnforceUpdate();
250
251      if (selectedGenealogyGraphNode != null)
252        /* emit clicked event */
253        OnGenealogyGraphNodeClicked(selectedGenealogyGraphNode, e);
254    }
255
256    private void DrawLineage(VisualGenealogyGraphNode node, Func<VisualGenealogyGraphNode, IEnumerable<VisualGenealogyGraphArc>> arcSelector, Func<VisualGenealogyGraphArc, VisualGenealogyGraphNode> nodeSelector) {
257      if (node.Brush != null) return;
258      node.Brush = new SolidBrush(node.Data.GetColor());
259      var arcs = arcSelector(node);
260      if (arcs == null) return;
261
262      foreach (var arc in arcs) {
263        var source = arc.Source.Data;
264        var target = arc.Target.Data;
265        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
266        var end = new Point((int)arc.End.X, (int)arc.End.Y);
267        arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
268        arc.Pen.Color = Color.Transparent;
269        //        arc.Pen.FontBrush = new SolidBrush(Color.DarkGray);
270        DrawLineage(nodeSelector(arc), arcSelector, nodeSelector);
271      }
272    }
273
274    void MarkSelectedNode() {
275      var center = selectedGenealogyGraphNode.Center;
276      var size = selectedGenealogyGraphNode.Size;
277      double x1 = center.X - size.Width / 2;
278      double x2 = x1 + size.Width;
279      double y1 = center.Y - size.Height / 2;
280      double y2 = y1 + size.Height;
281      if (targetRectangle == null) {
282        targetRectangle = new Visualization.Rectangle(Chart, x1, y1, x2, y2, new Pen(Color.Black), null);
283        Chart.Group.Add(targetRectangle);
284      } else {
285        targetRectangle.SetPosition(x1, y1, x2, y2);
286      }
287    }
288
289    public void ClearAllNodes() {
290      foreach (var primitive in Chart.Group.Primitives) {
291        if (primitive is VisualGenealogyGraphArc) {
292          var arc = primitive as VisualGenealogyGraphArc;
293          var sourceData = arc.Source.Data.SymbolicExpressionTree;
294          var targetData = arc.Target.Data.SymbolicExpressionTree;
295          primitive.Pen.Brush = sourceData != targetData ? new SolidBrush(Color.Transparent) : new SolidBrush(Color.LightGray);
296        } else {
297          primitive.Brush = null;
298        }
299      }
300    }
301
302    public void HighlightNodes(IEnumerable<SymbolicExpressionTreeGenealogyGraphNode> nodes) {
303      Chart.UpdateEnabled = false;
304      ClearAllNodes();
305      foreach (var node in nodes) {
306        GetVisualGenealogyGraphNode(node).Brush = new SolidBrush(node.GetColor());
307      }
308      Chart.UpdateEnabled = true;
309      Chart.EnforceUpdate();
310    }
311
312    // TODO: optimize and reduce complexity of this method
313    public void HighlightNodes(IEnumerable<ISymbolicExpressionTree> trees) {
314      foreach (var tree in trees) {
315        var graphNodes = graph.GetGraphNodes(tree);
316        foreach (var graphNode in graphNodes) {
317          GetVisualGenealogyGraphNode(graphNode).Brush = new SolidBrush(graphNode.GetColor());
318        }
319      }
320    }
321
322    public void HighlightArcs(IEnumerable<IEdge> arcs, Color color) {
323      foreach (var a in arcs) {
324        var arc = GetVisualGenealogyGraphArc(a.Source, a.Target);
325        if (arc != null) {
326          var source = arc.Source.Data;
327          var target = arc.Target.Data;
328          var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
329          var end = new Point((int)arc.End.X, (int)arc.End.Y);
330          arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
331        }
332      }
333    }
334
335    public void HighlightNode(SymbolicExpressionTreeGenealogyGraphNode graphNode, Color color) {
336      GetVisualGenealogyGraphNode(graphNode).Brush = new SolidBrush(color);
337    }
338
339    public void HighlightAll() {
340      Chart.UpdateEnabled = false;
341      foreach (var visualNode in visualNodeMap.Values) {
342        visualNode.Brush = new SolidBrush(visualNode.Data.GetColor());
343      }
344      foreach (var arc in visualArcMap.Values) {
345        var source = arc.Source.Data;
346        var target = arc.Target.Data;
347        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
348        var end = new Point((int)arc.End.X, (int)arc.End.Y);
349        arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
350      }
351      Chart.UpdateEnabled = true;
352      Chart.EnforceUpdate();
353    }
354
355    public void HighlightInDegree() {
356      Chart.UpdateEnabled = false;
357      ClearAllNodes();
358      double max = Graph.Nodes.Max(x => x.InEdges == null ? 0 : x.InEdges.Count);
359      foreach (var graphNode in Graph.Nodes) {
360        var visualNode = GetVisualGenealogyGraphNode(graphNode);
361        double deg = graphNode.InEdges == null ? 0 : graphNode.InEdges.Count;
362        int index = (int)(deg / max * ColorGradient.Colors.Count);
363        if (index == ColorGradient.Colors.Count) --index;
364        var color = ColorGradient.Colors[index];
365        visualNode.Brush = new SolidBrush(color);
366      }
367      Chart.UpdateEnabled = true;
368      Chart.EnforceUpdate();
369    }
370
371    public void HighlightOutDegree() {
372      Chart.UpdateEnabled = false;
373      ClearAllNodes();
374      var graphNodes = Graph.Nodes;
375      double max = graphNodes.Max(x => x.OutEdges == null ? 0 : x.OutEdges.Count);
376      foreach (var graphNode in graphNodes) {
377        var visualNode = GetVisualGenealogyGraphNode(graphNode);
378        double deg = graphNode.OutEdges == null ? 0 : graphNode.OutEdges.Count;
379        int index = (int)(deg / max * ColorGradient.Colors.Count);
380        if (index == ColorGradient.Colors.Count) --index;
381        var color = ColorGradient.Colors[index];
382        visualNode.Brush = new SolidBrush(color);
383      }
384      Chart.UpdateEnabled = true;
385      Chart.EnforceUpdate();
386    }
387  }
388
389  internal static class Util {
390    public static Color GetColor(this SymbolicExpressionTreeGenealogyGraphNode node) {
391      var colorIndex = (int)(node.Quality * ColorGradient.Colors.Count);
392      if (colorIndex >= ColorGradient.Colors.Count) --colorIndex;
393      return ColorGradient.Colors[colorIndex];
394    }
395  }
396}
Note: See TracBrowser for help on using the repository browser.