source: branches/HeuristicLab.VariableInteractionNetworks/HeuristicLab.VariableInteractionNetworks.Views/3.3/DirectedGraphChart.cs @ 13789

Last change on this file since 13789 was 13789, checked in by bburlacu, 5 years ago

#2288:

  • Refactor RunCollectionVariableInteractionNetworkView improving functionality, modularity and code organisation.
  • Small tweaks to the DirectedGraphChart and DirectedGraphChartMode
File size: 9.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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.Core;
29using HeuristicLab.Random;
30using HeuristicLab.Visualization;
31using Pen = System.Drawing.Pen;
32using Rectangle = HeuristicLab.Visualization.Rectangle;
33using Routing = HeuristicLab.VariableInteractionNetworks.Views.ConstrainedForceDirectedLayout.EdgeRouting;
34
35
36namespace HeuristicLab.VariableInteractionNetworks.Views {
37  public partial class DirectedGraphChart : ChartControl {
38    private ConstrainedForceDirectedLayout layout;
39    // provide direct and reverse mapping between graph objects and layout shapes
40    private Dictionary<IPrimitive, IVertex> vertexMap;
41    private Dictionary<IPrimitive, IArc> arcMap;
42    private Dictionary<IArc, LinearPrimitiveBase> arcShapes;
43    private Dictionary<IVertex, RectangularPrimitiveBase> vertexShapes;
44
45    // graph layout options
46    public double IdealEdgeLength { get; set; } = 50;
47    public bool PerformEdgeRouting { get; set; } = false;
48    public Routing RoutingMode { get; set; }
49
50    public IVertex GetVertex(IPrimitive primitive) {
51      IVertex v;
52      vertexMap.TryGetValue(primitive, out v);
53      return v;
54    }
55
56    public IArc GetArc(IPrimitive primitive) {
57      IArc a;
58      arcMap.TryGetValue(primitive, out a);
59      return a;
60    }
61
62    public RectangularPrimitiveBase GetVertexShape(IVertex v) {
63      RectangularPrimitiveBase p;
64      vertexShapes.TryGetValue(v, out p);
65      return p;
66    }
67
68    public LinearPrimitiveBase GetArcShape(IArc arc) {
69      LinearPrimitiveBase l;
70      arcShapes.TryGetValue(arc, out l);
71      return l;
72    }
73
74    public ToolTip ToolTip {
75      get { return toolTip; }
76    }
77
78    public DirectedGraphChart() {
79      InitializeComponent();
80
81      var directedGraphChartMode = new DirectedGraphChartMode(this);
82
83      this.AddChartModes(directedGraphChartMode, new PanChartMode(this), new ZoomInChartMode(this), new ZoomOutChartMode(this));
84      shapes = new Dictionary<Type, LabeledPrimitive>();
85      SmoothingMode = SmoothingMode.HighQuality; // set antialiasing for nicer rendering
86    }
87
88    private IDirectedGraph graph;
89    public IDirectedGraph Graph {
90      get { return graph; }
91      set {
92        if (value == null || graph == value) return;
93        graph = value;
94        Draw();
95      }
96    }
97
98    private void RegisterEvents() { }
99
100    private void DeregisterEvents() { }
101
102    public void Draw() {
103      SuspendRendering();
104      Chart.Group.Clear();
105      vertexMap = new Dictionary<IPrimitive, IVertex>();
106      arcMap = new Dictionary<IPrimitive, IArc>();
107      var rng = new MersenneTwister();
108      vertexShapes = new Dictionary<IVertex, RectangularPrimitiveBase>();
109      arcShapes = new Dictionary<IArc, LinearPrimitiveBase>();
110      var vertexRectangles = new Dictionary<IVertex, RectangleF>();
111      foreach (var v in graph.Vertices) {
112        var shape = CreateShape(v);
113        shape.ToolTipText = v.Label;
114        vertexMap[shape] = v;
115        vertexShapes[v] = shape;
116        var width = (float)shape.Size.Width;
117        var height = (float)shape.Size.Height;
118        var x = (float)(rng.NextDouble() * (Chart.Size.Width - width));
119        var y = (float)(rng.NextDouble() * (Chart.Size.Height - height));
120        vertexRectangles[v] = new RectangleF(x, y, width, height);
121      }
122      layout = new ConstrainedForceDirectedLayout {
123        DefaultEdgeLength = IdealEdgeLength,
124        PerformEdgeRouting = PerformEdgeRouting,
125        LayoutWidth = (int)Chart.Size.Width,
126        LayoutHeight = (int)Chart.Size.Height,
127        MinHorizontalSpacing = 0,
128        MinVerticalSpacing = 0,
129        EdgeRoutingMode = RoutingMode
130      };
131
132      var arcs = graph.Vertices.SelectMany(x => x.InArcs).ToList();
133      if (arcs.Count != graph.Arcs.Count())
134        throw new Exception("Arcs count does not match!");
135
136      layout.Run(vertexRectangles);
137      var vertexPositions = layout.VertexPositions;
138      var arcRoutes = layout.ArcRoutes;
139
140      var max = arcRoutes.Keys.Max(x => x.Weight);
141
142      // move shapes to the positions computed by the layout
143      foreach (var pair in vertexPositions) {
144        var shape = vertexShapes[pair.Key];
145        var pos = vertexPositions[pair.Key];
146        shape.Move(new Offset(pos.X, pos.Y));
147      }
148
149      foreach (var pair in arcRoutes) {
150        var arc = pair.Key;
151        var weight = arc.Weight;
152        var points = pair.Value;
153        var target = (LabeledPrimitive)vertexShapes[pair.Key.Target];
154        var len = points.Length; // len = 2 when no edge routingMode is performed
155        Pen pen;
156        var penWidth = weight / max * 2;
157        if (len == 2) {
158          if (double.IsInfinity(penWidth) || double.IsNaN(penWidth)) penWidth = 1;
159          pen = new Pen(Color.Black, (float)penWidth) { CustomEndCap = new AdjustableArrowCap(5, 3) };
160          var start = points[0];
161          var end = points[1];
162          var line = new Line(Chart, new PointD(start.X, start.Y), new PointD(end.X, end.Y));
163          var rect = target.Primitive as Rectangle;
164          var ell = target.Primitive as Ellipse;
165          PointD intersectionPoint;
166          if (rect != null)
167            intersectionPoint = rect.ComputeIntersect(line).FirstOrDefault();
168          else if (ell != null)
169            intersectionPoint = ell.ComputeIntersect(line).FirstOrDefault();
170          else
171            throw new InvalidOperationException("Unknown vertex shape.");
172          if (intersectionPoint == default(PointD))
173            intersectionPoint = end;
174          line = new Line(Chart, new PointD(start.X, start.Y), intersectionPoint, pen) { ToolTipText = arc.Label };
175          Chart.Group.Add(line);
176          arcShapes[arc] = line;
177          arcMap[line] = arc;
178        } else {
179          for (int i = 0; i < points.Length - 1; ++i) {
180            Line line;
181            var start = points[i];
182            var end = points[i + 1];
183
184            var segment = new Line(Chart, new PointD(start.X, start.Y), new PointD(end.X, end.Y));
185            var rect = target.Primitive as Rectangle;
186            var ell = target.Primitive as Ellipse;
187
188            PointD endPoint;
189            List<PointD> intersectionPoints;
190            if (rect != null) {
191              intersectionPoints = rect.ComputeIntersect(segment);
192            } else if (ell != null) {
193              intersectionPoints = ell.ComputeIntersect(segment);
194            } else {
195              throw new InvalidOperationException("Unknown vertex shape.");
196            }
197            if (intersectionPoints.Any()) {
198              endPoint = intersectionPoints.First();
199              pen = new Pen(Color.Black, (float)penWidth) { CustomEndCap = new AdjustableArrowCap(5, 3) };
200            } else {
201              endPoint = end;
202              pen = new Pen(Color.Black, (float)penWidth);
203            }
204            line = new Line(Chart, new PointD(start.X, start.Y), endPoint, pen) { ToolTipText = arc.Label };
205            Chart.Group.Add(line);
206            if (intersectionPoints.Any()) {
207              arcShapes[arc] = line;
208              arcMap[line] = arc;
209              break;
210            }
211          }
212        }
213      }
214      // draw vertex shapes after the arcs so they appear on top
215      foreach (var pair in vertexPositions) {
216        var shape = vertexShapes[pair.Key];
217        shape.RedrawRequired += ShapeRedrawRequired;
218        Chart.Group.Add(shape);
219      }
220      ResumeRendering();
221    }
222
223    private void ShapeRedrawRequired(object sender, EventArgs args) {
224      layout.Run();
225    }
226
227    private Dictionary<Type, LabeledPrimitive> shapes;
228    public void AddShape(Type t, LabeledPrimitive shape) {
229      shapes[t] = shape;
230    }
231
232    public void ClearShapes() {
233      shapes.Clear();
234    }
235
236    private LabeledPrimitive CreateShape(IVertex v) {
237      var type = v.GetType();
238      if (!shapes.ContainsKey(type))
239        throw new ArgumentException(string.Format("No shape was associated with vertex type {0}", type));
240      var shape = shapes[type];
241      var p = shape.Primitive;
242      var rectangle = p as Rectangle;
243      var ellipse = p as Ellipse;
244      var brush = (SolidBrush)shape.Brush;
245
246      if (rectangle != null) {
247        var r = new Rectangle(this.Chart, shape.LowerLeft, shape.UpperRight, new Pen(shape.Pen.Color), new SolidBrush(brush.Color));
248        return new LabeledPrimitive(r, v.Label, shape.Font);
249      }
250      if (ellipse != null) {
251        var e = new Ellipse(this.Chart, shape.LowerLeft, shape.UpperRight, new Pen(shape.Pen.Color), new SolidBrush(brush.Color));
252        return new LabeledPrimitive(e, v.Label, shape.Font);
253      }
254      throw new Exception(string.Format("Unknown shape {0}.", shape));
255    }
256  }
257}
Note: See TracBrowser for help on using the repository browser.