Browse Source

Object graph visualizer - replaced Graphviz's neato.exe by my custom spline routing. Removed all native Graphviz libraries.

Routing algorithm (Graphviz's algorithm is similar): search shortest path (Dijkstra - fast enough, A* would be faster) in the following graph: vertices are corners off all boxes, moved outside of the boxes a little. Edges: any pair of vertices that can be connected by straight line not while not intersecting any box. Having the shortest path as poly-line. Just curve the corners (looks surprisingly good). Edges are routed independently, so they can cross. Having our own implementation, we could work on edge non-crossing algorithm in the future. That is, if the whole layout and routing won't be replaced by GraphSharp in the end.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5665 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Martin Koníček 16 years ago
parent
commit
324cd7923b
  1. 36
      src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
  2. 46
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphEdgeRouter.cs
  3. 12
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphMatcher.cs
  4. 16
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedEdge.cs
  5. 14
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedGraphNode.cs
  6. 62
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/Box.cs
  7. 27
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/Edge.cs
  8. 45
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/EdgeRouter.cs
  9. 104
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/GeomUtils.cs
  10. 23
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/IEdge.cs
  11. 21
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/IPoint.cs
  12. 24
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/IRect.cs
  13. 27
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/Point2D.cs
  14. 34
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/EdgeStartEnd.cs
  15. 222
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/RouteGraph.cs
  16. 30
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/RouteGraphEdge.cs
  17. 60
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/RouteVertex.cs
  18. 87
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/RoutedEdge.cs
  19. 69
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/ShortestPath/AStarShortestPathFinder.cs
  20. 7
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/NeatoEdgeRouter.cs
  21. 5
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayouter.cs

36
src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj

@ -159,6 +159,21 @@ @@ -159,6 +159,21 @@
<Compile Include="Visualizers\Graph\Layout\ContentNodeEventArgs.cs" />
<Compile Include="Visualizers\Graph\Layout\ContentPropertyNode.cs" />
<Compile Include="Visualizers\Graph\Layout\ContentNode.cs" />
<Compile Include="Visualizers\Graph\Layout\GraphEdgeRouter.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\Box.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\Edge.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\EdgeRouter.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\GeomUtils.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\IEdge.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\IPoint.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\IRect.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\Point2D.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\RouteGraph\EdgeStartEnd.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\RouteGraph\RoutedEdge.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\RouteGraph\RouteGraph.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\RouteGraph\RouteGraphEdge.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\RouteGraph\RouteVertex.cs" />
<Compile Include="Visualizers\Graph\Layout\SplineRouting\ShortestPath\AStarShortestPathFinder.cs" />
<Compile Include="Visualizers\Graph\NodeControlCache.cs" />
<Compile Include="Visualizers\Graph\ObjectGraphControl.xaml.cs">
<DependentUpon>ObjectGraphControl.xaml</DependentUpon>
@ -272,23 +287,6 @@ @@ -272,23 +287,6 @@
<Compile Include="Visualizers\PresentationBindings\EnumViewModel.cs" />
<Compile Include="Visualizers\PresentationBindings\ViewModelBase.cs" />
<None Include="COPYING" />
<None Include="Visualizers\Graph\Graphviz\cdt.dll" />
<None Include="Visualizers\Graph\Graphviz\config6" />
<None Include="Visualizers\Graph\Graphviz\graph.dll" />
<None Include="Visualizers\Graph\Graphviz\gvc.dll" />
<None Include="Visualizers\Graph\Graphviz\gvplugin_core.dll" />
<None Include="Visualizers\Graph\Graphviz\gvplugin_dot_layout.dll" />
<None Include="Visualizers\Graph\Graphviz\gvplugin_neato_layout.dll" />
<None Include="Visualizers\Graph\Graphviz\iconv.dll" />
<None Include="Visualizers\Graph\Graphviz\intl.dll" />
<None Include="Visualizers\Graph\Graphviz\libexpat.dll" />
<None Include="Visualizers\Graph\Graphviz\libglib-2.0-0.dll" />
<None Include="Visualizers\Graph\Graphviz\ltdl.dll" />
<None Include="Visualizers\Graph\Graphviz\msvcp80.dll" />
<None Include="Visualizers\Graph\Graphviz\msvcr80.dll" />
<None Include="Visualizers\Graph\Graphviz\neato.exe" />
<None Include="Visualizers\Graph\Graphviz\Pathplan.dll" />
<None Include="Visualizers\Graph\Graphviz\zlib1.dll" />
</ItemGroup>
<ItemGroup>
<Content Include="Debugger.AddIn.addin">
@ -357,8 +355,10 @@ @@ -357,8 +355,10 @@
<Folder Include="Visualizers\Graph" />
<Folder Include="Visualizers\Graph\Drawing" />
<Folder Include="Visualizers\Graph\Layout" />
<Folder Include="Visualizers\Graph\Layout\SplineRouting" />
<Folder Include="Visualizers\Graph\Layout\SplineRouting\RouteGraph" />
<Folder Include="Visualizers\Graph\Layout\SplineRouting\ShortestPath" />
<Folder Include="Visualizers\Graph\Layout\Tree" />
<Folder Include="Visualizers\Graph\Graphviz" />
<Folder Include="Visualizers\Graph\TreeModel" />
<Folder Include="Visualizers\Graph\ObjectGraph" />
<Folder Include="Visualizers\GridVisualizer\ValueProviders" />

46
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphEdgeRouter.cs

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Debugger.AddIn.Visualizers.Graph.SplineRouting;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Adapts generic <see cref="EdgeRouter" /> to work with <see cref="PositionedGraph" />.
/// </summary>
public class GraphEdgeRouter
{
EdgeRouter router = new EdgeRouter();
public GraphEdgeRouter()
{
}
public PositionedGraph RouteEdges(PositionedGraph posGraph)
{
List<RoutedEdge> routedEdges = router.RouteEdges(posGraph.Nodes, posGraph.Edges);
int i = 0;
// assume routedEdges come in the same order as posGraph.Edges
foreach (var edge in posGraph.Edges) {
SetEdgeSplinePoints(edge, routedEdges[i]);
i++;
}
return posGraph;
}
void SetEdgeSplinePoints(PositionedEdge edge, RoutedEdge routedEdge)
{
foreach (Point2D point in routedEdge.SplinePoints) {
edge.SplinePoints.Add(new Point(point.X, point.Y));
}
}
}
}

