Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 11262 was 11262, checked in by bburlacu, 10 years ago

#1772: Added missing Storable constructor to GenealogyGraphNode<T> class. Prefer cloning vertices in the GenealogyAnalyzer. Add In/Out Degree in node tooltip in the GenealogyGraphChart.

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