Browse Source

Object graph visualizer - animations.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4155 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts^2
Martin Koníček 17 years ago
parent
commit
85e500e2c0
  1. 5
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Debugger.AddIn.csproj
  2. 35
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/CanvasLocationAdapter.cs
  3. 260
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/GraphDrawer.cs
  4. 82
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphDiff.cs
  5. 100
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs
  6. 11
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/PositionedNode.cs
  7. 9
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeLayouter.cs
  8. 8
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs
  9. 4
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeLR.cs
  10. 4
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNodeTB.cs
  11. 18
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs
  12. 23
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DebuggerHelpers.cs
  13. 30
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DictionaryExtensions.cs
  14. 32
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs

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

@ -168,8 +168,11 @@ @@ -168,8 +168,11 @@
<Compile Include="Src\TreeModel\StackFrameNode.cs" />
<Compile Include="Src\TreeModel\Utils.cs" />
<Compile Include="Src\Visualizers\Graph\DebuggerVisualizerException.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\CanvasLocationAdapter.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\GraphDrawer.cs" />
<Compile Include="Src\Visualizers\Graph\Drawing\NodeControl.xaml.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\GraphDiff.cs" />
<Compile Include="Src\Visualizers\Graph\Layout\GraphMatcher.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" />
@ -192,6 +195,8 @@ @@ -192,6 +195,8 @@
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectNode.cs" />
<Compile Include="Src\Visualizers\Graph\ObjectGraph\ObjectProperty.cs" />
<Compile Include="Src\Visualizers\Graph\ShowObjectGraphVisualizerCommand.cs" />
<Compile Include="Src\Visualizers\Graph\Utils\DebuggerHelpers.cs" />
<Compile Include="Src\Visualizers\Graph\Utils\DictionaryExtensions.cs" />
<Compile Include="Src\Visualizers\Graph\Utils\Lookup.cs" />
<Compile Include="Src\Visualizers\Graph\Utils\LookupValueCollection.cs" />
<Compile Include="Src\Visualizers\Graph\VisualizerWinFormsControl.cs" />

35
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Drawing/CanvasLocationAdapter.cs

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
// <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;
using System.Windows.Controls;
namespace Debugger.AddIn.Visualizers.Graph.Drawing
{
/// <summary>
/// Provides LocationProperty, which enables animating Canvas.LeftProperty and Canvas.TopProperty
/// at the same time using PointAnimation.
/// </summary>
public class CanvasLocationAdapter : UIElement
{
/// <summary>
/// Add this dependency property to UIElement object and animate it using PointAnimation.
/// </summary>
public static DependencyProperty LocationProperty = DependencyProperty.RegisterAttached("Location", typeof(Point), typeof(Canvas), new FrameworkPropertyMetadata(new Point(0, 0), new PropertyChangedCallback(locationChanged)), new ValidateValueCallback(IsPointValid));
private static void locationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Canvas.SetLeft((UIElement)d, ((Point)e.NewValue).X);
Canvas.SetTop((UIElement)d, ((Point)e.NewValue).Y);
}
private static bool IsPointValid(object o)
{
return true;
}
}
}

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

