Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 13399 was 13399, checked in by bburlacu, 8 years ago

#1772: Fix compilation errors caused by changes in the Visualization branch.

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