Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 10827 was 10827, checked in by bburlacu, 11 years ago

#1772: Added default font, pen, and brush for the graphical components (the idea is to save memory by sharing default pens and brushes - not allocating new ones all the time), added support for tracing mutation operations

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