Free cookie consent management tool by TermsFeed Policy Generator

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

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

#1772: Fixed namespaces.

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