@ -12,6 +12,7 @@ using System.Windows.Controls; @@ -12,6 +12,7 @@ using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
using Debugger.AddIn.Visualizers.Graph.Drawing;
using Debugger.AddIn.Visualizers.Graph.Layout;
@ -20,98 +21,179 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -20,98 +21,179 @@ namespace Debugger.AddIn.Visualizers.Graph
/// <summary>
/// Draws <see cref="PositionedGraph"></see> on Canvas.
/// </summary>
public class GraphDrawer
{
public GraphDrawer()
{
}
/// <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;
public class GraphDrawer
{
Canvas canvas;
public GraphDrawer(Canvas canvas)
{
this.canvas = canvas;
}
/// <summary>
/// Starts animation from oldGraph to newGraph.
/// </summary>
/// <param name="oldGraph"></param>
/// <param name="newGraph"></param>
/// <param name="diff"></param>
public void StartAnimation(PositionedGraph oldGraph, PositionedGraph newGraph, GraphDiff diff)
{
if (oldGraph == null)
{
Draw(newGraph);
return;
}
double seconds = 1;
var durationMove = new Duration(TimeSpan.FromSeconds(seconds));
var durationFade = durationMove;
DoubleAnimation fadeOutAnim = new DoubleAnimation(1.0, 0.0, durationFade);
DoubleAnimation fadeInAnim = new DoubleAnimation(0.0, 1.0, durationFade);
foreach (UIElement drawing in canvas.Children)
{
var arrow = drawing as Path;
if (arrow != null)
{
arrow.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
}
}
foreach (PositionedEdge edge in newGraph.Edges)
{
addEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
}
foreach (PositionedNode removedNode in diff.RemovedNodes)
{
removedNode.NodeVisualControl.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
}
foreach (PositionedNode addedNode in diff.AddedNodes)
{
addNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
}
bool first = true;
foreach (PositionedNode node in diff.ChangedNodes)
{
var newNode = diff.GetMatchingNewNode(node);
PointAnimation anim = new PointAnimation();
if (first)
{
anim.Completed += new EventHandler((o, e) => { Draw(newGraph); });
first = false;
}
//anim.From = new Point(Canvas.GetLeft(node.NodeVisualControl), Canvas.GetTop(node.NodeVisualControl));
anim.From = node.LeftTop;
anim.To = newNode.LeftTop;
anim.DecelerationRatio = 0.3;
anim.AccelerationRatio = 0.3;
anim.Duration = durationMove;
node.NodeVisualControl.BeginAnimation(CanvasLocationAdapter.LocationProperty, anim);
}
}
/// <summary>
/// Draws <see cref="PositionedGraph"></see> on Canvas.
/// </summary>
/// <param name="posGraph">Graph to draw.</param>
/// <param name="canvas">Destination Canvas.</param>
public void Draw(PositionedGraph posGraph)
{
canvas.Children.Clear();
// draw nodes
foreach (PositionedNode node in posGraph.Nodes)
{
addNodeToCanvas(node);
}
// draw edges
foreach (PositionedEdge edge in posGraph.Edges)
{
addEdgeToCanvas(edge);
}
}
private NodeControl addNodeToCanvas(PositionedNode node)
{
canvas.Children.Add(node.NodeVisualControl);
Canvas.SetLeft(node.NodeVisualControl, node.Left);
Canvas.SetTop(node.NodeVisualControl, node.Top);
return node.NodeVisualControl;
}
private Path addEdgeToCanvas(PositionedEdge edge)
{
Path edgePath = createEdgeWithArrow(edge);
canvas.Children.Add(edgePath);
return edgePath;
}
private Path createEdgeWithArrow(PositionedEdge edge)
{
Path path = new Path();
path.Stroke = Brushes.Black;
path.Fill = Brushes.Black;
path.StrokeThickness = 1;
PathGeometry geometry = new PathGeometry();
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)
{
geometry.Figures.Add(createEdgeSpline(edge));
geometry.Figures.Add(createEdgeArrow(edge));
path.Data = geometry;
return path;
}
private 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 PathFigure createEdgeArrow(PositionedEdge edge)
{
Point splineEndPoint = edge.SplinePoints[edge.SplinePoints.Count - 1];
Point splineEndHandlePoint = edge.SplinePoints[edge.SplinePoints.Count - 2];
Vector tangent = splineEndPoint - splineEndHandlePoint;
tangent.Normalize();
tangent = tangent * 20;
Point basePoint = splineEndPoint - 0.2 * tangent;
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.IsClosed = true;
arrowFigure.IsFilled = true;
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));
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;
}
}
}
private static Vector rotate90(Vector v)
{
// (x, y) -> (y, -x)
double t = v.X;
v.X = v.Y;
v.Y = -t;
return v;
}
}
}

