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

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

#2288: Clean up code and add comments in the ConstrainedForceDirectedLayout class. Minor changes to view and directed graph chart. Introduced an INetworkNode interface for more flexibility. Updated cola and adaptagrams dlls with latest changes from upstream.

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