Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Cleaned up the design of the generic genealogy analyzer and related classes, created generic genealogy graph view. Added instrumentation code to TravelingSalesmapProblem.cs allowing genealogy tracking. Merged trunk changes (instrumentation for multi operators).

File size: 10.1 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Drawing;
4using System.Drawing.Drawing2D;
5using System.Linq;
6using System.Windows.Forms;
7using HeuristicLab.Common;
8using HeuristicLab.Visualization;
9
10namespace HeuristicLab.EvolutionTracking.Views {
11  public partial class GenealogyGraphChart : ChartControl {
12    private IGenealogyGraph genealogyGraph;
13
14    private const double XIncrement = 30;
15    private const double YIncrement = 30;
16    private const double Diameter = 20;
17
18    public IGenealogyGraph GenealogyGraph {
19      get { return genealogyGraph; }
20      set {
21        if (value == null) return;
22        genealogyGraph = value;
23        Clear();
24        DrawGraph(XIncrement, YIncrement, Diameter);
25      }
26    }
27
28    private void Clear() {
29      if (nodeMap == null)
30        nodeMap = new Dictionary<IGenealogyGraphNode, VisualGenealogyGraphNode>();
31      else nodeMap.Clear();
32
33      if (arcMap == null)
34        arcMap = new Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc>();
35      else arcMap.Clear();
36
37      Chart.Group.Clear();
38    }
39
40    private Dictionary<IGenealogyGraphNode, VisualGenealogyGraphNode> nodeMap;
41    private Dictionary<Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>, VisualGenealogyGraphArc> arcMap;
42
43    public bool SimpleLineages { get; set; }
44    public bool LockGenealogy { get; set; }
45    private Visualization.Rectangle TargetRectangle { get; set; }
46
47    private VisualGenealogyGraphNode GetMappedNode(IGenealogyGraphNode node) {
48      VisualGenealogyGraphNode v;
49      nodeMap.TryGetValue(node, out v);
50      return v;
51    }
52
53    private VisualGenealogyGraphArc GetMappedArc(IGenealogyGraphNode source, IGenealogyGraphNode target) {
54      VisualGenealogyGraphNode visualSource, visualTarget;
55      nodeMap.TryGetValue(source, out visualSource);
56      nodeMap.TryGetValue(target, out visualTarget);
57
58      if (visualSource == null || visualTarget == null) return null;
59
60      VisualGenealogyGraphArc arc;
61      arcMap.TryGetValue(new Tuple<VisualGenealogyGraphNode, VisualGenealogyGraphNode>(visualSource, visualTarget), out arc);
62      return arc;
63    }
64    private bool DrawInProgress { get; set; } // do not try to update the chart while the drawing is not finished
65    private VisualGenealogyGraphNode SelectedVisualNode { get; set; }
66
67    public event MouseEventHandler GenealogyGraphNodeClicked;
68    private void OnGenealogyGraphNodeClicked(object sender, MouseEventArgs e) {
69      var clicked = GenealogyGraphNodeClicked;
70      if (clicked != null) clicked(sender, e);
71    }
72    public GenealogyGraphChart() {
73      InitializeComponent();
74    }
75    protected virtual void DrawGraph(double xIncrement, double yIncrement, double diameter) {
76      Chart.UpdateEnabled = false;
77      DrawInProgress = true;
78
79      var ranks = GenealogyGraph.Ranks.Select(t => new { Rank = t.Key, Nodes = t.Value }).OrderBy(p => p.Rank).ToList();
80      double x = 0;
81      double y = PreferredSize.Height + yIncrement + 2 * diameter;
82
83      foreach (var rank in ranks) {
84        var nodes = rank.Nodes.ToList();
85        nodes.Sort((a, b) => b.CompareTo(a)); // sort descending by quality
86        var nl = Environment.NewLine;
87
88        foreach (var node in nodes) {
89          var pen = new Pen(Color.LightGray);
90
91          var visualNode = new VisualGenealogyGraphNode(Chart, x, y, x + diameter, y + diameter, pen, null) {
92            Data = node,
93            ToolTipText = "Rank: " + node.Rank + nl +
94                          "Quality: " + String.Format("{0:0.0000}", node.Quality) + nl +
95                          "IsElite: " + node.IsElite
96          };
97          Chart.Group.Add(visualNode);
98          nodeMap.Add(node, visualNode);
99
100          x += xIncrement;
101        }
102        y -= yIncrement;
103        x = 0;
104      }
105
106      // add arcs
107      foreach (var node in GenealogyGraph.Nodes.Cast<IGenealogyGraphNode>()) {
108        if (node.InArcs == null) continue;
109        var visualNode = nodeMap[node];
110        if (visualNode == null) continue;
111        foreach (var arc in node.InArcs) {
112          var parent = arc.Source;
113          var visualParent = GetMappedNode(parent);
114          if (visualParent == null) continue;
115          var pen = new Pen(Color.Transparent);
116          var visualArc = AddArc(Chart, visualParent, visualNode, pen);
117          if (!arcMap.ContainsKey(Tuple.Create(visualParent, visualNode)))
118            arcMap.Add(Tuple.Create(visualParent, visualNode), visualArc);
119        }
120      }
121      // TODO: connect elites
122
123      Chart.UpdateEnabled = true;
124      Chart.EnforceUpdate();
125
126      DrawInProgress = false;
127    }
128    protected override void pictureBox_MouseMove(object sender, MouseEventArgs e) {
129      if (!DrawInProgress) {
130        switch (e.Button) {
131          case MouseButtons.Left:
132            Chart.Mode = ChartMode.Select;
133            Cursor = Cursors.Default;
134            break;
135          case MouseButtons.Middle:
136            Chart.Mode = ChartMode.Move;
137            Cursor = Cursors.Hand;
138            break;
139        }
140      }
141      base.pictureBox_MouseMove(sender, e);
142    }
143    protected override void pictureBox_MouseUp(object sender, MouseEventArgs e) {
144      Cursor = Cursors.Default;
145      if (Chart.Mode == ChartMode.Move) {
146        Chart.Mode = ChartMode.Select;
147        return;
148      }
149      if (Chart.Mode != ChartMode.Select) {
150        base.pictureBox_MouseUp(sender, e);
151        return;
152      }
153      var visualNodes = Chart.GetAllPrimitives(e.Location).Where(p => p is VisualGenealogyGraphNode).ToList();
154      if (visualNodes.Count <= 0) {
155        SelectedVisualNode = null;
156        return;
157      }
158      if (SelectedVisualNode == visualNodes[0]) return;
159      SelectedVisualNode = visualNodes[0] as VisualGenealogyGraphNode;
160      if (SelectedVisualNode == null) return;
161
162      if (!LockGenealogy) {
163        // new node has been selected, clean up
164        Chart.UpdateEnabled = false;
165        if (ModifierKeys != Keys.Shift)
166          // clear colors
167          ClearPrimitives();
168        // use a rectangle to highlight the currently selected genealogy graph node
169        var gNode = SelectedVisualNode.Data;
170        var visualNode = GetMappedNode(gNode);
171
172        DrawLineage(visualNode, n => SimpleLineages ? n.IncomingArcs.Take(1) : n.IncomingArcs, a => a.Source);
173        visualNode.Brush = null;
174        DrawLineage(visualNode, n => n.OutgoingArcs, a => a.Target);
175      }
176
177      MarkSelectedNode();
178
179      // update
180      Chart.UpdateEnabled = true;
181      Chart.EnforceUpdate();
182
183      if (SelectedVisualNode != null)
184        /* emit clicked event */
185        OnGenealogyGraphNodeClicked(SelectedVisualNode, e);
186
187      base.pictureBox_MouseUp(sender, e);
188    }
189    private void DrawLineage(VisualGenealogyGraphNode node, Func<VisualGenealogyGraphNode, IEnumerable<VisualGenealogyGraphArc>> arcSelector, Func<VisualGenealogyGraphArc, VisualGenealogyGraphNode> nodeSelector) {
190      if (node.Brush != null) return;
191      node.Brush = new SolidBrush(node.Data.GetColor());
192      var arcs = arcSelector(node);
193      if (arcs == null) return;
194
195      foreach (var arc in arcs) {
196        var source = arc.Source.Data;
197        var target = arc.Target.Data;
198        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
199        var end = new Point((int)arc.End.X, (int)arc.End.Y);
200        arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
201        arc.Pen.Color = Color.Transparent;
202        //        arc.Pen.FontBrush = new SolidBrush(Color.DarkGray);
203        DrawLineage(nodeSelector(arc), arcSelector, nodeSelector);
204      }
205    }
206    void MarkSelectedNode() {
207      var center = SelectedVisualNode.Center;
208      var size = SelectedVisualNode.Size;
209      double x1 = center.X - size.Width / 2;
210      double x2 = x1 + size.Width;
211      double y1 = center.Y - size.Height / 2;
212      double y2 = y1 + size.Height;
213      if (TargetRectangle == null) {
214        TargetRectangle = new Visualization.Rectangle(Chart, x1, y1, x2, y2, new Pen(Color.Black), null);
215        Chart.Group.Add(TargetRectangle);
216      } else {
217        TargetRectangle.SetPosition(x1, y1, x2, y2);
218      }
219    }
220    private static VisualGenealogyGraphArc AddArc(IChart chart, VisualGenealogyGraphNode source, VisualGenealogyGraphNode target, Pen pen, Brush brush = null) {
221      var arc = new VisualGenealogyGraphArc(chart, source, target, pen) { Brush = brush };
222      arc.UpdatePosition();
223      source.OutgoingArcs.Add(arc);
224      target.IncomingArcs.Add(arc);
225      chart.Group.Add(arc);
226      return arc;
227    }
228    public virtual void ClearPrimitives() {
229      foreach (var primitive in Chart.Group.Primitives) {
230        if (primitive is VisualGenealogyGraphArc) {
231          primitive.Pen.Brush = new SolidBrush(Color.Transparent);
232        } else {
233          primitive.Brush = null;
234        }
235      }
236    }
237    public void HighlightNodes(IEnumerable<IGenealogyGraphNode> nodes) {
238      Chart.UpdateEnabled = false;
239      ClearPrimitives();
240      foreach (var node in nodes) {
241        GetMappedNode(node).Brush = new SolidBrush(node.GetColor());
242      }
243      Chart.UpdateEnabled = true;
244      Chart.EnforceUpdate();
245    }
246    public void HighlightAll() {
247      Chart.UpdateEnabled = false;
248      foreach (var visualNode in nodeMap.Values) {
249        visualNode.Brush = new SolidBrush(visualNode.Data.GetColor());
250      }
251      foreach (var arc in arcMap.Values) {
252        var source = arc.Source.Data;
253        var target = arc.Target.Data;
254        var start = new Point((int)arc.Start.X, (int)arc.Start.Y);
255        var end = new Point((int)arc.End.X, (int)arc.End.Y);
256        arc.Pen.Brush = new LinearGradientBrush(start, end, source.GetColor(), target.GetColor());
257      }
258      Chart.UpdateEnabled = true;
259      Chart.EnforceUpdate();
260    }
261  }
262  internal static class Util {
263    public static Color GetColor(this IGenealogyGraphNode node) {
264      var colorIndex = (int)(node.Quality * ColorGradient.Colors.Count);
265      if (colorIndex >= ColorGradient.Colors.Count) --colorIndex;
266      return ColorGradient.Colors[colorIndex];
267    }
268  }
269}
Note: See TracBrowser for help on using the repository browser.