Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Updated the GenealogyGraphChart using new chart modes from HeuristicLab.Visualization

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