Browse Source

Graph visualizer - edges are routed by Graphviz's neato.exe and drawn by WPF.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4085 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Martin Koníček 16 years ago
parent
commit
ca05ee092e
  1. 25
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj
  2. 84
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs
  3. 19
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Point.cs
  4. 6
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs
  5. 15
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedGraph.cs
  6. 19
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs
  7. 192
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/DotGraph.cs
  8. 13
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/IdGenerator.cs
  9. 60
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoEdgeRouter.cs
  10. 71
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoPositionTransform.cs
  11. 96
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoProcess.cs
  12. 4
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs

25
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj

@ -170,13 +170,15 @@ @@ -170,13 +170,15 @@
<Compile Include="Src\Visualizers\Graph\DebuggerVisualizerException.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\GraphDrawer.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\NodeControl.xaml.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Point.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedEdge.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedGraph.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\PositionedNode.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\DotGraph.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\IdGenerator.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\LayoutDirection.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoEdgeRouter.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoRunner.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoPositionTransform.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\NeatoProcess.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeEdge.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeLayouter.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\Tree\TreeNode.cs" />
@ -208,6 +210,21 @@ @@ -208,6 +210,21 @@
<Compile Include="Src\Visualizers\PresentationBindings\ViewModelBase.cs" />
<None Include="COPYING" />
</ItemGroup>
<ItemGroup>
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\cdt.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\config6" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\graph.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\gvc.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\gvplugin_core.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\gvplugin_dot_layout.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\gvplugin_gd.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\gvplugin_neato_layout.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\gvplugin_pango.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\libexpat.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\ltdl.dll" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\neato.exe" />
<GraphvizBinaries Include="Src\Visualizers\Graph\Graphviz\Pathplan.dll" />
</ItemGroup>
<ItemGroup>
<Content Include="Client.config">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
@ -271,6 +288,7 @@ @@ -271,6 +288,7 @@
<Folder Include="Src\Visualizers\Graph\Drawing" />
<Folder Include="Src\Visualizers\Graph\Layout" />
<Folder Include="Src\Visualizers\Graph\Layout\Tree" />
<Folder Include="Src\Visualizers\Graph\Graphviz" />
<Folder Include="Src\Visualizers\Graph\ObjectGraph" />
<Folder Include="Src\Visualizers\Graph\Utils" />
<Folder Include="Src\Visualizers\PresentationBindings" />
@ -307,4 +325,7 @@ @@ -307,4 +325,7 @@
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
<Import Project="PostBuildEvent.proj" />
<PropertyGroup>
<PostBuildEvent>xcopy "$(ProjectDir)Src\Visualizers\Graph\Graphviz\*.*" "$(TargetDir)" /Y</PostBuildEvent>
</PropertyGroup>
</Project>

84
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs

@ -11,33 +11,107 @@ using Debugger.AddIn.Visualizers.Graph; @@ -11,33 +11,107 @@ using Debugger.AddIn.Visualizers.Graph;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
using System.Windows.Shapes;
using Debugger.AddIn.Visualizers.Graph.Drawing;
using Debugger.AddIn.Visualizers.Graph.Layout;
namespace Debugger.AddIn.Visualizers.Graph
{
/// <summary>
/// Draws ObjectGraph on a Canvas.
/// Draws <see cref="PositionedGraph"></see> on Canvas.
/// </summary>
public class GraphDrawer
{
private ObjectGraph graph;
public GraphDrawer(ObjectGraph graph)
public GraphDrawer()
{
this.graph = graph;
}
/// <summary>
/// Draws <see cref="PositionedGraph"></see> on Canvas.
/// </summary>
/// <param name="posGraph">Graph to draw.</param>
/// <param name="canvas">Destination Canvas.</param>
public static void Draw(PositionedGraph posGraph, Canvas canvas)
{
canvas.Children.Clear();
// draw nodes
foreach (PositionedNode node in posGraph.Nodes)
{
canvas.Children.Add(node.NodeVisualControl);
Canvas.SetLeft(node.NodeVisualControl, node.Left);
Canvas.SetTop(node.NodeVisualControl, node.Top);
}
// draw edges
foreach (PositionedEdge edge in posGraph.Edges)
{
Path edgePath = createEdgeWithArrow(edge);
canvas.Children.Add(edgePath);
}
}
private static Path createEdgeWithArrow(PositionedEdge edge)
{
Path path = new Path();
path.Stroke = Brushes.Black;
path.Fill = Brushes.Black;
path.StrokeThickness = 1;
PathGeometry geometry = new PathGeometry();
geometry.Figures.Add(createEdgeSpline(edge));
geometry.Figures.Add(createEdgeArrow(edge));
path.Data = geometry;
return path;
}
private static PathFigure createEdgeSpline(PositionedEdge edge)
{
PathFigure figure = new PathFigure();
figure.IsClosed = false;
figure.IsFilled = false;
figure.StartPoint = edge.SplinePoints[0];
for (int i = 1; i < edge.SplinePoints.Count; i += 3)
{
figure.Segments.Add(new BezierSegment(edge.SplinePoints[i], edge.SplinePoints[i + 1], edge.SplinePoints[i + 2], true));
}
return figure;
}
private static PathFigure createEdgeArrow(PositionedEdge edge)
{
PathFigure arrowFigure = new PathFigure();
arrowFigure.IsClosed = true;
arrowFigure.IsFilled = true;
Point endPoint = edge.SplinePoints[edge.SplinePoints.Count - 1];
Point endHandlePoint = edge.SplinePoints[edge.SplinePoints.Count - 2];
Vector tangent = endPoint - endHandlePoint;
tangent.Normalize();
tangent = tangent * 20;
Point basePoint = endPoint - 0.2 * tangent;
arrowFigure.StartPoint = basePoint + tangent * 0.4; // arrow tip
Vector tangent2 = rotate90(tangent);
arrowFigure.Segments.Add(new LineSegment(basePoint + tangent2 * 0.15, true));
arrowFigure.Segments.Add(new LineSegment(basePoint - tangent2 * 0.15, true));
return arrowFigure;
}
private static Vector rotate90(Vector v)
{
// (x, y) -> (y, -x)
double t = v.X;
v.X = v.Y;
v.Y = -t;
return v;
}
}
}

19
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Point.cs

@ -1,19 +0,0 @@ @@ -1,19 +0,0 @@
// <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;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Description of Point.
/// </summary>
public struct Point
{
public double X { get; set; }
public double Y { get; set; }
}
}

