Browse Source

Object graph - simplified layout code.

pull/15/head
mkonicek 15 years ago
parent
commit
550e862c4d
  1. 8
      src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
  2. 74
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/GraphDrawer.cs
  3. 6
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/ContentNode.cs
  4. 2
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/ContentPropertyNode.cs
  5. 22
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphDiff.cs
  6. 26
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphMatcher.cs
  7. 2
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedEdge.cs
  8. 10
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedGraph.cs
  9. 53
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedNode.cs
  10. 6
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedNodeProperty.cs
  11. 21
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeEdge.cs
  12. 75
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNode.cs
  13. 27
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNodeLR.cs
  14. 27
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNodeTB.cs
  15. 187
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayout.cs
  16. 137
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayouter.cs
  17. 27
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/ObjectGraph/ObjectGraph.cs
  18. 126
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/ObjectGraphControl.xaml.cs

8
src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj

@ -315,15 +315,11 @@
<Compile Include="Visualizers\Graph\Layout\GraphMatcher.cs" /> <Compile Include="Visualizers\Graph\Layout\GraphMatcher.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedEdge.cs" /> <Compile Include="Visualizers\Graph\Layout\PositionedEdge.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedGraph.cs" /> <Compile Include="Visualizers\Graph\Layout\PositionedGraph.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedGraphNode.cs" /> <Compile Include="Visualizers\Graph\Layout\PositionedNode.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedNodeProperty.cs" /> <Compile Include="Visualizers\Graph\Layout\PositionedNodeProperty.cs" />
<Compile Include="Visualizers\Graph\Layout\PositionedPropertyEventArgs.cs" /> <Compile Include="Visualizers\Graph\Layout\PositionedPropertyEventArgs.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\LayoutDirection.cs" /> <Compile Include="Visualizers\Graph\Layout\Tree\LayoutDirection.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeEdge.cs" /> <Compile Include="Visualizers\Graph\Layout\Tree\TreeLayout.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeLayouter.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeGraphNode.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeGraphNodeLR.cs" />
<Compile Include="Visualizers\Graph\Layout\Tree\TreeGraphNodeTB.cs" />
<Compile Include="Visualizers\Graph\ObjectGraph\ObjectGraph.cs" /> <Compile Include="Visualizers\Graph\ObjectGraph\ObjectGraph.cs" />
<Compile Include="Visualizers\Graph\ObjectGraph\ObjectGraphBuilder.cs" /> <Compile Include="Visualizers\Graph\ObjectGraph\ObjectGraphBuilder.cs" />
<Compile Include="Visualizers\Graph\ObjectGraph\ObjectGraphNode.cs" /> <Compile Include="Visualizers\Graph\ObjectGraph\ObjectGraphNode.cs" />

74
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/GraphDrawer.cs