12
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphMatcher.cs

@ -22,7 +22,6 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -22,7 +22,6 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
public GraphDiff MatchGraphs(PositionedGraph oldGraph, PositionedGraph newGraph)
{
// handle any of the graphs null
if (oldGraph == null)
{
if (newGraph == null)
@ -33,14 +32,19 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -33,14 +32,19 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{
GraphDiff addAllDiff = new GraphDiff();
foreach (PositionedGraphNode newNode in newGraph.Nodes)
{
addAllDiff.SetAdded(newNode);
}
return addAllDiff;
}
}
else if (newGraph == null)
{
GraphDiff removeAllDiff = new GraphDiff();
foreach (PositionedGraphNode oldNode in oldGraph.Nodes)
removeAllDiff.SetRemoved(oldNode);
return removeAllDiff;
}
// both graph are not null
// none of the graphs is null
GraphDiff diff = new GraphDiff();
Dictionary<int, PositionedGraphNode> newNodeForHashCode = buildHashToNodeMap(newGraph);

16
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedEdge.cs

@ -15,11 +15,9 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -15,11 +15,9 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary>
/// Edge with position information.
/// </summary>
public class PositionedEdge : NamedEdge<PositionedNodeProperty, PositionedGraphNode>
public class PositionedEdge : NamedEdge<PositionedNodeProperty, PositionedGraphNode>, SplineRouting.IEdge
{
private IList<Point> splinePoints = new List<Point>();
// ReadOnlyCollection<Point> ?
/// <summary>
/// Control points of edge's spline, in standart format: 1 start point + 3 points per segment
/// </summary>
@ -39,5 +37,17 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -39,5 +37,17 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// Drawn spline representation of this edge.
/// </summary>
public System.Windows.Shapes.Path Spline { get; set; }
public SplineRouting.IRect From {
get {
return this.Source.ContainingNode;
}
}
public SplineRouting.IRect To {
get {
return this.Target;
}
}
}
}