6
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedEdge.cs

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
// </file>
using System;
using System.Collections.Generic;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
@ -14,9 +15,12 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -14,9 +15,12 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary>
public class PositionedEdge : NamedEdge<PositionedNode>
{
private IList<Point> splinePoints = null;
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>
public IList<Point> SplinePoints
{
get

15
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedGraph.cs

@ -6,6 +6,8 @@ @@ -6,6 +6,8 @@
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
@ -16,6 +18,19 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -16,6 +18,19 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{
internal List<PositionedNode> nodes = new List<PositionedNode>();
public System.Windows.Rect BoundingRect
{
get
{
double minX = nodes.Select(node => node.Left).Min();
double maxX = nodes.Select(node => node.Left + node.Width).Max();
double minY = nodes.Select(node => node.Top).Min();
double maxY = nodes.Select(node => node.Top + node.Height).Max();
return new Rect(minX, minY, maxX - minX, maxY - minY);
}
}
/// <summary>
/// All nodes in the graph.
/// </summary>

19
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using Debugger.AddIn.Visualizers.Graph.Drawing;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
@ -22,7 +23,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -22,7 +23,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
public double Left { get; set; }
public double Top { get; set; }
public double Width
public double Width
{
get { return NodeVisualControl.DesiredSize.Width; }
}
@ -31,12 +32,24 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -31,12 +32,24 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
get { return NodeVisualControl.DesiredSize.Height; }
}
public Point LeftTop
{
get { return new Point(Left, Top); }
}
public Point Center
{
get { return new Point(Left + Width / 2, Top + Height / 2); }
}
public Rect Rect { get { return new Rect(Left, Top, Width, Height); } }
private NodeControl nodeVisualControl;
/// <summary>
/// Visual control to be shown for this node.
/// </summary>
public NodeControl NodeVisualControl
{
public NodeControl NodeVisualControl
{
get
{
return this.nodeVisualControl;

192
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/DotGraph.cs

@ -0,0 +1,192 @@ @@ -0,0 +1,192 @@
// <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.Globalization;
using System.IO;
using System.Text;
using System.Linq;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Converts <see cref="PositionedGraph/> to Graphviz's string (dot format) and back (from plain format).
/// </summary>
public class DotGraph
{
private PositionedGraph posGraph;
NeatoPositionTransform transform;
// state (node and edge names) needed for converting back
private Dictionary<PositionedNode, string> nodeNames = new Dictionary<PositionedNode, string>();
private Dictionary<PositionedEdge, string> edgeNames = new Dictionary<PositionedEdge, string>();
private CultureInfo formatCulture = CultureInfo.GetCultureInfo("en-US");
/// <summary>
/// Used for generating node and edge names.
/// </summary>
private IdGenerator genId = new IdGenerator();
public DotGraph(PositionedGraph posGraph)
{
if (posGraph.Nodes.Count() == 0)
{
throw new ArgumentException("Cannot process empty graphs.");
}
this.posGraph = posGraph;
this.transform = new NeatoPositionTransform(this.posGraph.BoundingRect);
}
/// <summary>
/// Gets Graphviz's dot format string for the positioned graph.
/// </summary>
public string DotGraphString
{
get
{
StringBuilder dotStringBuilder = new StringBuilder("digraph G { node [shape = box];");
foreach (PositionedNode posNode in this.posGraph.Nodes)
{
appendPosNode(posNode, dotStringBuilder);
}
foreach (PositionedEdge posEdge in this.posGraph.Edges)
{
appendPosEdge(posEdge, dotStringBuilder);
}
dotStringBuilder.AppendLine("}");
return dotStringBuilder.ToString();
}
}
private void appendPosNode(PositionedNode node, StringBuilder builder)
{
string nodeName = genId.GetNextId().ToString();
nodeNames[node] = nodeName;
Rect neatoInput = transform.NodeToNeatoInput(node);
string dotFormatNode =
string.Format(formatCulture,
"{0} [pos=\"{1},{2}!\" width=\"{3}\" height=\"{4}\"];",
nodeName, neatoInput.Location.X, neatoInput.Location.Y, neatoInput.Width, neatoInput.Height);
builder.AppendLine(dotFormatNode);
}
private void appendPosEdge(PositionedEdge edge, StringBuilder builder)
{
string sourceNodeName = nodeNames[edge.SourceNode];
string targetNodeName = nodeNames[edge.TargetNode];
builder.AppendLine(string.Format("{0} -> {1}", sourceNodeName, targetNodeName));
}
private bool validateSplinePoints(PositionedEdge edge)
{
// must have correct number of points: one start point and 3 points for every bezier segment
return ((edge.SplinePoints.Count - 1) % 3) == 0;
}
/// <summary>
/// Parses edge positions (from Graphviz's plain format) and sets these positions to underlying positioned graph.
/// </summary>
/// <param name="dotGraphString">Graph with positions in Graphviz's plain format</param>
/// <returns><see cref="PositionedGraph"/> with edge positions filled.</returns>
public PositionedGraph ParseEdgePositions(string dotGraphString)
{
using (StringReader reader = new System.IO.StringReader(dotGraphString))
{
skipAfterPattern(reader, "node " + nodeNames[posGraph.Nodes.First()] + " ");
Point neatoFirstNodePos = readPoint(reader);
PositionedNode firstNode = posGraph.Nodes.First();
Point firstNodePosOur = transform.AsNeato(firstNode.Center);
// determine how Neato shifted the nodes
transform.NeatoShiftX = neatoFirstNodePos.X - firstNodePosOur.X;
transform.NeatoShiftY = neatoFirstNodePos.Y - firstNodePosOur.Y;
foreach (PositionedEdge posEdge in posGraph.Edges)
{
skipAfterPattern(reader, "edge ");
readWord(reader); // source node name
readWord(reader); // target node name
int splinePointCount = readInt(reader);
for (int i = 0; i < splinePointCount; i++)
{
Point edgePoint = readPoint(reader);
edgePoint = transform.FromNeatoOutput(edgePoint);
posEdge.SplinePoints.Add(edgePoint);
}
bool edgeOk = validateSplinePoints(posEdge);
if (!edgeOk)
throw new DebuggerVisualizerException("Parsed edge invalid");
}
}
// return original graph with filled edge positions
return this.posGraph;
}
private Point readPoint(TextReader reader)
{
double x = readDouble(reader);
double y = readDouble(reader);
return new Point(x, y);
}
private double readDouble(TextReader reader)
{
return double.Parse(readWord(reader), formatCulture);
}
private int readInt(TextReader reader)
{
return int.Parse(readWord(reader));
}
private string readWord(TextReader reader)
{
StringBuilder word = new StringBuilder();
int ch = ' ';
while ((ch = reader.Read()) != ' ')
{
if (ch == -1 || ch == '\n' || ch == '\t')
break;
word.Append((char)ch);
}
return word.ToString();
}
private bool skipAfterPattern(StringReader reader, string pattern)
{
int ch = -1;
int pIndex = 0;
int pTarget = pattern.Length;
while ((ch = reader.Read()) != -1)
{
if (ch == pattern[pIndex])
{
pIndex++;
if (pIndex == pTarget)
return true;
}
else
{
pIndex = 0;
}
}
return false;
}
}
}

13
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoRunner.cs → src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/IdGenerator.cs

@ -9,12 +9,19 @@ using System; @@ -9,12 +9,19 @@ using System;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Runs neato.exe, passes string to standart input and reads from standart output.
/// Generates sequential ids, usefull for node and edge ids.
/// </summary>
public class NeatoRunner
public class IdGenerator
{
public NeatoRunner()
int currentId = 0;
public IdGenerator()
{
}
public int GetNextId()
{
return currentId++;
}
}
}

60
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoEdgeRouter.cs

@ -5,7 +5,12 @@ @@ -5,7 +5,12 @@
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Linq;
using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
@ -18,54 +23,23 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -18,54 +23,23 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{
}
/// <summary>
/// Given <see cref="PositionedGraph" /> with node positions, calculates edge positions.
/// </summary>
/// <param name="graphWithNodesPositioned"><see cref="PositionedGraph" />, the nodes must have positions filled.</param>
/// <returns><see cref="PositionedGraph" /> with preserved node positions and calculated edge positions.</returns>
public PositionedGraph CalculateEdges(PositionedGraph graphWithNodesPositioned)
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = @"D:\__prog__\Graphviz\Graphviz2.22\bin\neato.exe";
//p.StartInfo.Arguments = arguments.ToString();
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
//p.EnableRaisingEvents = true;
p.Exited += delegate {
p.Dispose();
};
/*p.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e) {
SvnClient.Instance.SvnCategory.AppendText(e.Data);
};
p.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e) {
SvnClient.Instance.SvnCategory.AppendText(e.Data);
};*/
// convert PosGraph to .dot string
// assign unique ids to edges, build map id->edge
DotGraph dotGraph = new DotGraph(graphWithNodesPositioned);
p.Start();
p.StandardInput.Write("digraph G {}");
p.StandardInput.Flush();
p.StandardInput.Close();
NeatoProcess neatoProcess = NeatoProcess.Start();
// convert PosGraph to .dot string, pass to neato.exe
string dotGraphStringWithPositions = neatoProcess.CalculatePositions(dotGraph.DotGraphString);
StringBuilder output = new StringBuilder();
while(true)
{
string line = p.StandardOutput.ReadLine();
output.Append(line);
if (line == "}")
{
break;
}
}
// parse edge positions from neato's plain output format
PositionedGraph result = dotGraph.ParseEdgePositions(dotGraphStringWithPositions);
string layoutedGraph = output.ToString();
// parse edge positions
// lookup edge in id->edge map, set position
PositionedGraph posGraph = graphWithNodesPositioned;
//posGraph.Edges
return posGraph;
return result;
}
}
}

