Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2288: Simplify and optimize code for cluster identification in ConstrainedForceDirectedLayout.cs. Introduce a TrackBar for adjusting network threshold in the RunCollectionVariableInteractionNetworkView. Minor improvements to the DirectedGraphChart (work in progress).

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