Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.EvolutionTracking/HeuristicLab.EvolutionTracking.Views/3.4/GenealogyGraphChart.cs @ 11864

Last change on this file since 11864 was 11864, checked in by bburlacu, 8 years ago

#1772: Improved functionality of the SymbolicDataAnalysisGenealogyGraphView in terms of graph navigation and display of useful information. Fixed save of tree image to file in the SymbolicExpressionTreeChart.

File size: 14.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 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.Visualization;
30
31namespace HeuristicLab.EvolutionTracking.Views {
32  public partial class GenealogyGraphChart : ChartControl {
33    private IGenealogyGraph genealogyGraph;
34
35    private const double XIncrement = 30;
36    private const double YIncrement = 30;
37    private const double Diameter = 20;
38
39    private readonly Brush defaultBrush;
40    private readonly Pen defaultPen;
41
42    private Dictionary<IGenealogyGraphNode, VisualGenealogyGraphNode> nodeMap;
43    private Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc> arcMap;
44
45    public IGenealogyGraph GenealogyGraph {
46      get { return genealogyGraph; }
47      set {
48        if (value == null) return;
49        genealogyGraph = value;
50        Clear();
51        DrawGraph(XIncrement, YIncrement, Diameter);
52      }
53    }
54
55    public IGenealogyGraphNode SelectedGraphNode {
56      get {
57        return SelectedVisualNode == null ? null : SelectedVisualNode.Data;
58      }
59      set {
60        SelectedVisualNode = GetMappedNode(value);
61        UpdateSelectedVisualNode();
62      }
63    }
64
65    private void Clear() {
66      nodeMap = new Dictionary<IGenealogyGraphNode, VisualGenealogyGraphNode>();
67      arcMap = new Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc>();
68
69      Chart.Group.Clear();
70    }
71
72    public bool UpdateEnabled {
73      get { return Chart.UpdateEnabled; }
74      set { Chart.UpdateEnabled = value; }
75    }
76
77    public void EnforceUpdate() {
78      Chart.EnforceUpdate();
79    }
80
81    #region chart modes
82    public bool SimpleLineages { get; set; }
83    public bool LockGenealogy { get; set; }
84    public bool TraceFragments { get; set; }
85    #endregion
86
87    private Visualization.Rectangle TargetRectangle { get; set; }
88    private bool DrawInProgress { get; set; } // do not try to update the chart while the drawing is not finished
89    protected VisualGenealogyGraphNode SelectedVisualNode { get; set; }
90
91    private VisualGenealogyGraphNode GetMappedNode(IGenealogyGraphNode node) {
92      VisualGenealogyGraphNode v;
93      nodeMap.TryGetValue(node, out v);
94      return v;
95    }
96
97    private VisualGenealogyGraphArc GetMappedArc(IGenealogyGraphNode source, IGenealogyGraphNode target) {
98      VisualGenealogyGraphNode visualSource, visualTarget;
99      nodeMap.TryGetValue(source, out visualSource);
100      nodeMap.TryGetValue(target, out visualTarget);
101
102      if (visualSource == null || visualTarget == null) return null;
103
104      VisualGenealogyGraphArc arc;
105      arcMap.TryGetValue(new Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>(visualSource, visualTarget), out arc);
106      return arc;
107    }
108
109    public GenealogyGraphChart() {
110      InitializeComponent();
111
112      defaultBrush = new SolidBrush(Color.Transparent);
113      defaultPen = new Pen(Color.DarkGray);
114    }
115
116    protected virtual void DrawGraph(double xIncrement, double yIncrement, double diameter) {
117      Chart.UpdateEnabled = false;
118      DrawInProgress = true;
119
120      var ranks = GenealogyGraph.Ranks.Select(t => new { Rank = t.Key, Nodes = t.Value }).OrderBy(p => p.Rank).ToList();
121      double x = 0;
122      double y = PreferredSize.Height + yIncrement + 2 * diameter;
123
124      foreach (var rank in ranks) {
125        var nodes = rank.Nodes.ToList();
126        nodes.Sort((a, b) => b.CompareTo(a)); // sort descending by quality
127        var nl = Environment.NewLine;
128
129        foreach (var node in nodes) {
130          var brush = new SolidBrush(node.GetColor());
131          var visualNode = new VisualGenealogyGraphNode(Chart, x, y, x + diameter, y + diameter, defaultPen, brush) {
132            Data = node,
133            ToolTipText = "Rank: " + node.Rank + nl +
134                          "Quality: " + String.Format("{0:0.0000}", node.Quality) + nl +
135                          "IsElite: " + node.IsElite + nl +
136                          "In/Out Degree: " + node.InDegree + " " + node.OutDegree
137          };
138          Chart.Group.Add(visualNode);
139          nodeMap.Add(node, visualNode);
140
141          x += xIncrement;
142        }
143        y -= yIncrement;
144        x = 0;
145      }
146
147      // add arcs
148      foreach (var node in GenealogyGraph.Vertices) {
149        if (!node.InArcs.Any()) continue;
150        var visualNode = GetMappedNode(node);
151
152        if (visualNode == null) continue;
153        foreach (var arc in node.InArcs) {
154          var parent = arc.Source;
155          var visualParent = GetMappedNode(parent);
156          if (visualParent == null) continue;
157          var pen = Pens.Transparent;
158          var visualArc = AddArc(Chart, visualParent, visualNode, pen);
159          visualArc.OneLayerDown(); // send it behind the visual nodes
160          if (!arcMap.ContainsKey(Tuple.Create(visualParent, visualNode))) {
161            arcMap.Add(Tuple.Create(visualParent, visualNode), visualArc);
162          }
163        }
164      }
165      // TODO: connect elites
166
167      Chart.UpdateEnabled = true;
168      Chart.EnforceUpdate();
169
170      DrawInProgress = false;
171    }
172
173    public event MouseEventHandler GenealogyGraphNodeClicked;
174    protected void OnGenealogyGraphNodeClicked(object sender, MouseEventArgs e) {
175      var clicked = GenealogyGraphNodeClicked;
176      if (clicked != null) clicked(sender, e);
177    }
178
179    public event MouseEventHandler GenealogyGraphNodeDoubleClicked;
180    protected void OnGenealogyGraphNodeDoubleClicked(object sender, MouseEventArgs e) {
181      var doubleClicked = GenealogyGraphNodeDoubleClicked;
182      if (doubleClicked != null)
183        doubleClicked(sender, e);
184    }
185    #region event handlers
186
187    protected override void pictureBox_MouseMove(object sender, MouseEventArgs e) {
188      if (!DrawInProgress) {
189        switch (e.Button) {
190          case MouseButtons.Left:
191            Chart.Mode = ChartMode.Select;
192            Cursor = Cursors.Default;
193            break;
194          case MouseButtons.Middle:
195            Chart.Mode = ChartMode.Move;
196            Cursor = Cursors.Hand;
197            break;
198        }
199      }
200      base.pictureBox_MouseMove(sender, e);
201    }
202    protected override void pictureBox_MouseUp(object sender, MouseEventArgs e) {
203      Cursor = Cursors.Default;
204      if (Chart.Mode == ChartMode.Move) {
205        Chart.Mode = ChartMode.Select;
206        return;
207      }
208      if (Chart.Mode != ChartMode.Select) {
209        base.pictureBox_MouseUp(sender, e);
210        return;
211      }
212      var primitive = Chart.GetAllPrimitives(e.Location).FirstOrDefault(p => p is VisualGenealogyGraphNode);
213      if (primitive == null) {
214        SelectedVisualNode = null;
215        return;
216      }
217      if (SelectedVisualNode == primitive) return;
218      SelectedVisualNode = primitive as VisualGenealogyGraphNode;
219      if (SelectedVisualNode == null) return;
220
221      UpdateSelectedVisualNode(); // redraw ancestries, mark node etc.
222
223      base.pictureBox_MouseUp(sender, e);
224    }
225
226    private void UpdateSelectedVisualNode() {
227      if (!LockGenealogy) {
228        // new node has been selected, clean up
229        Chart.UpdateEnabled = false;
230        if (ModifierKeys != Keys.Shift) {
231          // clear colors
232          ClearPrimitives();
233        }
234        // use a rectangle to highlight the currently selected genealogy graph node
235        var graphNode = SelectedVisualNode.Data;
236        var visualNode = GetMappedNode(graphNode);
237
238        DrawLineage(visualNode, n => SimpleLineages ? n.IncomingArcs.Take(1) : n.IncomingArcs, a => a.Source);
239        ((SolidBrush)visualNode.Brush).Color = Color.Transparent;
240        DrawLineage(visualNode, n => n.OutgoingArcs, a => a.Target);
241      }
242
243      MarkSelectedNode();
244
245      // update
246      UpdateEnabled = true;
247      EnforceUpdate();
248
249      if (SelectedVisualNode != null)
250        OnGenealogyGraphNodeClicked(SelectedVisualNode, null); // emit clicked event
251    }
252    #endregion
253
254    private static void DrawLineage(VisualGenealogyGraphNode node, Func<VisualGenealogyGraphNode, IEnumerable<VisualGenealogyGraphArc>> arcSelector, Func<VisualGenealogyGraphArc, VisualGenealogyGraphNode> nodeSelector) {
255      var brush = (SolidBrush)node.Brush;
256      if (brush.Color != Color.Transparent) return; // this lineage was already drawn (avoid redrawing common ancestors)
257      brush.Color = node.Data.GetColor();
258      var arcs = arcSelector(node);
259      var pen = new Pen(Color.Transparent);
260      foreach (var arc in arcs) {
261        var source = arc.Source.Data;
262        var target = arc.Target.Data;
263        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
264        var end = new Point((int)arc.End.X, (int)arc.End.Y);
265        arc.Pen = pen;
266        arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
267        DrawLineage(nodeSelector(arc), arcSelector, nodeSelector);
268      }
269    }
270
271    public void HighlightHotPaths() {
272      Chart.UpdateEnabled = false;
273      ClearPrimitives();
274      var arcs = GenealogyGraph.Vertices.SelectMany(n => n.InArcs).ToList();
275      foreach (var arc in arcs) { arc.Weight = 1.0; } // reset weights
276      var rank = GenealogyGraph.Ranks.Max(x => x.Key);
277      foreach (var graphNode in GenealogyGraph.GetByRank(rank)) {
278        foreach (var ancestor in graphNode.Ancestors) {
279          foreach (var arc in ancestor.InArcs) {
280            arc.Weight++;
281          }
282        }
283      }
284      double max = arcs.Max(a => a.Weight);
285      double min = arcs.Min(a => a.Weight);
286
287      if (min.IsAlmost(max)) return;
288      //translate interval (min,max) to interval (0,255)
289      foreach (var arc in arcs) {
290        var vArc = GetMappedArc(arc.Source, arc.Target);
291        int colorIndex = (int)Math.Round((arc.Weight - min) * 255 / (max - min));
292        if (colorIndex > 254) colorIndex = 254;
293        vArc.Pen = new Pen(ColorGradient.Colors[colorIndex]);
294        //        vArc.Pen.Brush = new SolidBrush(ColorGradient.Colors[colorIndex]);
295      }
296      Chart.UpdateEnabled = true;
297      Chart.EnforceUpdate();
298    }
299
300    void MarkSelectedNode() {
301      var center = SelectedVisualNode.Center;
302      var size = SelectedVisualNode.Size;
303      double x1 = center.X - size.Width / 2;
304      double x2 = x1 + size.Width;
305      double y1 = center.Y - size.Height / 2;
306      double y2 = y1 + size.Height;
307      if (TargetRectangle == null) {
308        TargetRectangle = new Visualization.Rectangle(Chart, x1, y1, x2, y2, new Pen(Color.Black), null);
309        Chart.Group.Add(TargetRectangle);
310      } else {
311        TargetRectangle.SetPosition(x1, y1, x2, y2);
312      }
313    }
314
315    private static VisualGenealogyGraphArc AddArc(IChart chart, VisualGenealogyGraphNode source, VisualGenealogyGraphNode target, Pen pen, Brush brush = null) {
316      var arc = new VisualGenealogyGraphArc(chart, source, target, pen) { Brush = brush };
317      arc.UpdatePosition();
318      source.OutgoingArcs.Add(arc);
319      target.IncomingArcs.Add(arc);
320      chart.Group.Add(arc);
321      return arc;
322    }
323
324    public void ClearArcs() {
325      foreach (var primitive in Chart.Group.Primitives.OfType<VisualGenealogyGraphArc>()) {
326        primitive.Pen = Pens.Transparent;
327      }
328    }
329
330    public void ClearNodes() {
331      foreach (var primitive in Chart.Group.Primitives.OfType<VisualGenealogyGraphNode>()) {
332        primitive.Brush = new SolidBrush(Color.Transparent);
333        primitive.Pen = new Pen(Color.LightGray);
334      }
335    }
336
337    public virtual void ClearPrimitives() {
338      foreach (var primitive in Chart.Group.Primitives) {
339        if (primitive is VisualGenealogyGraphArc) {
340          primitive.Pen = Pens.Transparent;
341        } else if (primitive is VisualGenealogyGraphNode) {
342          primitive.Brush = new SolidBrush(Color.Transparent);
343          primitive.Pen = new Pen(Color.DarkGray);
344          //          primitive.Pen = Pens.Transparent; // remove the node contour
345        }
346      }
347    }
348
349    public void HighlightNodes(IEnumerable<IGenealogyGraphNode> nodes, bool clearPrimitives = true) {
350      if (clearPrimitives)
351        ClearPrimitives();
352
353      foreach (var node in nodes) {
354        var graphNode = GetMappedNode(node);
355        graphNode.Brush = new SolidBrush(node.GetColor());
356      }
357    }
358
359    public void HighlightNodes(IEnumerable<IGenealogyGraphNode> nodes, Color color, bool clearPrimitives = true) {
360      if (clearPrimitives)
361        ClearPrimitives();
362
363      foreach (var node in nodes.Select(GetMappedNode)) {
364        node.Brush = new SolidBrush(color);
365      }
366    }
367
368    public void HighlightAll() {
369      foreach (var visualNode in nodeMap.Values) {
370        visualNode.Brush = new SolidBrush(visualNode.Data.GetColor());
371      }
372      foreach (var arc in arcMap.Values) {
373        var source = arc.Source.Data;
374        var target = arc.Target.Data;
375        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
376        var end = new Point((int)arc.End.X, (int)arc.End.Y);
377        arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
378      }
379    }
380
381    public void HighlightArc(IGenealogyGraphNode source, IGenealogyGraphNode target) {
382      var arc = GetMappedArc(source, target) ??
383                AddArc(Chart, GetMappedNode(source), GetMappedNode(target), new Pen(Color.Transparent));
384      var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
385      var end = new Point((int)arc.End.X, (int)arc.End.Y);
386      arc.Pen = new Pen(Color.Transparent);
387      arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
388    }
389  }
390
391  internal static class Util {
392    public static Color GetColor(this IGenealogyGraphNode node) {
393      if (double.IsNaN(node.Quality))
394        return ColorGradient.Colors[0];
395      var colorIndex = (int)Math.Round(node.Quality * ColorGradient.Colors.Count);
396      if (colorIndex >= ColorGradient.Colors.Count) return ColorGradient.Colors.Last();
397      return ColorGradient.Colors[colorIndex];
398    }
399  }
400}
Note: See TracBrowser for help on using the repository browser.