14
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedGraphNode.cs

@ -7,9 +7,9 @@ @@ -7,9 +7,9 @@
using System;
using System.Collections.Generic;
using Debugger.AddIn.Visualizers.Graph.Drawing;
using System.Windows;
using System.Linq;
using System.Windows;
using Debugger.AddIn.Visualizers.Graph.Drawing;
using Debugger.AddIn.Visualizers.Utils;
namespace Debugger.AddIn.Visualizers.Graph.Layout
@ -17,7 +17,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -17,7 +17,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary>
/// ObjectNode with added position information.
/// </summary>
public class PositionedGraphNode
public class PositionedGraphNode : SplineRouting.IRect
{
public static readonly double MaxHeight = 300;
@ -158,5 +158,13 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -158,5 +158,13 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
this.ContentNodeCollapsed(sender, e);
}
#endregion
SplineRouting.Box box;
public SplineRouting.IRect Inflated(double padding)
{
if (box == null)
box = new SplineRouting.Box(this);
return box.Inflated(padding);
}
}
}

62
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/Box.cs

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Description of Box.
/// </summary>
public class Box : IRect, ICloneable
{
public Rectangle rect;
Box inflatedCache;
public Box(double left, double top, double width, double height)
{
this.rect = new Rectangle();
this.Left = left;
this.Top = top;
this.Width = width;
this.Height = height;
}
public Box(IRect original)
{
this.rect = new Rectangle();
this.Left = original.Left;
this.Top = original.Top;
this.Width = original.Width;
this.Height = original.Height;
}
public double Left { get { return Canvas.GetLeft(rect); } set {Canvas.SetLeft(rect, value); } }
public double Top { get { return Canvas.GetTop(rect); } set {Canvas.SetTop(rect, value); } }
public double Width { get { return rect.Width; } set { rect.Width = value; } }
public double Height { get { return rect.Height; } set { rect.Height = value; } }
public IRect Inflated(double padding)
{
//if (inflatedCache.ContainsKey(padding))
if (inflatedCache == null)
{
inflatedCache = GeomUtils.InflateRect(this, padding);
}
return inflatedCache;
}
public object Clone()
{
return new Box(this.Left, this.Top, this.Width, this.Height);
}
}
}

27
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/Edge.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System.Collections.Generic;
using System.Linq;
using System;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Description of Edge.
/// </summary>
public class Edge : IEdge
{
public Edge()
{
}
public IRect From { get; set; }
public IRect To { get; set; }
public IPoint StartPoint { get; set; }
public IPoint EndPoint { get; set; }
}
}

45
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/EdgeRouter.cs

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System.Collections.Generic;
using System.Linq;
using System;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Description of EdgeRouter.
/// </summary>
public class EdgeRouter
{
public EdgeRouter()
{
}
public List<RoutedEdge> RouteEdges(IEnumerable<IRect> nodes, IEnumerable<IEdge> edges)
{
var routeGraph = RouteGraph.InitializeVertices(nodes, edges);
List<RoutedEdge> routedEdges = new List<RoutedEdge>();
var occludedEdges = new List<IEdge>();
foreach (IEdge edge in edges) {
var straightEdge = routeGraph.TryRouteEdgeStraight(edge);
if (straightEdge != null)
routedEdges.Add(straightEdge);
else
occludedEdges.Add(edge);
}
if (occludedEdges.Count > 0) {
// there are some edges that couldn't be routed as straight lines
routeGraph.ComputeVisibilityGraph();
foreach (IEdge edge in occludedEdges) {
RoutedEdge routedEdge = routeGraph.RouteEdge(edge);
routedEdges.Add(routedEdge);
}
}
return routedEdges;
}
}
}

104
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/GeomUtils.cs