71
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoPositionTransform.cs

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
// <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.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Transforms positions to and from Neato.
/// </summary>
public class NeatoPositionTransform
{
private Rect graphBoundingRect;
// So that Neato works with smaller numbers. Always > 1
double ourInputScale = 40;
// Neato itself scales input by this constant. Always > 1, 1 for plain output, 72 for .dot output
double neatoOutputScale = 1;
public NeatoPositionTransform(Rect graphBoundingRectangle)
{
graphBoundingRect = graphBoundingRectangle;
}
/// <summary>
/// X shift, in Neato coords.
/// </summary>
public double NeatoShiftX { get; set; }
/// <summary>
/// Y shift, in Neato coords.
/// </summary>
public double NeatoShiftY { get; set; }
public Point ToNeatoInput(Point ourPoint)
{
// invert Y axis, as Neato expects this
return new Point(ourPoint.X / ourInputScale, (graphBoundingRect.Bottom - ourPoint.Y) / ourInputScale);
}
public System.Windows.Point FromNeatoOutput(Point neatoPoint)
{
// Neato multiplies coords by 72 and adds arbitrary shift (which has to be parsed)
double ourX = (neatoPoint.X - NeatoShiftX) / neatoOutputScale * ourInputScale;
double ourYInverted = (neatoPoint.Y - NeatoShiftY) / neatoOutputScale * ourInputScale;
// invert back - our Y axis grows down
return new Point(ourX, graphBoundingRect.Bottom - ourYInverted);
}
/// <summary>
/// Transform points as Neato would transform it if Neato used no shift
/// </summary>
/// <param name="ourPoint"></param>
/// <returns></returns>
public System.Windows.Point AsNeato(Point ourPoint)
{
return new Point(ourPoint.X * neatoOutputScale / ourInputScale, (graphBoundingRect.Bottom - ourPoint.Y) * neatoOutputScale / ourInputScale);
}
public Rect NodeToNeatoInput(PositionedNode node)
{
// don't transform size
return new Rect(ToNeatoInput(node.Center),
new Size(node.Width / ourInputScale, node.Height / ourInputScale));
}
}
}

