Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Improved the LineageExplorerView, added generation labels in the GenealogyGraphChart, added new visual component VisualGenealogyGraphTextLabel.

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