@ -0,0 +1,104 @@ @@ -0,0 +1,104 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Description of GeomUtils.
/// </summary>
public class GeomUtils
{
static readonly double eps = 1e-8;
public GeomUtils()
{
}
public static IPoint RectCenter(IRect rect)
{
return new Point2D(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2);
}
public static double LineLenght(IPoint lineStart, IPoint lineEnd)
{
var dx = lineEnd.X - lineStart.X;
var dy = lineEnd.Y - lineStart.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
public static Point2D? LineRectIntersection(IPoint lineStart, IPoint lineEnd, IRect rect)
{
double vx = lineEnd.X - lineStart.X;
double vy = lineEnd.Y - lineStart.Y;
double right = rect.Left + rect.Width;
double bottom = rect.Top + rect.Height;
var isectTop = isectHoriz(lineStart, vx, vy, rect.Top, rect.Left, right);
if (isectTop != null)
return isectTop;
var isectBottom = isectHoriz(lineStart, vx, vy, bottom, rect.Left, right);
if (isectBottom != null)
return isectBottom;
var isectLeft = isectVert(lineStart, vx, vy, rect.Left, rect.Top, bottom);
if (isectLeft != null)
return isectLeft;
var isectRight = isectVert(lineStart, vx, vy, right, rect.Top, bottom);
if (isectRight != null)
return isectRight;
return null;
}
static Point2D? isectHoriz(IPoint lineStart, double vx, double vy, double yBound, double left, double right)
{
if (Math.Abs(vy) < eps)
return null;
double t = (yBound - lineStart.Y) / vy;
if (t > 0 && t < 1)
{
double iX = (lineStart.X + t * vx);
if (iX >= left && iX <= right)
return new Point2D(iX, yBound);
}
return null;
}
static Point2D? isectVert(IPoint lineStart, double vx, double vy, double xBound, double top, double bottom)
{
if (Math.Abs(vx) < eps)
return null;
double t = (xBound - lineStart.X) / vx;
if (t > 0 && t < 1)
{
double iY = (lineStart.Y + t * vy);
if (iY >= top && iY <= bottom)
return new Point2D(xBound, iY);
}
return null;
}
public static Box InflateRect(IRect rect, double padding)
{
return new Box(rect.Left - padding, rect.Top - padding, rect.Width + padding * 2, rect.Height + padding * 2);
}
public static Point2D Interpolate(Point2D pointA, Point2D pointB, double t)
{
double b = 1 - t;
return new Point2D(pointA.X * t + pointB.X * b, pointA.Y * t + pointB.Y * b);
}
/*public static double LineLineIntersection(Point line1Start, Point line1End, Point line2Start, Point line2End)
{
}*/
}
}

23
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/IEdge.cs

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System.Collections.Generic;
using System.Linq;
using System;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Enables passing any type of graph (implementing IRect, IEdge) into EdgeRouter.
/// </summary>
public interface IEdge
{
IRect From { get; }
IRect To { get; }
//IPoint StartPoint { get; set; }
//IPoint EndPoint { get; set; }
}
}

21
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/IPoint.cs

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System.Collections.Generic;
using System.Linq;
using System;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Enables passing any type of graph (implementing IRect, IEdge) into EdgeRouter.
/// </summary>
public interface IPoint
{
double X { get; set; }
double Y { get; set; }
}
}

24
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/IRect.cs

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System.Collections.Generic;
using System.Linq;
using System;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Enables passing any type of graph (implementing IRect, IEdge) into EdgeRouter.
/// </summary>
public interface IRect
{
double Left { get; }
double Top { get; }
double Width { get; }
double Height { get; }
IRect Inflated(double padding);
}
}

27
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/Point2D.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System.Collections.Generic;
using System.Linq;
using System;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Description of Point2D.
/// </summary>
public struct Point2D : IPoint
{
public Point2D(double x, double y) : this()
{
this.X = x;
this.Y = y;
}
public double X { get; set; }
public double Y { get; set; }
}
}