82
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphDiff.cs

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
// <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 Debugger.AddIn.Visualizers.Graph.Utils;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Describes changes between 2 <see cref="PositionedGraph"/>s.
/// </summary>
public class GraphDiff
{
private List<PositionedNode> addedNodes = new List<PositionedNode>();
private List<PositionedNode> deletedNodes = new List<PositionedNode>();
private List<PositionedNode> changedNodes = new List<PositionedNode>();
private Dictionary<PositionedNode, PositionedNode> matching = new Dictionary<PositionedNode, PositionedNode>();
/// <summary>
/// Nodes in the new graph that were added.
/// </summary>
public IList<PositionedNode> AddedNodes
{
get { return addedNodes.AsReadOnly(); }
}
/// <summary>
/// Nodes in the old graph that were removed.
/// </summary>
public IList<PositionedNode> RemovedNodes
{
get { return deletedNodes.AsReadOnly(); }
}
/// <summary>
/// Nodes in the old graph that were chaged.
/// These have matching new nodes, which can be obtained by <see cref="GetMatchingNewNode"/>.
/// </summary>
public IList<PositionedNode> ChangedNodes
{
get { return changedNodes.AsReadOnly(); }
}
public PositionedNode GetMatchingNewNode(PositionedNode oldNode)
{
return matching.GetValue(oldNode);
}
internal void SetAdded(PositionedNode addedNode)
{
addedNodes.Add(addedNode);
}
internal void SetRemoved(PositionedNode removeddNode)
{
deletedNodes.Add(removeddNode);
}
internal void SetMatching(PositionedNode matchFrom, PositionedNode matchTo)
{
matching[matchFrom] = matchTo;
changedNodes.Add(matchFrom);
}
public GraphDiff()
{
}
/*public void MakeReadOnly()
{
addedNodes = ((List<PositionedNode>)addedNodes).AsReadOnly();
deletedNodes = ((List<PositionedNode>)deletedNodes).AsReadOnly();
changedNodes = ((List<PositionedNode>)changedNodes).AsReadOnly();
}*/
}
}

100
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/GraphMatcher.cs

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
// <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 Debugger.AddIn.Visualizers.Graph.Utils;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Calculates diff of 2 <see cref="PositionedGraph"/>s.
/// </summary>
public class GraphMatcher
{
public GraphMatcher()
{
}
public GraphDiff MatchGraphs(PositionedGraph oldGraph, PositionedGraph newGraph)
{
// handle any of the graphs null
if (oldGraph == null)
{
if (newGraph == null)
{
return new GraphDiff();
}
else
{
GraphDiff addAllDiff = new GraphDiff();
foreach (PositionedNode newNode in newGraph.Nodes)
{
addAllDiff.SetAdded(newNode);
}
return addAllDiff;
}
}
// both graph are not null
GraphDiff diff = new GraphDiff();
Dictionary<int, PositionedNode> newNodeForHashCode = buildHashToNodeMap(newGraph);
Dictionary<PositionedNode, bool> newNodeMatched = new Dictionary<PositionedNode, bool>();
foreach (PositionedNode oldNode in oldGraph.Nodes)
{
PositionedNode matchingNode = matchNode(oldNode, newNodeForHashCode);
if (matchingNode != null)
{
diff.SetMatching(oldNode, matchingNode);
newNodeMatched[matchingNode] = true;
}
else
{
diff.SetRemoved(oldNode);
}
}
foreach (PositionedNode newNode in newGraph.Nodes)
{
if (!newNodeMatched.ContainsKey(newNode))
{
diff.SetAdded(newNode);
}
}
return diff;
}
private Dictionary<int, PositionedNode> buildHashToNodeMap(PositionedGraph graph)
{
var hashToNodeMap = new Dictionary<int, PositionedNode>();
foreach (PositionedNode node in graph.Nodes)
{
hashToNodeMap[node.ObjectNode.HashCode] = node;
}
return hashToNodeMap;
}
private PositionedNode matchNode(PositionedNode oldNode, Dictionary<int, PositionedNode> newNodeMap)
{
PositionedNode newNodeFound = newNodeMap.GetValue(oldNode.ObjectNode.HashCode);
if ((newNodeFound != null) && isSameAddress(oldNode, newNodeFound))
{
return newNodeFound;
}
else
{
return null;
}
}
private bool isSameAddress(PositionedNode node1, PositionedNode node2)
{
return node1.ObjectNode.PermanentReference.GetObjectAddress() == node2.ObjectNode.PermanentReference.GetObjectAddress();
}
}
}

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

