Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 13083 was 13061, checked in by bburlacu, 9 years ago

#1772: Adapted visualization code according to the changes in the HeuristicLab.Visualization branch.

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