Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2288: Used localization when formatting strings. Add canceling in CreateTargetVariationExperimentDialog. Remove use of C# 4.6 language features.

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