96
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/NeatoProcess.cs

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
// <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.Text;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Encapsulates Neato.exe.
/// </summary>
public class NeatoProcess
{
private System.Diagnostics.Process neatoProcess;
/// <summary>
/// Creates new NeatoProcess.
/// </summary>
/// <param name="neatoProcess">Underlying neato.exe process</param>
private NeatoProcess(System.Diagnostics.Process neatoProcess)
{
this.neatoProcess = neatoProcess;
}
/// <summary>
/// Starts neato.exe
/// </summary>
/// <returns></returns>
public static NeatoProcess Start()
{
System.Diagnostics.Process neatoProcess = new System.Diagnostics.Process();
string neatoPath = getNeatoExePath();
neatoProcess.StartInfo.FileName = neatoPath;
neatoProcess.StartInfo.RedirectStandardInput = true;
neatoProcess.StartInfo.RedirectStandardError = true;
neatoProcess.StartInfo.RedirectStandardOutput = true;
neatoProcess.StartInfo.UseShellExecute = false;
neatoProcess.StartInfo.Arguments = " -Gsplines=true -Tplain";
//p.EnableRaisingEvents = true;
neatoProcess.Exited += delegate {
neatoProcess.Dispose();
};
/*p.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e) {
};
p.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e) {
};*/
neatoProcess.Start();
return new NeatoProcess(neatoProcess);
}
private static string getCurrentAssemblyPath()
{
return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
}
private static string getNeatoExePath()
{
return System.IO.Path.Combine(getCurrentAssemblyPath(), "neato.exe");
}
/// <summary>
/// Passes given graph to neato and reads output.
/// </summary>
/// <param name="dotGraph">Graph in Graphviz dot format.</param>
/// <returns>Same graph in Graphviz plain with position information added.</returns>
public string CalculatePositions(string dotGraph)
{
neatoProcess.StandardInput.Write(dotGraph);
neatoProcess.StandardInput.Flush();
neatoProcess.StandardInput.Close();
StringBuilder output = new StringBuilder();
while(true)
{
string line = neatoProcess.StandardOutput.ReadLine();
if (line == null)
{
// happens if neato.exe is killed
throw new DebuggerVisualizerException("Problem getting layout information from neato.exe");
}
if (line == "stop")
{
break;
}
output.AppendLine(line);
}
return output.ToString();
}
}
}

4
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs

@ -17,8 +17,8 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -17,8 +17,8 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary>
public class TreeLayouter
{
private static readonly double horizNodeMargin = 20;
private static readonly double vertNodeMargin = 20;
private static readonly double horizNodeMargin = 30;
private static readonly double vertNodeMargin = 30;
private LayoutDirection layoutDirection = LayoutDirection.TopBottom;

Loading…
Cancel
Save