Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Added some useful functionality to the GenealogyGraphView.

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