Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 15421 was 15421, checked in by bburlacu, 7 years ago

#2288: Sync with trunk + Minor refactor.

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