@ -19,7 +19,8 @@ using Debugger.AddIn.Visualizers.Graph.Layout;
namespace Debugger.AddIn.Visualizers.Graph namespace Debugger.AddIn.Visualizers.Graph
{ {
/// <summary> /// <summary>
/// Draws <see cref="PositionedGraph"></see> on Canvas. /// Draws <see cref="PositionedGraph" /> on Canvas.
/// Keeps the last displayed graph and does a smooth transition into the new graph.
/// </summary> /// </summary>
public class GraphDrawer public class GraphDrawer
{ {
@ -41,11 +42,10 @@ namespace Debugger.AddIn.Visualizers.Graph
{ {
// account for that the visual controls could have been reused (we are not reusing controls now - NodeControlCache does nothing) // account for that the visual controls could have been reused (we are not reusing controls now - NodeControlCache does nothing)
// this.canvas.Width = newGraph.BoundingRect.Width; this.canvas.Width = newGraph.BoundingRect.Width;
// this.canvas.Height = newGraph.BoundingRect.Height; this.canvas.Height = newGraph.BoundingRect.Height;
if (oldGraph == null) if (oldGraph == null) {
{
Draw(newGraph); Draw(newGraph);
return; return;
} }
@ -57,38 +57,31 @@ namespace Debugger.AddIn.Visualizers.Graph
DoubleAnimation fadeOutAnim = new DoubleAnimation(1.0, 0.0, durationFade); DoubleAnimation fadeOutAnim = new DoubleAnimation(1.0, 0.0, durationFade);
DoubleAnimation fadeInAnim = new DoubleAnimation(0.0, 1.0, durationFade); DoubleAnimation fadeInAnim = new DoubleAnimation(0.0, 1.0, durationFade);
foreach (UIElement drawing in canvas.Children) foreach (UIElement drawing in canvas.Children) {
{
var arrow = drawing as Path; var arrow = drawing as Path;
if (arrow != null) if (arrow != null) {
{
arrow.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim); arrow.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
} }
} }
foreach (PositionedEdge edge in newGraph.Edges) foreach (PositionedEdge edge in newGraph.Edges) {
{ AddEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
addEdgeToCanvas(edge).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
} }
foreach (PositionedGraphNode removedNode in diff.RemovedNodes) foreach (PositionedNode removedNode in diff.RemovedNodes) {
{
removedNode.NodeVisualControl.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim); removedNode.NodeVisualControl.BeginAnimation(UIElement.OpacityProperty, fadeOutAnim);
} }
foreach (PositionedGraphNode addedNode in diff.AddedNodes) foreach (PositionedNode addedNode in diff.AddedNodes) {
{ AddNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
addNodeToCanvas(addedNode).BeginAnimation(UIElement.OpacityProperty, fadeInAnim);
} }
bool first = true; bool first = true;
foreach (PositionedGraphNode node in diff.ChangedNodes) foreach (PositionedNode node in diff.ChangedNodes) {
{
var newNode = diff.GetMatchingNewNode(node); var newNode = diff.GetMatchingNewNode(node);
PointAnimation anim = new PointAnimation(); PointAnimation anim = new PointAnimation();
if (first) if (first) {
{
anim.Completed += new EventHandler((o, e) => { Draw(newGraph); }); anim.Completed += new EventHandler((o, e) => { Draw(newGraph); });
first = false; first = false;
} }
@ -111,29 +104,14 @@ namespace Debugger.AddIn.Visualizers.Graph
{ {
canvas.Children.Clear(); canvas.Children.Clear();
/*try
{
// why do the controls disappear?
var n1 = posGraph.Nodes.First().NodeVisualControl;
var n2 = posGraph.Nodes.Skip(1).First().NodeVisualControl;
var n3 = posGraph.Nodes.Skip(2).First().NodeVisualControl;
if (n1 == n2 || n1 == n3 || n2 == n3)
{
ClearCanvas();
}
}
catch{}*/
// draw nodes // draw nodes
foreach (PositionedGraphNode node in posGraph.Nodes) foreach (PositionedNode node in posGraph.Nodes) {
{ AddNodeToCanvas(node);
addNodeToCanvas(node);
} }
// draw edges // draw edges
foreach (PositionedEdge edge in posGraph.Edges) foreach (PositionedEdge edge in posGraph.Edges) {
{ AddEdgeToCanvas(edge);
addEdgeToCanvas(edge);
} }
edgeTooltip.Visibility = Visibility.Hidden; edgeTooltip.Visibility = Visibility.Hidden;
@ -149,7 +127,7 @@ namespace Debugger.AddIn.Visualizers.Graph
canvas.Children.Clear(); canvas.Children.Clear();
} }
private PositionedGraphNodeControl addNodeToCanvas(PositionedGraphNode node) PositionedGraphNodeControl AddNodeToCanvas(PositionedNode node)
{ {
canvas.Children.Add(node.NodeVisualControl); canvas.Children.Add(node.NodeVisualControl);
Canvas.SetLeft(node.NodeVisualControl, node.Left); Canvas.SetLeft(node.NodeVisualControl, node.Left);
@ -157,13 +135,13 @@ namespace Debugger.AddIn.Visualizers.Graph
return node.NodeVisualControl; return node.NodeVisualControl;
} }
private Path addEdgeToCanvas(PositionedEdge edge) Path AddEdgeToCanvas(PositionedEdge edge)
{ {
PathFigure edgeSplineFigure = createEdgeSpline(edge); PathFigure edgeSplineFigure = CreateEdgeSpline(edge);
PathGeometry geometryVisible = new PathGeometry(); PathGeometry geometryVisible = new PathGeometry();
geometryVisible.Figures.Add(edgeSplineFigure); geometryVisible.Figures.Add(edgeSplineFigure);
geometryVisible.Figures.Add(createEdgeArrow(edge)); geometryVisible.Figures.Add(CreateEdgeArrow(edge));
Path pathVisible = new Path(); Path pathVisible = new Path();
pathVisible.Stroke = Brushes.Black; pathVisible.Stroke = Brushes.Black;
@ -210,7 +188,7 @@ namespace Debugger.AddIn.Visualizers.Graph
return pathVisible; return pathVisible;
} }
private PathFigure createEdgeSpline(PositionedEdge edge) PathFigure CreateEdgeSpline(PositionedEdge edge)
{ {
PathFigure figure = new PathFigure(); PathFigure figure = new PathFigure();
figure.IsClosed = false; figure.IsClosed = false;
@ -225,7 +203,7 @@ namespace Debugger.AddIn.Visualizers.Graph
return figure; return figure;
} }
private PathFigure createEdgeArrow(PositionedEdge edge) PathFigure CreateEdgeArrow(PositionedEdge edge)
{ {
Point splineEndPoint = edge.SplinePoints[edge.SplinePoints.Count - 1]; Point splineEndPoint = edge.SplinePoints[edge.SplinePoints.Count - 1];
Point splineEndHandlePoint = edge.SplinePoints[edge.SplinePoints.Count - 2]; Point splineEndHandlePoint = edge.SplinePoints[edge.SplinePoints.Count - 2];
@ -240,14 +218,14 @@ namespace Debugger.AddIn.Visualizers.Graph
arrowFigure.IsFilled = true; arrowFigure.IsFilled = true;
arrowFigure.StartPoint = splineEndPoint; // arrow tip arrowFigure.StartPoint = splineEndPoint; // arrow tip
Vector tangent2 = rotate90(tangent); 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.Segments.Add(new LineSegment(basePoint - tangent2 * 0.15, true)); arrowFigure.Segments.Add(new LineSegment(basePoint - tangent2 * 0.15, true));
return arrowFigure; return arrowFigure;
} }
private static Vector rotate90(Vector v) static Vector Rotate90(Vector v)
{ {
// (x, y) -> (y, -x) // (x, y) -> (y, -x)
double t = v.X; double t = v.X;

6
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/ContentNode.cs

@ -15,7 +15,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary> /// </summary>
public class ContentNode : Utils.ITreeNode<ContentNode> public class ContentNode : Utils.ITreeNode<ContentNode>
{ {
public ContentNode(PositionedGraphNode containingNode, ContentNode parent) public ContentNode(PositionedNode containingNode, ContentNode parent)
{ {
if (containingNode == null) if (containingNode == null)
throw new ArgumentNullException("containingNode"); throw new ArgumentNullException("containingNode");
@ -74,11 +74,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
private List<ContentNode> children = new List<ContentNode>(); private List<ContentNode> children = new List<ContentNode>();
public List<ContentNode> Children { get { return this.children; } } public List<ContentNode> Children { get { return this.children; } }
PositionedGraphNode containingNode; PositionedNode containingNode;
/// <summary> /// <summary>
/// PositionedGraphNode that contains this PropertyNodeViewModel. /// PositionedGraphNode that contains this PropertyNodeViewModel.
/// </summary> /// </summary>
public PositionedGraphNode ContainingNode public PositionedNode ContainingNode
{ {
get { return this.containingNode; } get { return this.containingNode; }
} }

2
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/ContentPropertyNode.cs

@ -13,7 +13,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{ {
PositionedNodeProperty positionedProperty; PositionedNodeProperty positionedProperty;
public ContentPropertyNode(PositionedGraphNode containingNode, ContentNode parent) public ContentPropertyNode(PositionedNode containingNode, ContentNode parent)
: base(containingNode, parent) : base(containingNode, parent)
{ {
} }

22
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphDiff.cs

@ -13,15 +13,15 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary> /// </summary>
public class GraphDiff public class GraphDiff
{ {
private List<PositionedGraphNode> addedNodes = new List<PositionedGraphNode>(); private List<PositionedNode> addedNodes = new List<PositionedNode>();
private List<PositionedGraphNode> deletedNodes = new List<PositionedGraphNode>(); private List<PositionedNode> deletedNodes = new List<PositionedNode>();
private List<PositionedGraphNode> changedNodes = new List<PositionedGraphNode>(); private List<PositionedNode> changedNodes = new List<PositionedNode>();
private Dictionary<PositionedGraphNode, PositionedGraphNode> matching = new Dictionary<PositionedGraphNode, PositionedGraphNode>(); private Dictionary<PositionedNode, PositionedNode> matching = new Dictionary<PositionedNode, PositionedNode>();
/// <summary> /// <summary>
/// Nodes in the new graph that were added. /// Nodes in the new graph that were added.
/// </summary> /// </summary>
public IList<PositionedGraphNode> AddedNodes public IList<PositionedNode> AddedNodes
{ {
get { return addedNodes.AsReadOnly(); } get { return addedNodes.AsReadOnly(); }
} }
@ -29,7 +29,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary> /// <summary>
/// Nodes in the old graph that were removed. /// Nodes in the old graph that were removed.
/// </summary> /// </summary>
public IList<PositionedGraphNode> RemovedNodes public IList<PositionedNode> RemovedNodes
{ {
get { return deletedNodes.AsReadOnly(); } get { return deletedNodes.AsReadOnly(); }
} }
@ -38,27 +38,27 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// Nodes in the old graph that were chaged. /// Nodes in the old graph that were chaged.
/// These have matching new nodes, which can be obtained by <see cref="GetMatchingNewNode"/>. /// These have matching new nodes, which can be obtained by <see cref="GetMatchingNewNode"/>.
/// </summary> /// </summary>
public IList<PositionedGraphNode> ChangedNodes public IList<PositionedNode> ChangedNodes
{ {
get { return changedNodes.AsReadOnly(); } get { return changedNodes.AsReadOnly(); }
} }
public PositionedGraphNode GetMatchingNewNode(PositionedGraphNode oldNode) public PositionedNode GetMatchingNewNode(PositionedNode oldNode)
{ {
return matching.GetValue(oldNode); return matching.GetValue(oldNode);
} }
internal void SetAdded(PositionedGraphNode addedNode) internal void SetAdded(PositionedNode addedNode)
{ {
addedNodes.Add(addedNode); addedNodes.Add(addedNode);
} }
internal void SetRemoved(PositionedGraphNode removeddNode) internal void SetRemoved(PositionedNode removeddNode)
{ {
deletedNodes.Add(removeddNode); deletedNodes.Add(removeddNode);
} }
internal void SetMatching(PositionedGraphNode matchFrom, PositionedGraphNode matchTo) internal void SetMatching(PositionedNode matchFrom, PositionedNode matchTo)
{ {
matching[matchFrom] = matchTo; matching[matchFrom] = matchTo;
changedNodes.Add(matchFrom); changedNodes.Add(matchFrom);

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

@ -27,7 +27,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
else else
{ {
GraphDiff addAllDiff = new GraphDiff(); GraphDiff addAllDiff = new GraphDiff();
foreach (PositionedGraphNode newNode in newGraph.Nodes) foreach (PositionedNode newNode in newGraph.Nodes)
addAllDiff.SetAdded(newNode); addAllDiff.SetAdded(newNode);
return addAllDiff; return addAllDiff;
} }
@ -35,7 +35,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
else if (newGraph == null) else if (newGraph == null)
{ {
GraphDiff removeAllDiff = new GraphDiff(); GraphDiff removeAllDiff = new GraphDiff();
foreach (PositionedGraphNode oldNode in oldGraph.Nodes) foreach (PositionedNode oldNode in oldGraph.Nodes)
removeAllDiff.SetRemoved(oldNode); removeAllDiff.SetRemoved(oldNode);
return removeAllDiff; return removeAllDiff;
} }
@ -43,12 +43,12 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
// none of the graphs is null // none of the graphs is null
GraphDiff diff = new GraphDiff(); GraphDiff diff = new GraphDiff();
Dictionary<int, PositionedGraphNode> newNodeForHashCode = buildHashToNodeMap(newGraph); Dictionary<int, PositionedNode> newNodeForHashCode = buildHashToNodeMap(newGraph);
Dictionary<PositionedGraphNode, bool> newNodeMatched = new Dictionary<PositionedGraphNode, bool>(); Dictionary<PositionedNode, bool> newNodeMatched = new Dictionary<PositionedNode, bool>();
foreach (PositionedGraphNode oldNode in oldGraph.Nodes) foreach (PositionedNode oldNode in oldGraph.Nodes)
{ {
PositionedGraphNode matchingNode = matchNode(oldNode, newNodeForHashCode); PositionedNode matchingNode = matchNode(oldNode, newNodeForHashCode);
if (matchingNode != null) if (matchingNode != null)
{ {
diff.SetMatching(oldNode, matchingNode); diff.SetMatching(oldNode, matchingNode);
@ -59,7 +59,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
diff.SetRemoved(oldNode); diff.SetRemoved(oldNode);
} }
} }
foreach (PositionedGraphNode newNode in newGraph.Nodes) foreach (PositionedNode newNode in newGraph.Nodes)
{ {
if (!newNodeMatched.ContainsKey(newNode)) if (!newNodeMatched.ContainsKey(newNode))
{ {
@ -70,19 +70,19 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
return diff; return diff;
} }
private Dictionary<int, PositionedGraphNode> buildHashToNodeMap(PositionedGraph graph) private Dictionary<int, PositionedNode> buildHashToNodeMap(PositionedGraph graph)
{ {
var hashToNodeMap = new Dictionary<int, PositionedGraphNode>(); var hashToNodeMap = new Dictionary<int, PositionedNode>();
foreach (PositionedGraphNode node in graph.Nodes) foreach (PositionedNode node in graph.Nodes)
{ {
hashToNodeMap[node.ObjectNode.HashCode] = node; hashToNodeMap[node.ObjectNode.HashCode] = node;
} }
return hashToNodeMap; return hashToNodeMap;
} }
private PositionedGraphNode matchNode(PositionedGraphNode oldNode, Dictionary<int, PositionedGraphNode> newNodeMap) private PositionedNode matchNode(PositionedNode oldNode, Dictionary<int, PositionedNode> newNodeMap)
{ {
PositionedGraphNode newNodeFound = newNodeMap.GetValue(oldNode.ObjectNode.HashCode); PositionedNode newNodeFound = newNodeMap.GetValue(oldNode.ObjectNode.HashCode);
if ((newNodeFound != null) && isSameAddress(oldNode, newNodeFound)) if ((newNodeFound != null) && isSameAddress(oldNode, newNodeFound))
{ {
return newNodeFound; return newNodeFound;
@ -93,7 +93,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
} }
} }
private bool isSameAddress(PositionedGraphNode node1, PositionedGraphNode node2) private bool isSameAddress(PositionedNode node1, PositionedNode node2)
{ {
return node1.ObjectNode.PermanentReference.GetObjectAddress() == node2.ObjectNode.PermanentReference.GetObjectAddress(); return node1.ObjectNode.PermanentReference.GetObjectAddress() == node2.ObjectNode.PermanentReference.GetObjectAddress();
} }

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

@ -11,7 +11,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary> /// <summary>
/// Edge with position information. /// Edge with position information.
/// </summary> /// </summary>
public class PositionedEdge : NamedEdge<PositionedNodeProperty, PositionedGraphNode>, SplineRouting.IEdge public class PositionedEdge : NamedEdge<PositionedNodeProperty, PositionedNode>, SplineRouting.IEdge
{ {
private IList<Point> splinePoints = new List<Point>(); private IList<Point> splinePoints = new List<Point>();
/// <summary> /// <summary>

10
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedGraph.cs

@ -13,9 +13,9 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// </summary> /// </summary>
public class PositionedGraph public class PositionedGraph
{ {
private List<PositionedGraphNode> nodes = new List<PositionedGraphNode>(); private List<PositionedNode> nodes = new List<PositionedNode>();
public PositionedGraphNode Root { get; set; } public PositionedNode Root { get; set; }
public System.Windows.Rect BoundingRect public System.Windows.Rect BoundingRect
{ {
@ -33,12 +33,12 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary> /// <summary>
/// All nodes in the graph. /// All nodes in the graph.
/// </summary> /// </summary>
public IEnumerable<PositionedGraphNode> Nodes public IEnumerable<PositionedNode> Nodes
{ {
get { return nodes; } get { return nodes; }
} }
internal void AddNode(PositionedGraphNode node) internal void AddNode(PositionedNode node)
{ {
this.nodes.Add(node); this.nodes.Add(node);
} }
@ -50,7 +50,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{ {
get get
{ {
foreach (PositionedGraphNode node in this.Nodes) foreach (PositionedNode node in this.Nodes)
{ {
foreach (PositionedEdge edge in node.Edges) foreach (PositionedEdge edge in node.Edges)
{ {

53
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedGraphNode.cs → src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedNode.cs

@ -13,18 +13,16 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary> /// <summary>
/// ObjectNode with added position information. /// ObjectNode with added position information.
/// </summary> /// </summary>
public class PositionedGraphNode : SplineRouting.IRect public class PositionedNode : SplineRouting.IRect
{ {
public static readonly double MaxHeight = 300;
/// <summary> /// <summary>
/// Creates new PositionedNode. /// Creates new PositionedNode.
/// </summary> /// </summary>
/// <param name="objectNode">Underlying ObjectNode.</param> /// <param name="objectNode">Underlying ObjectNode.</param>
public PositionedGraphNode(ObjectGraphNode objectNode) public PositionedNode(ObjectGraphNode objectNode)
{ {
this.objectNode = objectNode; this.objectNode = objectNode;
initVisualControl(); InitVisualControl();
} }
public event EventHandler<PositionedPropertyEventArgs> PropertyExpanded; public event EventHandler<PositionedPropertyEventArgs> PropertyExpanded;
@ -44,56 +42,45 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <summary> /// <summary>
/// Tree-of-properties content of this node. /// Tree-of-properties content of this node.
/// </summary> /// </summary>
public ContentNode Content public ContentNode Content { get; set; }
{
get; set; /// <summary>
} /// The size of the subtree of this node in the layout.
/// </summary>
public double SubtreeSize { get; set; }
private PositionedGraphNodeControl nodeVisualControl;
/// <summary> /// <summary>
/// Visual control to be shown for this node. /// Visual control to be shown for this node.
/// </summary> /// </summary>
public PositionedGraphNodeControl NodeVisualControl public PositionedGraphNodeControl NodeVisualControl { get; private set; }
{
get
{
return this.nodeVisualControl;
}
}
public void InitContentFromObjectNode(Expanded expanded) public void InitContentFromObjectNode(Expanded expanded)
{ {
this.Content = new ContentNode(this, null); this.Content = new ContentNode(this, null);
this.Content.InitOverride(this.ObjectNode.Content, expanded); this.Content.InitOverride(this.ObjectNode.Content, expanded);
this.nodeVisualControl.Root = this.Content; this.NodeVisualControl.Root = this.Content;
} }
private void initVisualControl() private void InitVisualControl()
{ {
this.nodeVisualControl = NodeControlCache.Instance.GetNodeControl(); this.NodeVisualControl = NodeControlCache.Instance.GetNodeControl();
this.nodeVisualControl.MaxHeight = MaxHeight;
// propagate events from nodeVisualControl // propagate events from nodeVisualControl
this.nodeVisualControl.PropertyExpanded += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_PropertyExpanded); this.NodeVisualControl.PropertyExpanded += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_PropertyExpanded);
this.nodeVisualControl.PropertyCollapsed += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_PropertyCollapsed); this.NodeVisualControl.PropertyCollapsed += new EventHandler<PositionedPropertyEventArgs>(NodeVisualControl_PropertyCollapsed);
this.nodeVisualControl.ContentNodeExpanded += new EventHandler<ContentNodeEventArgs>(NodeVisualControl_ContentNodeExpanded); this.NodeVisualControl.ContentNodeExpanded += new EventHandler<ContentNodeEventArgs>(NodeVisualControl_ContentNodeExpanded);
this.nodeVisualControl.ContentNodeCollapsed += new EventHandler<ContentNodeEventArgs>(NodeVisualControl_ContentNodeCollapsed); this.NodeVisualControl.ContentNodeCollapsed += new EventHandler<ContentNodeEventArgs>(NodeVisualControl_ContentNodeCollapsed);
} }
public IEnumerable<PositionedNodeProperty> Properties public IEnumerable<PositionedNodeProperty> Properties
{ {
get get { return this.Content.FlattenProperties(); }
{
return this.Content.FlattenProperties();
}
} }
public virtual IEnumerable<PositionedEdge> Edges public virtual IEnumerable<PositionedEdge> Edges
{ {
get get
{ {
foreach (PositionedNodeProperty property in this.Properties) foreach (PositionedNodeProperty property in this.Properties) {
{
if (property.Edge != null) if (property.Edge != null)
yield return property.Edge; yield return property.Edge;
} }
@ -102,7 +89,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
public void Measure() public void Measure()
{ {
this.nodeVisualControl.Measure(new Size(800, 800)); this.NodeVisualControl.Measure(new Size(800, 800));
} }
public double Left { get; set; } public double Left { get; set; }

6
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/PositionedNodeProperty.cs

@ -16,7 +16,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// Creates new PositionedNodeProperty. /// Creates new PositionedNodeProperty.
/// </summary> /// </summary>
/// <param name="objectProperty">Underlying <see cref="ObjectProperty"/></param> /// <param name="objectProperty">Underlying <see cref="ObjectProperty"/></param>
public PositionedNodeProperty(ObjectGraphProperty objectProperty, PositionedGraphNode containingNode, bool isPropertyExpanded) public PositionedNodeProperty(ObjectGraphProperty objectProperty, PositionedNode containingNode, bool isPropertyExpanded)
{ {
if (containingNode == null) if (containingNode == null)
throw new ArgumentNullException("containingNode"); throw new ArgumentNullException("containingNode");
@ -48,11 +48,11 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
get { return this.objectGraphProperty; } get { return this.objectGraphProperty; }
} }
private PositionedGraphNode containingNode; private PositionedNode containingNode;
/// <summary> /// <summary>
/// <see cref="PositionedNode"/> which contains this Property. /// <see cref="PositionedNode"/> which contains this Property.
/// </summary> /// </summary>
public PositionedGraphNode ContainingNode public PositionedNode ContainingNode
{ {
get { return this.containingNode; } get { return this.containingNode; }
} }

21
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeEdge.cs

@ -1,21 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
// TODO this class is almost not necessary, is used only for TreeLayouter purposes.
// TreeLayouter could remember the additional information in a Dictionary PositionedEdge -> bool
/// <summary>
/// Edge in the tree-layouted <see cref="PositionedGraph"/>.
/// </summary>
public class TreeGraphEdge : PositionedEdge
{
/// <summary>
/// Is this an edges making up the main tree?
/// </summary>
public bool IsTreeEdge { get; set; }
}
}

75
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNode.cs

@ -1,75 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Node in tree-layouted <see cref="PositionedGraph"/>.
/// This is the abstract ancestor of TreeNodeLR and TreeNodeTB.
/// There are 2 dimensions - "main" and "lateral".
/// Main dimension is the dimension in which the graph depth grows (vertical when TB, horizontal when LR).
/// Lateral dimension is the other dimension. Siblings are placed next to each other in Lateral dimension.
/// </summary>
public abstract class TreeGraphNode : PositionedGraphNode
{
public static TreeGraphNode Create(LayoutDirection direction, ObjectGraphNode objectNode)
{
switch (direction) {
case LayoutDirection.TopBottom: return new TreeNodeTB(objectNode);
case LayoutDirection.LeftRight: return new TreeNodeLR(objectNode);
default: throw new DebuggerVisualizerException("Unsupported layout direction: " + direction.ToString());
}
}
public double HorizontalMargin { get; set; }
public double VerticalMargin { get; set; }
protected TreeGraphNode(ObjectGraphNode objectNode) : base(objectNode)
{
}
/// <summary>
/// Width or height of the subtree.
/// </summary>
public double SubtreeSize { get; set; }
public abstract double MainCoord { get; set; }
public abstract double LateralCoord { get; set; }
public abstract double MainSize { get; }
public abstract double LateralSize { get; }
public double MainSizeWithMargin { get { return MainSize + MainMargin; } }
public double LateralSizeWithMargin { get { return LateralSize + LateralMargin; } }
public abstract double MainMargin { get; }
public abstract double LateralMargin { get; }
public IEnumerable<PositionedEdge> ChildEdges
{
get
{
foreach (TreeGraphEdge childEdge in Edges)
{
if (childEdge.IsTreeEdge)
{
yield return childEdge;
}
}
}
}
public IEnumerable<TreeGraphNode> Childs
{
get
{
foreach (PositionedEdge outEdge in this.ChildEdges)
yield return (TreeGraphNode)outEdge.Target;
}
}
}
}

27
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNodeLR.cs

@ -1,27 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// TreeNode used in LR layout mode.
/// </summary>
public class TreeNodeLR: TreeGraphNode
{
public TreeNodeLR(ObjectGraphNode objectNode) : base(objectNode)
{
}
public override double MainSize { get { return this.Width; } }
public override double LateralSize { get { return this.Height ; } }
public override double MainCoord { get { return this.Left; } set { this.Left = value; } }
public override double LateralCoord { get { return this.Top; } set { this.Top = value; } }
public override double MainMargin { get { return this.HorizontalMargin; } }
public override double LateralMargin { get { return this.VerticalMargin; } }
}
}

27
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeGraphNodeTB.cs

@ -1,27 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using Debugger.AddIn.Visualizers.Graph.Drawing;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// TreeNode used in TB layout mode.
/// </summary>
public class TreeNodeTB : TreeGraphNode
{
public TreeNodeTB(ObjectGraphNode objectNode) : base(objectNode)
{
}
public override double MainSize { get { return this.Height; } }
public override double LateralSize { get { return this.Width; } }
public override double MainCoord { get { return this.Top; } set { this.Top = value; } }
public override double LateralCoord { get { return this.Left; } set { this.Left = value; } }
public override double MainMargin { get { return this.VerticalMargin; } }
public override double LateralMargin { get { return this.HorizontalMargin; } }
}
}

187
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayout.cs

@ -0,0 +1,187 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using System.Windows;
using Debugger.AddIn.Visualizers.Graph.Drawing;
using System.Linq;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Calculates layout of <see cref="ObjectGraph" />, producing <see cref="PositionedGraph" />.
/// </summary>
public class TreeLayout
{
private static readonly double NodeMarginH = 30;
private static readonly double NodeMarginV = 30;
GraphEdgeRouter edgeRouter = new GraphEdgeRouter();
/// <summary>
/// The produced layout is either a horizontal or vertical tree.
/// </summary>
public LayoutDirection LayoutDirection { get; private set; }
public TreeLayout(LayoutDirection layoutDirection)
{
this.LayoutDirection = layoutDirection;
}
/// <summary>
/// Calculates layout for given <see cref="ObjectGraph" />.
/// </summary>
/// <param name="objectGraph"></param>
/// <returns></returns>
public PositionedGraph CalculateLayout(ObjectGraph objectGraph, Expanded expanded)
{
var positionedGraph = BuildPositionedGraph(objectGraph, expanded);
CalculateLayout(positionedGraph);
this.edgeRouter.RouteEdges(positionedGraph);
return positionedGraph;
}
// Expanded is passed so that the correct ContentNodes are expanded in the PositionedNode
PositionedGraph BuildPositionedGraph(ObjectGraph objectGraph, Expanded expanded)
{
var positionedNodeFor = new Dictionary<ObjectGraphNode, PositionedNode>();
var positionedGraph = new PositionedGraph();
// create empty PositionedNodes
foreach (ObjectGraphNode objectNode in objectGraph.ReachableNodes) {
var posNode = new PositionedNode(objectNode);
posNode.InitContentFromObjectNode(expanded);
positionedGraph.AddNode(posNode);
positionedNodeFor[objectNode] = posNode;
}
// create edges
foreach (PositionedNode posNode in positionedGraph.Nodes)
{
foreach (PositionedNodeProperty property in posNode.Properties) {
if (property.ObjectGraphProperty.TargetNode != null) {
ObjectGraphNode targetObjectNode = property.ObjectGraphProperty.TargetNode;
PositionedNode edgeTarget = positionedNodeFor[targetObjectNode];
property.Edge = new PositionedEdge {
Name = property.Name, Source = property, Target = edgeTarget
};
}
}
}
positionedGraph.Root = positionedNodeFor[objectGraph.Root];
return positionedGraph;
}
void CalculateLayout(PositionedGraph positionedGraph)
{
HashSet<PositionedNode> seenNodes = new HashSet<PositionedNode>();
HashSet<PositionedEdge> treeEdges = new HashSet<PositionedEdge>();
// first layout pass
CalculateSubtreeSizes(positionedGraph.Root, seenNodes, treeEdges);
// second layout pass
CalculateNodePosRecursive(positionedGraph.Root, treeEdges, 0, 0);
}
// determines which edges are tree edges, and calculates subtree size for each node
private void CalculateSubtreeSizes(PositionedNode root, HashSet<PositionedNode> seenNodes, HashSet<PositionedEdge> treeEdges)
{
seenNodes.Add(root);
double subtreeSize = 0;
foreach (var property in root.Properties) {
var edge = property.Edge;
if (edge != null) {
var targetNode = edge.Target;
if (!seenNodes.Contains(targetNode)) {
// when we come to a node for the first time, we declare the incoming edge a tree edge
treeEdges.Add(edge);
CalculateSubtreeSizes(targetNode, seenNodes, treeEdges);
subtreeSize += targetNode.SubtreeSize;
}
}
}
root.Measure();
root.SubtreeSize = Math.Max(GetLateralSizeWithMargin(root), subtreeSize);
}
/// <summary>
/// Given SubtreeSize for each node, positions the nodes, in a left-to-right or top-to-bottom layout.
/// </summary>
/// <param name="node"></param>
/// <param name="lateralStart"></param>
/// <param name="mainStart"></param>
void CalculateNodePosRecursive(PositionedNode node, HashSet<PositionedEdge> treeEdges, double lateralBase, double mainBase)
{
double childsSubtreeSize = TreeChildNodes(node, treeEdges).Sum(child => child.SubtreeSize);
double center = TreeEdges(node, treeEdges).Count() == 0 ? 0 : 0.5 * (childsSubtreeSize - (GetLateralSizeWithMargin(node)));
if (center < 0) {
// if root is larger than subtree, it would be shifted below lateralStart
// -> make whole layout start at lateralStart
lateralBase -= center;
}
SetLateral(node, GetLateral(node) + lateralBase + center);
SetMain(node, mainBase);
double childLateral = lateralBase;
double childsMainFixed = GetMain(node) + GetMainSizeWithMargin(node);
foreach (var child in TreeChildNodes(node, treeEdges)) {
CalculateNodePosRecursive(child, treeEdges, childLateral, childsMainFixed);
childLateral += child.SubtreeSize;
}
}
IEnumerable<PositionedEdge> TreeEdges(PositionedNode node, HashSet<PositionedEdge> treeEdges)
{
return node.Edges.Where(e => treeEdges.Contains(e));
}
IEnumerable<PositionedNode> TreeChildNodes(PositionedNode node, HashSet<PositionedEdge> treeEdges)
{
return TreeEdges(node, treeEdges).Select(e => e.Target);
}
#region Horizontal / vertical layout helpers
double GetMainSizeWithMargin(PositionedNode node)
{
return (this.LayoutDirection == LayoutDirection.LeftRight) ? node.Width + NodeMarginH : node.Height + NodeMarginV;
}
double GetLateralSizeWithMargin(PositionedNode node)
{
return (this.LayoutDirection == LayoutDirection.LeftRight) ? node.Height + NodeMarginV : node.Width + NodeMarginH;
}
double GetMain(PositionedNode node)
{
return (this.LayoutDirection == LayoutDirection.LeftRight) ? node.Left : node.Top;
}
double GetLateral(PositionedNode node)
{
return (this.LayoutDirection == LayoutDirection.LeftRight) ? node.Top : node.Left;
}
void SetMain(PositionedNode node, double value)
{
if (this.LayoutDirection == LayoutDirection.LeftRight) {
node.Left = value;
} else {
node.Top = value;
}
}
void SetLateral(PositionedNode node, double value)
{
if (this.LayoutDirection == LayoutDirection.LeftRight) {
node.Top = value;
} else {
node.Left = value;
}
}
#endregion
}
}

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

@ -1,137 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using System.Windows;
using Debugger.AddIn.Visualizers.Graph.Drawing;
using System.Linq;
namespace Debugger.AddIn.Visualizers.Graph.Layout
{
/// <summary>
/// Calculates layout of <see cref="ObjectGraph" />, producing <see cref="PositionedGraph" />.
/// </summary>
public class TreeLayouter
{
private static readonly double horizNodeMargin = 30;
private static readonly double vertNodeMargin = 30;
GraphEdgeRouter edgeRouter = new GraphEdgeRouter();
public TreeLayouter()
{
}
/// <summary>
/// Calculates layout for given <see cref="ObjectGraph" />.
/// </summary>
/// <param name="objectGraph"></param>
/// <returns></returns>
public PositionedGraph CalculateLayout(ObjectGraph objectGraph, LayoutDirection direction, Expanded expanded)
{
var positionedGraph = BuildPositionedGraph(objectGraph, direction, expanded);
CalculateLayout(positionedGraph);
this.edgeRouter.RouteEdges(positionedGraph);
return positionedGraph;
}
PositionedGraph BuildPositionedGraph(ObjectGraph objectGraph, LayoutDirection direction, Expanded expanded)// Expanded is passed so that the correct ContentNodes are expanded in the PositionedNode
{
var treeNodeFor = new Dictionary<ObjectGraphNode, PositionedGraphNode>();
var resultGraph = new PositionedGraph();
// create empty PositionedNodes
foreach (ObjectGraphNode objectGraphNode in objectGraph.ReachableNodes) {
TreeGraphNode posNode = TreeGraphNode.Create(direction, objectGraphNode);
posNode.InitContentFromObjectNode(expanded);
posNode.HorizontalMargin = horizNodeMargin;
posNode.VerticalMargin = vertNodeMargin;
resultGraph.AddNode(posNode);
treeNodeFor[objectGraphNode] = posNode;
}
// create edges
foreach (PositionedGraphNode posNode in resultGraph.Nodes)
{
foreach (PositionedNodeProperty property in posNode.Properties) {
if (property.ObjectGraphProperty.TargetNode != null) {
ObjectGraphNode targetObjectNode = property.ObjectGraphProperty.TargetNode;
PositionedGraphNode edgeTarget = treeNodeFor[targetObjectNode];
property.Edge = new TreeGraphEdge
{ IsTreeEdge = false, Name = property.Name, Source = property, Target = edgeTarget };
}
}
}
resultGraph.Root = treeNodeFor[objectGraph.Root];
return resultGraph;
}
void CalculateLayout(PositionedGraph resultGraph)
{
HashSet<PositionedGraphNode> seenNodes = new HashSet<PositionedGraphNode>();
// first layout pass
CalculateSubtreeSizes((TreeGraphNode)resultGraph.Root, seenNodes);
// second layout pass
CalculateNodePosRecursive((TreeGraphNode)resultGraph.Root, 0, 0);
}
// determines which edges are tree edges, and calculates subtree size for each node
private void CalculateSubtreeSizes(TreeGraphNode root, HashSet<PositionedGraphNode> seenNodes)
{
seenNodes.Add(root);
double subtreeSize = 0;
foreach (PositionedNodeProperty property in root.Properties) {
var edge = property.Edge as TreeGraphEdge; // we know that these egdes are TreeEdges
if (edge != null) {
var neigborNode = (TreeGraphNode)edge.Target;
if (seenNodes.Contains(neigborNode)) {
edge.IsTreeEdge = false;
} else {
edge.IsTreeEdge = true;
CalculateSubtreeSizes(neigborNode, seenNodes);
subtreeSize += neigborNode.SubtreeSize;
}
}
}
root.Measure();
root.SubtreeSize = Math.Max(root.LateralSizeWithMargin, subtreeSize);
}
/// <summary>
/// Given SubtreeSize for each node, positions the nodes, in a left-to-right or top-to-bottom fashion.
/// </summary>
/// <param name="node"></param>
/// <param name="lateralStart"></param>
/// <param name="mainStart"></param>
private void CalculateNodePosRecursive(TreeGraphNode node, double lateralStart, double mainStart)
{
double childsSubtreeSize = node.Childs.Sum(child => child.SubtreeSize);
// center this node
double center = node.ChildEdges.Count() == 0 ? 0 : 0.5 * (childsSubtreeSize - (node.LateralSizeWithMargin));
if (center < 0) {
// if root is larger than subtree, it would be shifted below lateralStart
// -> make whole layout start at lateralStart
lateralStart -= center;
}
// design alternatives
// node.MainPos += center; // used this
// Adapt(node).PosLateral += center; // TreeNodeAdapterLR + TreeNodeAdapterTB
// SetMainPos(node, GetMainPos(node) + 10) // TreeNodeAdapterLR + TreeNodeAdapterTB, no creation
node.LateralCoord += lateralStart + center;
node.MainCoord = mainStart;
double childLateral = lateralStart;
double childsMainFixed = node.MainCoord + node.MainSizeWithMargin;
foreach (TreeGraphNode child in node.Childs) {
CalculateNodePosRecursive(child, childLateral, childsMainFixed);
childLateral += child.SubtreeSize;
}
}
}
}

27
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/ObjectGraph/ObjectGraph.cs

@ -8,8 +8,11 @@ using System.Text;
namespace Debugger.AddIn.Visualizers.Graph namespace Debugger.AddIn.Visualizers.Graph
{ {
/// <summary> /// <summary>
/// Object graph built by <see cref="ObjectGraphBuilder"/>. The graph is never empty. /// Object graph built by <see cref="ObjectGraphBuilder"/>.
/// </summary> /// </summary>
/// <remarks>
/// The graph is never empty.
/// </remarks>
public class ObjectGraph public class ObjectGraph
{ {
/// <summary> /// <summary>
@ -23,16 +26,16 @@ namespace Debugger.AddIn.Visualizers.Graph
/// <param name="node">node to be added</param> /// <param name="node">node to be added</param>
internal void AddNode(ObjectGraphNode node) internal void AddNode(ObjectGraphNode node)
{ {
_nodes.Add(node); nodes.Add(node);
} }
private List<ObjectGraphNode> _nodes = new List<ObjectGraphNode>(); private List<ObjectGraphNode> nodes = new List<ObjectGraphNode>();
/// <summary> /// <summary>
/// All nodes in the graph. Should always contain at least one node. /// All nodes in the graph. Should always contain at least one node.
/// </summary> /// </summary>
public IEnumerable<ObjectGraphNode> Nodes public IEnumerable<ObjectGraphNode> Nodes
{ {
get { return _nodes; } get { return nodes; }
} }
// HACK to support expanding/collapsing, because expanding is done by modifying ObjectGraph and rebuiling PosGraph // HACK to support expanding/collapsing, because expanding is done by modifying ObjectGraph and rebuiling PosGraph
@ -40,22 +43,20 @@ namespace Debugger.AddIn.Visualizers.Graph
{ {
get get
{ {
var seenNodes = new HashSet<ObjectGraphNode>(); var reachableNodes = new HashSet<ObjectGraphNode>();
determineReachableNodes(this.Root, seenNodes); FindReachableNodesRecursive(this.Root, reachableNodes);
foreach (var node in seenNodes) foreach (var node in reachableNodes) {
{
yield return node; yield return node;
} }
} }
} }
private void determineReachableNodes(ObjectGraphNode root, HashSet<ObjectGraphNode> seenNodes)
void FindReachableNodesRecursive(ObjectGraphNode root, HashSet<ObjectGraphNode> seenNodes)
{ {
seenNodes.Add(root); seenNodes.Add(root);
foreach(var prop in root.Properties) {
foreach(var prop in root.Properties)
{
if (prop.TargetNode != null && !seenNodes.Contains(prop.TargetNode)) if (prop.TargetNode != null && !seenNodes.Contains(prop.TargetNode))
determineReachableNodes(prop.TargetNode, seenNodes); FindReachableNodesRecursive(prop.TargetNode, seenNodes);
} }
} }
} }

126
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/ObjectGraphControl.xaml.cs

@ -14,9 +14,11 @@ using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using Debugger.AddIn.Visualizers.Graph.Layout; using Debugger.AddIn.Visualizers.Graph.Layout;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Services; using ICSharpCode.SharpDevelop.Services;
using ICSharpCode.NRefactory.Ast;
using Log = ICSharpCode.Core.LoggingService;
namespace Debugger.AddIn.Visualizers.Graph namespace Debugger.AddIn.Visualizers.Graph
{ {
@ -33,7 +35,6 @@ namespace Debugger.AddIn.Visualizers.Graph
private PositionedGraph oldPosGraph; private PositionedGraph oldPosGraph;
private PositionedGraph currentPosGraph; private PositionedGraph currentPosGraph;
private GraphDrawer graphDrawer; private GraphDrawer graphDrawer;
private Layout.TreeLayouter layouter;
/// <summary> Long-lived map telling which graph nodes and content nodes the user expanded. </summary> /// <summary> Long-lived map telling which graph nodes and content nodes the user expanded. </summary>
private Expanded expanded = new Expanded(); private Expanded expanded = new Expanded();
@ -50,7 +51,6 @@ namespace Debugger.AddIn.Visualizers.Graph
this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged); this.layoutViewModel.PropertyChanged += new PropertyChangedEventHandler(layoutViewModel_PropertyChanged);
this.cmbLayoutDirection.DataContext = this.layoutViewModel; this.cmbLayoutDirection.DataContext = this.layoutViewModel;
this.layouter = new TreeLayouter();
this.graphDrawer = new GraphDrawer(this.canvas); this.graphDrawer = new GraphDrawer(this.canvas);
} }
@ -61,7 +61,40 @@ namespace Debugger.AddIn.Visualizers.Graph
public void Refresh() public void Refresh()
{ {
refreshGraph(); clearErrorMessage();
if (string.IsNullOrEmpty(txtExpression.Text))
{
this.graphDrawer.ClearCanvas();
return;
}
if (debuggerService.IsProcessRunning) // "Process not paused" exception still occurs
{
ErrorMessage("Cannot inspect when the process is running.");
return;
}
bool isSuccess = true;
try
{
this.objectGraph = RebuildGraph(txtExpression.Text);
}
catch(DebuggerVisualizerException ex)
{
isSuccess = false;
ErrorMessage(ex.Message);
}
catch(Debugger.GetValueException ex)
{
isSuccess = false;
ErrorMessage("Expression cannot be evaluated - " + ex.Message);
}
if (isSuccess)
{
LayoutGraph(this.objectGraph);
}
else
{
this.graphDrawer.ClearCanvas();
}
} }
private ICSharpCode.NRefactory.Ast.Expression shownExpression; private ICSharpCode.NRefactory.Ast.Expression shownExpression;
@ -97,80 +130,38 @@ namespace Debugger.AddIn.Visualizers.Graph
this.Refresh(); this.Refresh();
} }
void refreshGraph() ObjectGraph RebuildGraph(string expression)
{
clearErrorMessage();
if (string.IsNullOrEmpty(txtExpression.Text))
{
this.graphDrawer.ClearCanvas();
return;
}
if (debuggerService.IsProcessRunning) // "Process not paused" exception still occurs
{
showErrorMessage("Cannot inspect when the process is running.");
return;
}
bool graphBuiltOk = true;
try
{
this.objectGraph = rebuildGraph(txtExpression.Text);
}
catch(DebuggerVisualizerException ex)
{
graphBuiltOk = false;
showErrorMessage(ex.Message);
}
catch(Debugger.GetValueException ex)
{
graphBuiltOk = false;
showErrorMessage("Expression cannot be evaluated - " + ex.Message);
}
if (graphBuiltOk)
{
layoutGraph(this.objectGraph);
}
else
{
this.graphDrawer.ClearCanvas();
}
}
ObjectGraph rebuildGraph(string expression)
{ {
this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService); this.objectGraphBuilder = new ObjectGraphBuilder(debuggerService);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text); Log.Debug("Debugger visualizer: Building graph for expression: " + txtExpression.Text);
return this.objectGraphBuilder.BuildGraphForExpression(expression, this.expanded.Expressions); return this.objectGraphBuilder.BuildGraphForExpression(expression, this.expanded.Expressions);
} }
void layoutGraph(ObjectGraph graph) void LayoutGraph(ObjectGraph graph)
{ {
if (this.oldPosGraph != null) if (this.oldPosGraph != null) {
{ foreach (var oldNode in this.oldPosGraph.Nodes) {
foreach (var oldNode in this.oldPosGraph.Nodes)
{
// controls from old graph would be garbage collected, reuse them // controls from old graph would be garbage collected, reuse them
NodeControlCache.Instance.ReturnForReuse(oldNode.NodeVisualControl); NodeControlCache.Instance.ReturnForReuse(oldNode.NodeVisualControl);
} }
} }
this.oldPosGraph = this.currentPosGraph; this.oldPosGraph = this.currentPosGraph;
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Calculating graph layout"); Log.Debug("Debugger visualizer: Calculating graph layout");
this.currentPosGraph = this.layouter.CalculateLayout(graph, layoutViewModel.SelectedEnumValue, this.expanded); var layoutDirection = layoutViewModel.SelectedEnumValue;
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: Graph layout done"); this.currentPosGraph = new TreeLayout(layoutDirection).CalculateLayout(graph, this.expanded);
registerExpandCollapseEvents(this.currentPosGraph); Log.Debug("Debugger visualizer: Graph layout done");
RegisterExpandCollapseEvents(this.currentPosGraph);
var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph); var graphDiff = new GraphMatcher().MatchGraphs(oldPosGraph, currentPosGraph);
ICSharpCode.Core.LoggingService.Debug("Debugger visualizer: starting graph animation"); Log.Debug("Debugger visualizer: starting graph animation");
this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff); this.graphDrawer.StartAnimation(oldPosGraph, currentPosGraph, graphDiff);
//this.graphDrawer.Draw(this.currentPosGraph); // buggy layout with NodeControlCache
} }
void layoutViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) void layoutViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == "SelectedEnumValue") // TODO special event for enum value change if (e.PropertyName == "SelectedEnumValue") {
{ if (this.objectGraph != null) {
if (this.objectGraph != null) LayoutGraph(this.objectGraph);
{
layoutGraph(this.objectGraph);
} }
} }
} }
@ -180,17 +171,16 @@ namespace Debugger.AddIn.Visualizers.Graph
this.pnlError.Visibility = Visibility.Collapsed; this.pnlError.Visibility = Visibility.Collapsed;
} }
void showErrorMessage(string message) void ErrorMessage(string message)
{ {
this.txtError.Text = message; this.txtError.Text = message;
this.pnlError.Visibility = Visibility.Visible; this.pnlError.Visibility = Visibility.Visible;
//MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error); //MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error);
} }
void registerExpandCollapseEvents(PositionedGraph posGraph) void RegisterExpandCollapseEvents(PositionedGraph posGraph)
{ {
foreach (var node in posGraph.Nodes) foreach (var node in posGraph.Nodes) {
{
node.PropertyExpanded += new EventHandler<PositionedPropertyEventArgs>(node_PropertyExpanded); node.PropertyExpanded += new EventHandler<PositionedPropertyEventArgs>(node_PropertyExpanded);
node.PropertyCollapsed += new EventHandler<PositionedPropertyEventArgs>(node_PropertyCollapsed); node.PropertyCollapsed += new EventHandler<PositionedPropertyEventArgs>(node_PropertyCollapsed);
node.ContentNodeExpanded += new EventHandler<ContentNodeEventArgs>(node_ContentNodeExpanded); node.ContentNodeExpanded += new EventHandler<ContentNodeEventArgs>(node_ContentNodeExpanded);
@ -201,13 +191,13 @@ namespace Debugger.AddIn.Visualizers.Graph
void node_ContentNodeExpanded(object sender, ContentNodeEventArgs e) void node_ContentNodeExpanded(object sender, ContentNodeEventArgs e)
{ {
expanded.ContentNodes.SetExpanded(e.Node); expanded.ContentNodes.SetExpanded(e.Node);
layoutGraph(this.objectGraph); LayoutGraph(this.objectGraph);
} }
void node_ContentNodeCollapsed(object sender, ContentNodeEventArgs e) void node_ContentNodeCollapsed(object sender, ContentNodeEventArgs e)
{ {
expanded.ContentNodes.SetCollapsed(e.Node); expanded.ContentNodes.SetCollapsed(e.Node);
layoutGraph(this.objectGraph); LayoutGraph(this.objectGraph);
} }
void node_PropertyExpanded(object sender, PositionedPropertyEventArgs e) void node_PropertyExpanded(object sender, PositionedPropertyEventArgs e)
@ -218,7 +208,7 @@ namespace Debugger.AddIn.Visualizers.Graph
// add edge (+ possibly nodes) to underlying object graph (no need to fully rebuild) // add edge (+ possibly nodes) to underlying object graph (no need to fully rebuild)
// TODO can add more nodes if they are expanded - now this adds always one node // TODO can add more nodes if they are expanded - now this adds always one node
e.Property.ObjectGraphProperty.TargetNode = this.objectGraphBuilder.ObtainNodeForExpression(e.Property.Expression); e.Property.ObjectGraphProperty.TargetNode = this.objectGraphBuilder.ObtainNodeForExpression(e.Property.Expression);
layoutGraph(this.objectGraph); LayoutGraph(this.objectGraph);
} }
void node_PropertyCollapsed(object sender, PositionedPropertyEventArgs e) void node_PropertyCollapsed(object sender, PositionedPropertyEventArgs e)
@ -228,7 +218,7 @@ namespace Debugger.AddIn.Visualizers.Graph
// just remove edge from underlying object graph (no need to fully rebuild) // just remove edge from underlying object graph (no need to fully rebuild)
e.Property.ObjectGraphProperty.TargetNode = null; e.Property.ObjectGraphProperty.TargetNode = null;
layoutGraph(this.objectGraph); LayoutGraph(this.objectGraph);
} }
} }
} }

Loading…
Cancel
Save