Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772:

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