34
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/EdgeStartEnd.cs

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System.Collections.Generic;
using System.Linq;
using System;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Description of EdgeStartEnd.
/// </summary>
public class EdgeStartEnd
{
public IRect From { get; set; }
public IRect To { get; set; }
public override bool Equals(object obj)
{
var other = obj as EdgeStartEnd;
if (other == null)
return false;
return this.From == other.From && this.To == other.To;
}
public override int GetHashCode()
{
return this.From.GetHashCode() ^ this.To.GetHashCode();
}
}
}

222
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/RouteGraph.cs

@ -0,0 +1,222 @@ @@ -0,0 +1,222 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Description of RouteGraph.
/// </summary>
public class RouteGraph
{
static readonly double boxPadding = 15;
//static readonly double boxSafetyMargin = 5; // inflate boxes for collision testing
static readonly double multiEdgeGap = 10;
List<IRect> boxes = new List<IRect>();
public List<IRect> Boxes {
get { return boxes; }
}
List<RouteVertex> vertices = new List<RouteVertex>();
public List<RouteVertex> Vertices {
get { return vertices; }
}
AStarShortestPathFinder pathFinder;
public RouteGraph()
{
pathFinder = new AStarShortestPathFinder(this);
}
public static RouteGraph InitializeVertices(IEnumerable<IRect> nodes, IEnumerable<IEdge> edges)
{
var graph = new RouteGraph();
// add vertices for node corners
foreach (var node in nodes) {
graph.Boxes.Add(node);
foreach (var vertex in GetRectCorners(node, boxPadding)) {
graph.Vertices.Add(vertex);
}
}
// add vertices for egde endpoints
foreach (var multiEdgeGroup in edges.GroupBy(edge => GetStartEnd(edge))) {
int multiEdgeCount = multiEdgeGroup.Count();
IRect fromRect = multiEdgeGroup.First().From;
IRect toRect = multiEdgeGroup.First().To;
var sourceCenter = GeomUtils.RectCenter(fromRect);
var targetCenter = GeomUtils.RectCenter(toRect);
if (Math.Abs(sourceCenter.X - targetCenter.X) > Math.Abs(sourceCenter.Y - targetCenter.Y) ||
(fromRect == toRect))
{
// the line is horizontal
double multiEdgeSpanSource = GetMultiEdgeSpan(fromRect.Height, multiEdgeCount, multiEdgeGap);
double multiEdgeSpanTarget = GetMultiEdgeSpan(toRect.Height, multiEdgeCount, multiEdgeGap);
double originSourceCurrentY = sourceCenter.Y - multiEdgeSpanSource / 2;
double originTargetCurrentY = targetCenter.Y - multiEdgeSpanTarget / 2;
foreach (var edge in multiEdgeGroup) {
Point2D sourceOrigin = new Point2D(sourceCenter.X, originSourceCurrentY);
Point2D targetOrigin = new Point2D(targetCenter.X, originTargetCurrentY);
// Here user could provide custom edgeStart and edgeEnd
// inflate boxes a little so that edgeStart and edgeEnd are a little outside of the box (to prevent floating point errors)
if (edge.From == edge.To) {
var edgeStart = new Point2D(fromRect.Left + fromRect.Width + 0.01, originSourceCurrentY);
var edgeEnd = new Point2D(fromRect.Left + fromRect.Width / 2, fromRect.Top);
graph.AddEdgeEndpointVertices(edge, edgeStart, edgeEnd);
} else {
var edgeStart = GeomUtils.LineRectIntersection(sourceOrigin, targetOrigin, edge.From.Inflated(1e-3));
var edgeEnd = GeomUtils.LineRectIntersection(sourceOrigin, targetOrigin, edge.To.Inflated(1e-3));
graph.AddEdgeEndpointVertices(edge, edgeStart, edgeEnd);
}
originSourceCurrentY += multiEdgeSpanSource / (multiEdgeCount - 1);
originTargetCurrentY += multiEdgeSpanTarget / (multiEdgeCount - 1);
}
}
else
{
// the line is vertical
double multiEdgeSpanSource = GetMultiEdgeSpan(fromRect.Width, multiEdgeCount, multiEdgeGap);
double multiEdgeSpanTarget = GetMultiEdgeSpan(toRect.Width, multiEdgeCount, multiEdgeGap);
double originSourceCurrentX = sourceCenter.X - multiEdgeSpanSource / 2;
double originTargetCurrentX = targetCenter.X - multiEdgeSpanTarget / 2;
foreach (var edge in multiEdgeGroup) {
Point2D sourceOrigin = new Point2D(originSourceCurrentX, sourceCenter.Y);
Point2D targetOrigin = new Point2D(originTargetCurrentX, targetCenter.Y);
// Here user could provide custom edgeStart and edgeEnd
// inflate boxes a little so that edgeStart and edgeEnd are a little outside of the box (to prevent floating point errors)
var edgeStart = GeomUtils.LineRectIntersection(sourceOrigin, targetOrigin, edge.From.Inflated(1e-3));
var edgeEnd = GeomUtils.LineRectIntersection(sourceOrigin, targetOrigin, edge.To.Inflated(1e-3));
graph.AddEdgeEndpointVertices(edge, edgeStart, edgeEnd);
originSourceCurrentX += multiEdgeSpanSource / (multiEdgeCount - 1);
originTargetCurrentX += multiEdgeSpanTarget / (multiEdgeCount - 1);
}
}
}
return graph;
}
void AddEdgeEndpointVertices(IEdge edge, Point2D? edgeStart, Point2D? edgeEnd)
{
if (edgeStart == null || edgeEnd == null) {
// should not happen
return;
}
var startPoint = new RouteVertex(edgeStart.Value.X, edgeStart.Value.Y);
startPoint.IsEdgeEndpoint = true;
var endPoint = new RouteVertex(edgeEnd.Value.X, edgeEnd.Value.Y);
endPoint.IsEdgeEndpoint = true;
this.vertices.Add(startPoint);
this.vertices.Add(endPoint);
// remember what RouteVertices we created for this user edge
this.setStartVertex(edge, startPoint);
this.setEndVertex(edge, endPoint);
}
static IEnumerable<RouteVertex> GetRectCorners(IRect rect, double padding)
{
double left = rect.Left - padding;
double top = rect.Top - padding;
double right = left + rect.Width + 2 * padding;
double bottom = top + rect.Height + 2 * padding;
yield return new RouteVertex(left, top);
yield return new RouteVertex(right, top);
yield return new RouteVertex(right, bottom);
yield return new RouteVertex(left, bottom);
}
public void ComputeVisibilityGraph()
{
for (int i = 0; i < this.Vertices.Count; i++) {
for (int j = i + 1; j < this.Vertices.Count; j++) {
var vertex = this.Vertices[i];
var vertex2 = this.Vertices[j];
if (Visible(vertex, vertex2))
{
// bidirectional edge
vertex.AddNeighbor(vertex2);
vertex2.AddNeighbor(vertex);
}
}
}
}
public bool Visible(IPoint vertex, IPoint vertex2)
{
// test for intersection with every box
foreach (var rect in this.Boxes) {
if (GeomUtils.LineRectIntersection(vertex, vertex2, rect) != null)
return false;
}
return true;
}
public RoutedEdge RouteEdge(IEdge edge)
{
var pathVertices = pathFinder.FindShortestPath(getStartVertex(edge), getEndVertex(edge));
return BuildRoutedEdge(pathVertices);
}
public RoutedEdge TryRouteEdgeStraight(IEdge edge)
{
var startVertex = getStartVertex(edge);
var endVertex = getEndVertex(edge);
if (Visible(startVertex, endVertex)) {
// route the edge straight
return BuildRoutedEdge(new [] {startVertex, endVertex });
} else
return null;
}
public RoutedEdge BuildRoutedEdge(IEnumerable<IPoint> points)
{
var routedEdge = new RoutedEdge();
foreach (var point in points) {
routedEdge.Points.Add(new Point2D(point.X, point.Y));
}
return routedEdge;
}
Dictionary<IEdge, RouteVertex> edgeStarts = new Dictionary<IEdge, RouteVertex>();
Dictionary<IEdge, RouteVertex> edgeEnds = new Dictionary<IEdge, RouteVertex>();
RouteVertex getStartVertex(IEdge edge)
{
return edgeStarts[edge];
}
RouteVertex getEndVertex(IEdge edge)
{
return edgeEnds[edge];
}
void setStartVertex(IEdge edge, RouteVertex value)
{
edgeStarts[edge] = value;
}
void setEndVertex(IEdge edge, RouteVertex value)
{
edgeEnds[edge] = value;
}
static EdgeStartEnd GetStartEnd(IEdge edge)
{
return new EdgeStartEnd { From = edge.From, To = edge.To };
}
static double GetMultiEdgeSpan(double space, int multiEdgeCount, double multiEdgeGap)
{
if ((multiEdgeCount + 1) * multiEdgeGap < space)
// the edges fit, maintain the gap
return (multiEdgeCount - 1) * multiEdgeGap;
else
// there are too many edges, we have to make smaller gaps to fit edges into given space
return space - multiEdgeGap;
}
}
}

