Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 15561 was 15561, checked in by bburlacu, 6 years ago

#1772: Refactor SymbolicDataAnalysisGenealogyGraphView

File size: 13.6 KB
RevLine 
[10650]1#region License Information
2/* HeuristicLab
[13877]3 * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[10650]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;
[15561]34    private const double XIncrement = 25;
35    private const double YIncrement = 25;
[10269]36    private const double Diameter = 20;
[10827]37    private readonly Brush defaultBrush;
38    private readonly Pen defaultPen;
39
[11852]40    private Dictionary<IGenealogyGraphNode, VisualGenealogyGraphNode> nodeMap;
41    private Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc> arcMap;
42
[13399]43    #region chart options
[11881]44    public bool SimpleLineages { get; set; }
45    public bool LockGenealogy { get; set; }
[15561]46    public Func<IGenealogyGraphNode, Color> ColorSelector { get; set; }
[11881]47    #endregion
48
49    private bool DrawInProgress { get; set; } // do not try to update the chart while the drawing is not finished
50
[10300]51    public IGenealogyGraph GenealogyGraph {
[10269]52      get { return genealogyGraph; }
53      set {
54        if (value == null) return;
55        genealogyGraph = value;
56        Clear();
57        DrawGraph(XIncrement, YIncrement, Diameter);
58      }
59    }
[10264]60
[11506]61    public IGenealogyGraphNode SelectedGraphNode {
62      get {
63        return SelectedVisualNode == null ? null : SelectedVisualNode.Data;
64      }
[11864]65      set {
[12208]66        if (value == null || value == SelectedGraphNode)
67          return;
[11864]68        SelectedVisualNode = GetMappedNode(value);
[13877]69        RedrawLineages();
[11864]70      }
[11506]71    }
[10269]72    private void Clear() {
[10830]73      nodeMap = new Dictionary<IGenealogyGraphNode, VisualGenealogyGraphNode>();
74      arcMap = new Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc>();
[10264]75
[10269]76      Chart.Group.Clear();
[10264]77    }
78
[10269]79    private Visualization.Rectangle TargetRectangle { get; set; }
[11506]80    protected VisualGenealogyGraphNode SelectedVisualNode { get; set; }
[10269]81
[12208]82    public VisualGenealogyGraphNode GetMappedNode(IGenealogyGraphNode node) {
[10269]83      VisualGenealogyGraphNode v;
84      nodeMap.TryGetValue(node, out v);
85      return v;
[10264]86    }
87
[12208]88    public VisualGenealogyGraphArc GetMappedArc(IGenealogyGraphNode source, IGenealogyGraphNode target) {
[10269]89      VisualGenealogyGraphNode visualSource, visualTarget;
90      nodeMap.TryGetValue(source, out visualSource);
91      nodeMap.TryGetValue(target, out visualTarget);
92
93      if (visualSource == null || visualTarget == null) return null;
94
95      VisualGenealogyGraphArc arc;
96      arcMap.TryGetValue(new Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>(visualSource, visualTarget), out arc);
97      return arc;
[10264]98    }
[10269]99
[15561]100    public GenealogyGraphChart() : base(new GridlessChart(0, 0, 800, 600)) {
[10269]101      InitializeComponent();
[13426]102      AddChartModes(new PanChartMode(this), new ZoomInChartMode(this), new ZoomOutChartMode(this), new SelectChartMode(this));
[10827]103      defaultBrush = new SolidBrush(Color.Transparent);
104      defaultPen = new Pen(Color.DarkGray);
[15561]105
106      ColorSelector = node => ColorGradient.Colors[(int)Math.Round(node.Quality * 255)];
[10264]107    }
[10650]108
[10269]109    protected virtual void DrawGraph(double xIncrement, double yIncrement, double diameter) {
[13061]110      this.SuspendRendering();
[10269]111      DrawInProgress = true;
[10264]112
[10269]113      var ranks = GenealogyGraph.Ranks.Select(t => new { Rank = t.Key, Nodes = t.Value }).OrderBy(p => p.Rank).ToList();
114      double x = 0;
115      double y = PreferredSize.Height + yIncrement + 2 * diameter;
[10264]116
[10269]117      foreach (var rank in ranks) {
[15561]118        var rect = new Visualization.Rectangle(Chart, new PointD(x, y), new PointD(x + diameter, y + diameter));
119        var font = new Font(FontFamily.GenericSansSerif, 12);
120        var genLabel = new LabeledPrimitive(rect, rank.Rank.ToString(), font);
121        Chart.Group.Add(genLabel);
122
123        x += xIncrement;
124
[10269]125        var nodes = rank.Nodes.ToList();
126        nodes.Sort((a, b) => b.CompareTo(a)); // sort descending by quality
127        var nl = Environment.NewLine;
[10264]128
[10269]129        foreach (var node in nodes) {
[15561]130          var brush = new SolidBrush(ColorSelector(node));
[10827]131          var visualNode = new VisualGenealogyGraphNode(Chart, x, y, x + diameter, y + diameter, defaultPen, brush) {
[10264]132            Data = node,
133            ToolTipText = "Rank: " + node.Rank + nl +
[13426]134                          "Quality: " + string.Format("{0:N4}", node.Quality) + nl +
[11262]135                          "IsElite: " + node.IsElite + nl +
136                          "In/Out Degree: " + node.InDegree + " " + node.OutDegree
[10264]137          };
138          Chart.Group.Add(visualNode);
[10269]139          nodeMap.Add(node, visualNode);
[10264]140
[10269]141          x += xIncrement;
[10264]142        }
[10269]143        y -= yIncrement;
[10285]144        x = 0;
[10264]145      }
[10285]146
[10269]147      // add arcs
[10903]148      foreach (var node in GenealogyGraph.Vertices) {
[10732]149        if (!node.InArcs.Any()) continue;
150        var visualNode = GetMappedNode(node);
[10833]151
[10269]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;
[10827]157          var pen = Pens.Transparent;
[10269]158          var visualArc = AddArc(Chart, visualParent, visualNode, pen);
[13061]159          Chart.OneLayerDown(visualArc); // send it behind the visual nodes
[10830]160          if (!arcMap.ContainsKey(Tuple.Create(visualParent, visualNode))) {
[10285]161            arcMap.Add(Tuple.Create(visualParent, visualNode), visualArc);
[10830]162          }
[10264]163        }
164      }
165
[13061]166      ResumeRendering();
[10269]167      DrawInProgress = false;
[10264]168    }
[10650]169
170    public event MouseEventHandler GenealogyGraphNodeClicked;
[11506]171    protected void OnGenealogyGraphNodeClicked(object sender, MouseEventArgs e) {
[10650]172      var clicked = GenealogyGraphNodeClicked;
173      if (clicked != null) clicked(sender, e);
174    }
175
[11506]176    public event MouseEventHandler GenealogyGraphNodeDoubleClicked;
177    protected void OnGenealogyGraphNodeDoubleClicked(object sender, MouseEventArgs e) {
178      var doubleClicked = GenealogyGraphNodeDoubleClicked;
179      if (doubleClicked != null)
180        doubleClicked(sender, e);
181    }
[10650]182    #region event handlers
[11506]183
[13426]184    protected override void PictureBoxOnMouseUp(object sender, MouseEventArgs e) {
185      if (Mode is SelectChartMode) {
186        var primitive = Chart.GetAllPrimitives(e.Location).FirstOrDefault(p => p is VisualGenealogyGraphNode);
187        if (primitive != null && primitive != SelectedVisualNode) {
188          SelectedVisualNode = (VisualGenealogyGraphNode)primitive;
[13877]189          RedrawLineages(); // redraw ancestries, mark node etc.
190
191          if (SelectedVisualNode != null)
192            OnGenealogyGraphNodeClicked(SelectedVisualNode, null); // emit clicked event
[10264]193        }
194      }
[13061]195      base.PictureBoxOnMouseUp(sender, e);
[11864]196    }
197
[13877]198    private void RedrawLineages() {
[10264]199      if (!LockGenealogy) {
200        // new node has been selected, clean up
[13061]201        SuspendRendering();
[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
[15561]210        DrawLineage(visualNode, n => SimpleLineages ? n.IncomingArcs.Take(1) : n.IncomingArcs, a => a.Source, ColorSelector);
[10888]211        ((SolidBrush)visualNode.Brush).Color = Color.Transparent;
[15561]212        DrawLineage(visualNode, n => n.OutgoingArcs, a => a.Target, ColorSelector);
[10264]213      }
214
215      MarkSelectedNode();
216
217      // update
[13061]218      ResumeRendering();
[10264]219    }
[10650]220    #endregion
221
[11881]222    #region drawing routines
[15561]223    private static void DrawLineage(VisualGenealogyGraphNode node, Func<VisualGenealogyGraphNode, IEnumerable<VisualGenealogyGraphArc>> arcSelector, Func<VisualGenealogyGraphArc, VisualGenealogyGraphNode> nodeSelector, Func<IGenealogyGraphNode, Color> getNodeColor) {
[10732]224      var brush = (SolidBrush)node.Brush;
[10746]225      if (brush.Color != Color.Transparent) return; // this lineage was already drawn (avoid redrawing common ancestors)
[15561]226      brush.Color = getNodeColor(node.Data);
[10746]227      var arcs = arcSelector(node);
[10830]228      var pen = new Pen(Color.Transparent);
[10264]229      foreach (var arc in arcs) {
230        var source = arc.Source.Data;
231        var target = arc.Target.Data;
232        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
233        var end = new Point((int)arc.End.X, (int)arc.End.Y);
[10830]234        arc.Pen = pen;
[15561]235        arc.Pen.Brush = new LinearGradientBrush(start, end, getNodeColor(source), getNodeColor(target));
236        DrawLineage(nodeSelector(arc), arcSelector, nodeSelector, getNodeColor);
[10264]237      }
238    }
[10650]239
[10264]240    void MarkSelectedNode() {
[10269]241      var center = SelectedVisualNode.Center;
242      var size = SelectedVisualNode.Size;
[10264]243      double x1 = center.X - size.Width / 2;
244      double x2 = x1 + size.Width;
245      double y1 = center.Y - size.Height / 2;
246      double y2 = y1 + size.Height;
[10269]247      if (TargetRectangle == null) {
[13061]248        var lowerLeft = new PointD(x1, y1);
249        var upperRight = new PointD(x2, y2);
250        TargetRectangle = new Visualization.Rectangle(Chart, lowerLeft, upperRight, new Pen(Color.Black), null);
[10269]251        Chart.Group.Add(TargetRectangle);
[10264]252      } else {
[13061]253        var lowerLeft = new PointD(x1, y1);
254        var upperRight = new PointD(x2, y2);
255        TargetRectangle.SetPosition(lowerLeft, upperRight);
[10264]256      }
257    }
[10650]258
[10269]259    private static VisualGenealogyGraphArc AddArc(IChart chart, VisualGenealogyGraphNode source, VisualGenealogyGraphNode target, Pen pen, Brush brush = null) {
260      var arc = new VisualGenealogyGraphArc(chart, source, target, pen) { Brush = brush };
261      arc.UpdatePosition();
262      source.OutgoingArcs.Add(arc);
263      target.IncomingArcs.Add(arc);
264      chart.Group.Add(arc);
265      return arc;
266    }
[10650]267
[11852]268    public void ClearArcs() {
269      foreach (var primitive in Chart.Group.Primitives.OfType<VisualGenealogyGraphArc>()) {
270        primitive.Pen = Pens.Transparent;
271      }
272    }
273
274    public void ClearNodes() {
275      foreach (var primitive in Chart.Group.Primitives.OfType<VisualGenealogyGraphNode>()) {
276        primitive.Brush = new SolidBrush(Color.Transparent);
277        primitive.Pen = new Pen(Color.LightGray);
278      }
279    }
280
[10269]281    public virtual void ClearPrimitives() {
[10264]282      foreach (var primitive in Chart.Group.Primitives) {
283        if (primitive is VisualGenealogyGraphArc) {
[10827]284          primitive.Pen = Pens.Transparent;
[10732]285        } else if (primitive is VisualGenealogyGraphNode) {
[10830]286          primitive.Brush = new SolidBrush(Color.Transparent);
287          primitive.Pen = new Pen(Color.DarkGray);
[10264]288        }
289      }
290    }
[10650]291
[11852]292    public void HighlightNodes(IEnumerable<IGenealogyGraphNode> nodes, bool clearPrimitives = true) {
293      if (clearPrimitives)
294        ClearPrimitives();
295
[10264]296      foreach (var node in nodes) {
[10650]297        var graphNode = GetMappedNode(node);
[15561]298        graphNode.Brush = new SolidBrush(ColorSelector(node));
[10264]299      }
300    }
[10650]301
[11852]302    public void HighlightNodes(IEnumerable<IGenealogyGraphNode> nodes, Color color, bool clearPrimitives = true) {
303      if (clearPrimitives)
304        ClearPrimitives();
305
306      foreach (var node in nodes.Select(GetMappedNode)) {
307        node.Brush = new SolidBrush(color);
308      }
309    }
310
[10264]311    public void HighlightAll() {
[10269]312      foreach (var visualNode in nodeMap.Values) {
[15561]313        visualNode.Brush = new SolidBrush(ColorSelector(visualNode.Data));
314        Chart.IntoForeground(visualNode);
[10264]315      }
[15561]316      // draw arcs first and then nodes
[10269]317      foreach (var arc in arcMap.Values) {
[10264]318        var source = arc.Source.Data;
319        var target = arc.Target.Data;
320        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
321        var end = new Point((int)arc.End.X, (int)arc.End.Y);
[15561]322        arc.Pen = new Pen(new LinearGradientBrush(start, end, ColorSelector(source), ColorSelector(target)));
323        Chart.IntoBackground(arc);
[10264]324      }
325    }
[11506]326
327    public void HighlightArc(IGenealogyGraphNode source, IGenealogyGraphNode target) {
328      var arc = GetMappedArc(source, target) ??
329                AddArc(Chart, GetMappedNode(source), GetMappedNode(target), new Pen(Color.Transparent));
330      var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
331      var end = new Point((int)arc.End.X, (int)arc.End.Y);
332      arc.Pen = new Pen(Color.Transparent);
[15561]333      arc.Pen = new Pen(new LinearGradientBrush(start, end, ColorSelector(source), ColorSelector(target)));
334      Chart.IntoBackground(arc);
[11506]335    }
[11881]336    #endregion
[10650]337
[15561]338    // workaround to disable the chart grid
339    private class GridlessChart : Chart {
340      public GridlessChart(PointD lowerLeft, PointD upperRight) : base(lowerLeft, upperRight) {
341        Grid = null;
342      }
343
344      public GridlessChart(double x1, double y1, double x2, double y2) : this(new PointD(x1, y1), new PointD(x2, y2)) { }
[10264]345    }
346  }
[10269]347}
Note: See TracBrowser for help on using the repository browser.