Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 15401 was 13877, checked in by bburlacu, 9 years ago

#1772: Improve SymbolicDataAnalysisGenealogyGraphView by using a better coloring scheme for frequent subtrees. Fix bug when reaching the end of the genealogy. Minor refactorings.

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