30
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/RouteGraphEdge.cs

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System.Collections.Generic;
using System.Linq;
using System;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Edge in the RouteGraph.
/// </summary>
public class RouteGraphEdge
{
public RouteVertex StartVertex { get; private set; }
public RouteVertex EndVertex { get; private set; }
public double Length { get; private set; }
public RouteGraphEdge(RouteVertex startVertex, RouteVertex endVertex)
{
this.StartVertex = startVertex;
this.EndVertex = endVertex;
this.Length = GeomUtils.LineLenght(startVertex, endVertex);
}
}
}

60
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/RouteVertex.cs

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Description of RouteVertex.
/// </summary>
public class RouteVertex : IPoint
{
public double X { get; set; }
public double Y { get; set; }
public RouteVertex(double x, double y)
{
this.X = x;
this.Y = y;
}
List<RouteGraphEdge> neighbors = new List<RouteGraphEdge>();
public List<RouteGraphEdge> Neighbors {
get { return neighbors; }
}
public RouteVertex()
{
Reset();
IsUsed = false;
IsEdgeEndpoint = false;
}
public void AddNeighbor(RouteVertex target)
{
this.Neighbors.Add(new RouteGraphEdge(this, target));
}
public double Distance { get; set; }
public bool IsPermanent { get; set; }
public RouteVertex Predecessor { get; set; }
public bool IsEdgeEndpoint { get; set; }
public bool IsUsed { get; set; }
public bool IsAvailable { get { return !IsUsed && !IsEdgeEndpoint; } }
public void Reset()
{
Distance = 10e+6;
IsPermanent = false;
Predecessor = null;
//IsUsed = false;
//IsEdgeEndpoint = false;
}
}
}