@ -12,13 +12,20 @@ using System.Windows; @@ -12,13 +12,20 @@ using System.Windows;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Node with position information.
/// Node with added position information.
/// </summary>
public class PositionedNode
{
public PositionedNode(NodeControl nodeVisualControl)
private ObjectNode objectNode;
public ObjectNode ObjectNode
{
get { return objectNode; }
}
public PositionedNode(NodeControl nodeVisualControl, ObjectNode objectNode)
{
this.nodeVisualControl = nodeVisualControl;
this.objectNode = objectNode;
}
public double Left { get; set; }

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

@ -31,11 +31,6 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -31,11 +31,6 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{
}
private TreeNode createTreeNode(NodeControl nodeVisualControl)
{
return TreeNode.Create(this.layoutDirection, nodeVisualControl);
}
/// <summary>
/// Calculates layout for given <see cref="ObjectGraph" />.
/// </summary>
@ -66,7 +61,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -66,7 +61,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
nodeVisualControl.GraphNode = objectGraphNode;
nodeVisualControl.Measure(new Size(500, 500));
TreeNode newTreeNode = createTreeNode(nodeVisualControl);
TreeNode newTreeNode = TreeNode.Create(this.layoutDirection, nodeVisualControl, objectGraphNode);
newTreeNode.HorizontalMargin = horizNodeMargin;
newTreeNode.VerticalMargin = vertNodeMargin;
resultGraph.nodes.Add(newTreeNode);
@ -104,7 +99,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -104,7 +99,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{
// center this node
double subtreeSize = node.Childs.Sum(child => child.SubtreeSize);
double center = node.Childs.Count() == 0 ? 0 : 0.5 * (subtreeSize - (node.LateralSize + node.LateralMargin));
double center = node.ChildEdges.Count() == 0 ? 0 : 0.5 * (subtreeSize - (node.LateralSize + node.LateralMargin));
// design alternatives
// node.MainPos += center; // used this

8
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Layout/Tree/TreeNode.cs

@ -18,11 +18,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -18,11 +18,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary>
public abstract class TreeNode : PositionedNode
{
public static TreeNode Create(LayoutDirection direction, NodeControl nodeVisualControl)
public static TreeNode Create(LayoutDirection direction, NodeControl nodeVisualControl, ObjectNode objectNode)
{
switch (direction) {
case LayoutDirection.TopBottom: return new TreeNodeTB(nodeVisualControl);
case LayoutDirection.LeftRight: return new TreeNodeLR(nodeVisualControl);
case LayoutDirection.TopBottom: return new TreeNodeTB(nodeVisualControl, objectNode);
case LayoutDirection.LeftRight: return new TreeNodeLR(nodeVisualControl, objectNode);
default: throw new DebuggerVisualizerException("Unsupported layout direction: " + direction.ToString());
}
}
@ -30,7 +30,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout @@ -30,7 +30,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
public double HorizontalMargin { get; set; }
public double VerticalMargin { get; set; }
protected TreeNode(NodeControl nodeVisualControl) : base(nodeVisualControl)
protected TreeNode(NodeControl nodeVisualControl, ObjectNode objectNode) : base(nodeVisualControl, objectNode)
{
}

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

@ -10,11 +10,11 @@ using Debugger.AddIn.Visualizers.Graph.Drawing; @@ -10,11 +10,11 @@ using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Description of TreeNodeLR.
/// TreeNode used in LR layout mode.
/// </summary>
public class TreeNodeLR: TreeNode
{
public TreeNodeLR(NodeControl nodeControl) : base(nodeControl)
public TreeNodeLR(NodeControl nodeControl, ObjectNode objectNode) : base(nodeControl, objectNode)
{
}

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

@ -10,11 +10,11 @@ using Debugger.AddIn.Visualizers.Graph.Drawing; @@ -10,11 +10,11 @@ using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Description of TreeNodeTB.
/// TreeNode used in TB layout mode.
/// </summary>
public class TreeNodeTB : TreeNode
{
public TreeNodeTB(NodeControl nodeControl) : base(nodeControl)
public TreeNodeTB(NodeControl nodeControl, ObjectNode objectNode) : base(nodeControl, objectNode)
{
}

18
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/ObjectGraph/ObjectGraphBuilder.cs

@ -9,7 +9,6 @@ using System.Collections.Generic; @@ -9,7 +9,6 @@ using System.Collections.Generic;
using ICSharpCode.SharpDevelop.Services;
using Debugger;
using Debugger.MetaData;
using Debugger.Wrappers.CorDebug;
using Debugger.Expressions;
using Debugger.AddIn.Visualizers.Graph.Utils;
@ -159,10 +158,11 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -159,10 +158,11 @@ namespace Debugger.AddIn.Visualizers.Graph
private ObjectNode createNewNode(Value permanentReference)
{
ObjectNode newNode = new ObjectNode();
newNode.HashCode = invokeGetHashCode(permanentReference);
resultGraph.AddNode(newNode);
// remember this node's hashcode for quick lookup
objectNodesForHashCode.Add(invokeGetHashCode(permanentReference), newNode);
objectNodesForHashCode.Add(newNode.HashCode, newNode);
// permanent reference to the object this node represents is useful for graph building,
// and matching nodes in animations
@ -189,9 +189,9 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -189,9 +189,9 @@ namespace Debugger.AddIn.Visualizers.Graph
{
// if there is a node with same hash code, check if it has also the same address
// (hash codes are not uniqe - http://stackoverflow.com/questions/750947/-net-unique-object-identifier)
ulong objectAddress = getObjectValue(value);
ulong objectAddress = value.GetObjectAddress();
ObjectNode nodeWithSameAddress = nodesWithSameHashCode.Find(
node => { return objectAddress == getObjectValue(node.PermanentReference); } );
node => { return objectAddress == node.PermanentReference.GetObjectAddress(); } );
return nodeWithSameAddress;
}
}
@ -239,7 +239,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -239,7 +239,7 @@ namespace Debugger.AddIn.Visualizers.Graph
return (!typeOfValue.IsClass) || typeOfValue.IsString;
}
#region helpers
#region Expression helpers
private Value getPermanentReference(Expression expr)
{
@ -258,13 +258,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -258,13 +258,7 @@ namespace Debugger.AddIn.Visualizers.Graph
private ulong getObjectAddress(Expression expr)
{
return getObjectValue(expr.Evaluate(debuggerService.DebuggedProcess));
}
private ulong getObjectValue(Value val)
{
ICorDebugReferenceValue refVal = val.CorValue.CastTo<ICorDebugReferenceValue>();
return refVal.Value;
return expr.Evaluate(debuggerService.DebuggedProcess).GetObjectAddress();
}
#endregion

23
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DebuggerHelpers.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;
using Debugger.Wrappers.CorDebug;
namespace Debugger.AddIn.Visualizers.Graph.Utils
{
public static class DebuggerHelpers
{
/// <summary>
/// Gets underlying address of object in the debuggee.
/// </summary>
public static ulong GetObjectAddress(this Value val)
{
ICorDebugReferenceValue refVal = val.CorValue.CastTo<ICorDebugReferenceValue>();
return refVal.Value;
}
}
}

30
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/Utils/DictionaryExtensions.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;
using System.Collections.Generic;
namespace Debugger.AddIn.Visualizers.Graph.Utils
{
public static class DictionaryExtensions
{
/// <summary>
/// Gets value from Dictionary, returns null if not found.
/// </summary>
public static TValue GetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key) where TValue : class
{
TValue outValue;
if (dictionary.TryGetValue(key, out outValue))
{
return outValue;
}
else
{
return null;
}
}
}
}

32
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Visualizers/Graph/VisualizerWPFWindow.xaml.cs

@ -28,30 +28,36 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -28,30 +28,36 @@ namespace Debugger.AddIn.Visualizers.Graph
/// </summary>
public partial class VisualizerWPFWindow : Window
{
private WindowsDebugger _debuggerService;
private WindowsDebugger debuggerService;
private EnumViewModel<LayoutDirection> layoutViewModel;
private ObjectGraph objectGraph;
private PositionedGraph oldGraph;
private PositionedGraph currentGraph;
private GraphDrawer graphDrawer;
public VisualizerWPFWindow()
{
InitializeComponent();
_debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger;
if (_debuggerService == null)
debuggerService = DebuggerService.CurrentDebugger as WindowsDebugger;
if (debuggerService == null)
throw new ApplicationException("Only windows debugger is currently supported");
_debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged);
_debuggerService.DebugStopped += new EventHandler(_debuggerService_DebugStopped);
debuggerService.IsProcessRunningChanged += new EventHandler(debuggerService_IsProcessRunningChanged);
debuggerService.DebugStopped += new EventHandler(_debuggerService_DebugStopped);
this.layoutViewModel = new EnumViewModel<LayoutDirection>();
this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged);
this.DataContext = this.layoutViewModel;
this.graphDrawer = new GraphDrawer(this.canvas);
}
public void debuggerService_IsProcessRunningChanged(object sender, EventArgs e)
{
// on step or breakpoint hit
if (!_debuggerService.IsProcessRunning)
if (!debuggerService.IsProcessRunning)
{
refreshGraph();
}
@ -59,7 +65,7 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -59,7 +65,7 @@ namespace Debugger.AddIn.Visualizers.Graph
public void _debuggerService_DebugStopped(object sender, EventArgs e)
{
_debuggerService.IsProcessRunningChanged -= new EventHandler(debuggerService_IsProcessRunningChanged);
debuggerService.IsProcessRunningChanged -= new EventHandler(debuggerService_IsProcessRunningChanged);
this.Close();
}
@ -78,11 +84,12 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -78,11 +84,12 @@ namespace Debugger.AddIn.Visualizers.Graph
void refreshGraph()
{
ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(_debuggerService);
ObjectGraphBuilder graphBuilder = new ObjectGraphBuilder(debuggerService);
this.objectGraph = null;
try
{
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text);
this.objectGraph = graphBuilder.BuildGraphForExpression(txtExpression.Text);
}
catch(DebuggerVisualizerException ex)
@ -104,10 +111,15 @@ namespace Debugger.AddIn.Visualizers.Graph @@ -104,10 +111,15 @@ namespace Debugger.AddIn.Visualizers.Graph
void layoutGraph(ObjectGraph graph)
{
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout");
Layout.TreeLayouter layouter = new Layout.TreeLayouter();
Layout.PositionedGraph posGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue);
this.oldGraph = this.currentGraph;
this.currentGraph = layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue);
GraphDrawer.Draw(posGraph, canvas);
var graphDiff = new GraphMatcher().MatchGraphs(oldGraph, currentGraph);
this.graphDrawer.StartAnimation(oldGraph, currentGraph, graphDiff);
//this.graphDrawer.Draw(currentGraph);
}
void guiHandleException(System.Exception ex)

Loading…
Cancel
Save