Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2288: Small refactoring.

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.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 penColor = colors[(int)Math.Min(255, weight / max * 255)];
162        var penColor = Color.Black;
163        if (len == 2) {
164          if (double.IsInfinity(penWidth) || double.IsNaN(penWidth)) penWidth = 1;
165          pen = new Pen(penColor, (float)penWidth) { CustomEndCap = new AdjustableArrowCap(5, 3) };
166          var start = points[0];
167          var end = points[1];
168          var line = new Line(Chart, new PointD(start.X, start.Y), new PointD(end.X, end.Y));
169          var rect = target.Primitive as Rectangle;
170          var ell = target.Primitive as Ellipse;
171          PointD intersectionPoint;
172          if (rect != null)
173            intersectionPoint = rect.ComputeIntersect(line).FirstOrDefault();
174          else if (ell != null)
175            intersectionPoint = ell.ComputeIntersect(line).FirstOrDefault();
176          else
177            throw new InvalidOperationException("Unknown vertex shape.");
178          if (intersectionPoint == default(PointD))
179            intersectionPoint = end;
180          line = new Line(Chart, new PointD(start.X, start.Y), intersectionPoint, pen) { ToolTipText = arc.Label };
181          Chart.Group.Add(line);
182          arcShapes[arc] = line;
183          arcMap[line] = arc;
184        } else {
185          for (int i = 0; i < points.Length - 1; ++i) {
186            Line line;
187            var start = points[i];
188            var end = points[i + 1];
189
190            var segment = new Line(Chart, new PointD(start.X, start.Y), new PointD(end.X, end.Y));
191            var rect = target.Primitive as Rectangle;
192            var ell = target.Primitive as Ellipse;
193
194            PointD endPoint;
195            List<PointD> intersectionPoints;
196            if (rect != null) {
197              intersectionPoints = rect.ComputeIntersect(segment);
198            } else if (ell != null) {
199              intersectionPoints = ell.ComputeIntersect(segment);
200            } else {
201              throw new InvalidOperationException("Unknown vertex shape.");
202            }
203            if (intersectionPoints.Any()) {
204              endPoint = intersectionPoints.First();
205              pen = new Pen(penColor, (float)penWidth) { CustomEndCap = new AdjustableArrowCap(5, 3) };
206            } else {
207              endPoint = end;
208              pen = new Pen(penColor, (float)penWidth);
209            }
210            line = new Line(Chart, new PointD(start.X, start.Y), endPoint, pen) { ToolTipText = arc.Label };
211            Chart.Group.Add(line);
212            if (intersectionPoints.Any()) {
213              arcShapes[arc] = line;
214              arcMap[line] = arc;
215              break;
216            }
217          }
218        }
219      }
220      // draw vertex shapes after the arcs so they appear on top
221      foreach (var pair in vertexPositions) {
222        var shape = vertexShapes[pair.Key];
223        shape.RedrawRequired += ShapeRedrawRequired;
224        Chart.Group.Add(shape);
225      }
226      ResumeRendering();
227    }
228
229    private void ShapeRedrawRequired(object sender, EventArgs args) {
230      layout.Run();
231    }
232
233    private readonly Dictionary<Type, LabeledPrimitive> shapes;
234    public void AddShape(Type t, LabeledPrimitive shape) {
235      shapes[t] = shape;
236    }
237
238    public void ClearShapes() {
239      shapes.Clear();
240    }
241
242    private LabeledPrimitive CreateShape(IVertex v) {
243      var type = v.GetType();
244      if (!shapes.ContainsKey(type))
245        throw new ArgumentException(string.Format("No shape was associated with vertex type {0}", type));
246      var shape = shapes[type];
247      var p = shape.Primitive;
248      var rectangle = p as Rectangle;
249      var ellipse = p as Ellipse;
250      var brush = (SolidBrush)shape.Brush;
251
252      if (rectangle != null) {
253        var r = new Rectangle(this.Chart, shape.LowerLeft, shape.UpperRight, new Pen(shape.Pen.Color), new SolidBrush(brush.Color));
254        return new LabeledPrimitive(r, v.Label, shape.Font);
255      }
256      if (ellipse != null) {
257        var e = new Ellipse(this.Chart, shape.LowerLeft, shape.UpperRight, new Pen(shape.Pen.Color), new SolidBrush(brush.Color));
258        return new LabeledPrimitive(e, v.Label, shape.Font);
259      }
260      throw new Exception(string.Format("Unknown shape {0}.", shape));
261    }
262  }
263}
Note: See TracBrowser for help on using the repository browser.