87
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/RouteGraph/RoutedEdge.cs

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// The result of the main method EdgeRouter.RouteEdges(), passed back to the user.
/// </summary>
public class RoutedEdge
{
public double Lenght {
get {
double len = 0;
for (int i = 1; i < this.Points.Count; i++) {
len += GeomUtils.LineLenght(this.Points[i - 1], this.Points[i]);
}
return len;
}
}
List<Point2D> points;
public List<Point2D> Points
{
get { return this.points; }
}
public RoutedEdge()
{
this.points = new List<Point2D>();
}
ReadOnlyCollection<Point2D> splinePoints;
public ReadOnlyCollection<Point2D> SplinePoints
{
get
{
if (this.splinePoints == null)
{
List<Point2D> sPoints = RouteSpline(this.Points);
splinePoints = sPoints.AsReadOnly();
}
return this.splinePoints;
}
}
private static double tBend = 0.4;
private List<Point2D> RouteSpline(List<Point2D> anchorPoints)
{
var result = new List<Point2D>();
if (anchorPoints.Count == 0)
return new List<Point2D>();
Point2D point1 = anchorPoints[0];
result.Add(point1);
for (int i = 2; i < anchorPoints.Count; i++) {
var point2 = anchorPoints[i - 1];
var point3 = anchorPoints[i];
var anchor1 = GeomUtils.Interpolate(point1, point2, 1 - tBend);
var anchor2 = GeomUtils.Interpolate(point2, point3, tBend);
// straight segment
result.Add(anchor1); // guide point1 -> anchor1
result.Add(point1);
result.Add(anchor1); // guide anchor1 -> point2
// bend
result.Add(point2); // guide anchor1 -> point2 (more carved ?)
result.Add(point2);
result.Add(anchor2); // guide point2 -> anchor2
point1 = anchor2;
}
// last straight segment
var lastPoint = anchorPoints[anchorPoints.Count - 1];
result.Add(lastPoint);
result.Add(point1);
result.Add(lastPoint);
return result;
}
}
}

69
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/SplineRouting/ShortestPath/AStarShortestPathFinder.cs

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Martin Koníček" email="martin.konicek@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System.Collections.Generic;
using System.Linq;
using System;
namespace Debugger.AddIn.Visualizers.Graph.SplineRouting
{
/// <summary>
/// Description of AStarShortestPathFinder.
/// </summary>
public class AStarShortestPathFinder
{
RouteGraph graph;
public AStarShortestPathFinder(RouteGraph routeGraph)
{
this.graph = routeGraph;
}
public List<RouteVertex> FindShortestPath(RouteVertex start, RouteVertex end)
{
start.IsEdgeEndpoint = false;
end.IsEdgeEndpoint = false;
foreach (var vertex in this.graph.Vertices) {
vertex.Reset();
}
start.Distance = 0;
bool reached = false;
while (!reached)
{
RouteVertex minVertex = null;
foreach (var minCandidate in graph.Vertices.Where(v => !v.IsPermanent && v.IsAvailable)) {
if (minVertex == null || minCandidate.Distance < minVertex.Distance) {
minVertex = minCandidate;
}
}
minVertex.IsPermanent = true;
if (minVertex == end) {
reached = true;
}
foreach (var edge in minVertex.Neighbors) {
if (minVertex.Distance + edge.Length < edge.EndVertex.Distance) {
edge.EndVertex.Distance = minVertex.Distance + edge.Length;
edge.EndVertex.Predecessor = minVertex;
}
}
}
List<RouteVertex> path = new List<RouteVertex>();
RouteVertex pathVertex = end;
while (pathVertex != start) {
if (pathVertex == null)
break;
path.Add(pathVertex);
//pathVertex.IsUsed = true;
pathVertex = pathVertex.Predecessor;
}
path.Add(start);
start.IsEdgeEndpoint = true;
end.IsEdgeEndpoint = true;
path.Reverse();
return path;
}
}
}

7
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/NeatoEdgeRouter.cs

@ -33,14 +33,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -33,14 +33,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{
DotFormatter dotFormatter = new BoxDotFormatter(graphWithNodesPositioned);
// start Neato.exe
NeatoProcess neatoProcess = NeatoProcess.Start();
// convert PosGraph to .dot string
string dotGraphString = dotFormatter.OutputGraphInDotFormat();
// pass to neato.exe
string dotGraphStringWithPositions = neatoProcess.CalculatePositions(dotGraphString);
// pass to neato.exe and wait for output
string dotGraphStringWithPositions = NeatoProcess.Start().CalculatePositions(dotGraphString);
// parse edge positions from neato's plain output format
PositionedGraph result = dotFormatter.ParseEdgePositions(dotGraphStringWithPositions);

5
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayouter.cs

@ -51,8 +51,9 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -51,8 +51,9 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
// second layout pass
calculateNodePosRecursive((TreeGraphNode)resultGraph.Root, 0, 0);
var neatoRouter = new NeatoEdgeRouter();
resultGraph = neatoRouter.CalculateEdges(resultGraph);
//var neatoRouter = new NeatoEdgeRouter();
//resultGraph = neatoRouter.CalculateEdges(resultGraph);
resultGraph = new GraphEdgeRouter().RouteEdges(resultGraph);
return resultGraph;
}

Loading…
Cancel
Save