Browse Source

Object graph - refactored TreeLayouter.

pull/15/head
mkonicek 15 years ago
parent
commit
4897789b4e
  1. 18
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/GraphDrawer.cs
  2. 11
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml
  3. 7
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml.cs
  4. 3
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/GraphEdgeRouter.cs
  5. 96
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Layout/Tree/TreeLayouter.cs
  6. 3
      src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/NodeControlCache.cs

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

@ -39,22 +39,10 @@ namespace Debugger.AddIn.Visualizers.Graph
/// <param name="diff"></param> /// <param name="diff"></param>
public void StartAnimation(PositionedGraph oldGraph, PositionedGraph newGraph, GraphDiff diff) public void StartAnimation(PositionedGraph oldGraph, PositionedGraph newGraph, GraphDiff diff)
{ {
if (oldGraph != null) // account for that the visual controls could have been reused (we are not reusing controls now - NodeControlCache does nothing)
{
foreach (var oldNode in oldGraph.Nodes)
{
foreach (var newNode in newGraph.Nodes)
{
if (oldNode.NodeVisualControl == newNode.NodeVisualControl)
{
ClearCanvas();
}
}
}
}
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)
{ {

11
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml

@ -13,7 +13,11 @@
</UserControl.Resources> </UserControl.Resources>
<aero:SystemDropShadowChrome> <aero:SystemDropShadowChrome>
<Grid> <StackPanel Orientation="Vertical">
<!--
<Border Background="#ddeeff" BorderThickness="1,1,1,0" BorderBrush="DarkGray" HorizontalAlignment="Stretch" Padding="1">
<TextBlock>Hello</TextBlock>
</Border> -->
<ListView Name="listView" ScrollViewer.VerticalScrollBarVisibility="Auto"> <ListView Name="listView" ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListView.Background> <ListView.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
@ -51,8 +55,7 @@
<GridView> <GridView>
<GridView.ColumnHeaderContainerStyle> <GridView.ColumnHeaderContainerStyle>
<Style> <Style>
<!-- <!-- <Setter Property="FrameworkElement.Visibility" Value="Collapsed"/> -->
<Setter Property="FrameworkElement.Visibility" Value="Collapsed"/> -->
<Setter Property="FrameworkElement.Height" Value="1"/> <Setter Property="FrameworkElement.Height" Value="1"/>
</Style> </Style>
</GridView.ColumnHeaderContainerStyle> </GridView.ColumnHeaderContainerStyle>
@ -98,6 +101,6 @@
</GridView> </GridView>
</ListView.View> </ListView.View>
</ListView> </ListView>
</Grid> </StackPanel>
</aero:SystemDropShadowChrome> </aero:SystemDropShadowChrome>
</UserControl> </UserControl>

7
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/Drawing/PositionedGraphNodeControl.xaml.cs

@ -59,16 +59,15 @@ namespace Debugger.AddIn.Visualizers.Graph.Drawing
{ {
this.root = value; this.root = value;
this.view = getInitialView(this.root); this.view = getInitialView(this.root);
// data virtualization, PropertyNodeViewModel implements IEvaluate // data virtualization, ContentPropertyNode implements IEvaluate
this.listView.ItemsSource = new VirtualizingObservableCollection<ContentNode>(this.view); this.listView.ItemsSource = new VirtualizingObservableCollection<ContentNode>(this.view);
int maxLen = this.view.MaxOrDefault(contentNode => { return contentNode.Name.Length; }, 0); /*int maxLen = this.view.MaxOrDefault(contentNode => { return contentNode.Name.Length; }, 0);
int spaces = Math.Max((int)(maxLen * 1.8 - 3), 0); int spaces = Math.Max((int)(maxLen * 1.8 - 3), 0);
string addedSpaces = StringHelper.Repeat(' ', spaces); string addedSpaces = StringHelper.Repeat(' ', spaces);
GridView gv = listView.View as GridView; GridView gv = listView.View as GridView;
// hack - autosize Name column // hack - autosize Name column
gv.Columns[1].Header = "Name" + addedSpaces; gv.Columns[1].Header = "Name" + addedSpaces;*/
//AutoSizeColumns(); //AutoSizeColumns();

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

@ -21,7 +21,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
{ {
} }
public PositionedGraph RouteEdges(PositionedGraph posGraph) public void RouteEdges(PositionedGraph posGraph)
{ {
List<RoutedEdge> routedEdges = router.RouteEdges(posGraph.Nodes, posGraph.Edges); List<RoutedEdge> routedEdges = router.RouteEdges(posGraph.Nodes, posGraph.Edges);
int i = 0; int i = 0;
@ -30,7 +30,6 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
SetEdgeSplinePoints(edge, routedEdges[i]); SetEdgeSplinePoints(edge, routedEdges[i]);
i++; i++;
} }
return posGraph;
} }
void SetEdgeSplinePoints(PositionedEdge edge, RoutedEdge routedEdge) void SetEdgeSplinePoints(PositionedEdge edge, RoutedEdge routedEdge)

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

@ -17,10 +17,7 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
private static readonly double horizNodeMargin = 30; private static readonly double horizNodeMargin = 30;
private static readonly double vertNodeMargin = 30; private static readonly double vertNodeMargin = 30;
private LayoutDirection layoutDirection = LayoutDirection.TopBottom; GraphEdgeRouter edgeRouter = new GraphEdgeRouter();
HashSet<PositionedGraphNode> seenNodes = new HashSet<PositionedGraphNode>();
Dictionary<ObjectGraphNode, PositionedGraphNode> treeNodeFor = new Dictionary<ObjectGraphNode, PositionedGraphNode>();
public TreeLayouter() public TreeLayouter()
{ {
@ -33,50 +30,33 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
/// <returns></returns> /// <returns></returns>
public PositionedGraph CalculateLayout(ObjectGraph objectGraph, LayoutDirection direction, Expanded expanded) public PositionedGraph CalculateLayout(ObjectGraph objectGraph, LayoutDirection direction, Expanded expanded)
{ {
layoutDirection = direction; var positionedGraph = BuildPositionedGraph(objectGraph, direction, expanded);
CalculateLayout(positionedGraph);
treeNodeFor = new Dictionary<ObjectGraphNode, PositionedGraphNode>(); this.edgeRouter.RouteEdges(positionedGraph);
seenNodes = new HashSet<PositionedGraphNode>();
//TreeGraphNode tree = buildTreeRecursive(objectGraph.Root, expandedNodes); return positionedGraph;
// convert ObjectGraph to PositionedGraph with TreeEdges
var resultGraph = buildTreeGraph(objectGraph, expanded);
// first layout pass
calculateSubtreeSizes((TreeGraphNode)resultGraph.Root);
// second layout pass
calculateNodePosRecursive((TreeGraphNode)resultGraph.Root, 0, 0);
//var neatoRouter = new NeatoEdgeRouter();
//resultGraph = neatoRouter.CalculateEdges(resultGraph);
resultGraph = new GraphEdgeRouter().RouteEdges(resultGraph);
return resultGraph;
} }
private PositionedGraph buildTreeGraph(ObjectGraph objectGraph, Expanded expanded) 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(); var resultGraph = new PositionedGraph();
// create empty PosNodes // create empty PositionedNodes
foreach (ObjectGraphNode objectGraphNode in objectGraph.ReachableNodes) foreach (ObjectGraphNode objectGraphNode in objectGraph.ReachableNodes) {
{ TreeGraphNode posNode = TreeGraphNode.Create(direction, objectGraphNode);
TreeGraphNode posNode = createNewTreeGraphNode(objectGraphNode); posNode.InitContentFromObjectNode(expanded);
posNode.HorizontalMargin = horizNodeMargin;
posNode.VerticalMargin = vertNodeMargin;
resultGraph.AddNode(posNode); resultGraph.AddNode(posNode);
treeNodeFor[objectGraphNode] = posNode; treeNodeFor[objectGraphNode] = posNode;
posNode.InitContentFromObjectNode(expanded);
} }
// create edges // create edges
foreach (PositionedGraphNode posNode in resultGraph.Nodes) foreach (PositionedGraphNode posNode in resultGraph.Nodes)
{ {
// create edges outgoing from this posNode foreach (PositionedNodeProperty property in posNode.Properties) {
foreach (PositionedNodeProperty property in posNode.Properties) if (property.ObjectGraphProperty.TargetNode != null) {
{
//property.IsPropertyExpanded = expanded.Expressions.IsExpanded(property.Expression);
if (property.ObjectGraphProperty.TargetNode != null)
{
ObjectGraphNode targetObjectNode = property.ObjectGraphProperty.TargetNode; ObjectGraphNode targetObjectNode = property.ObjectGraphProperty.TargetNode;
PositionedGraphNode edgeTarget = treeNodeFor[targetObjectNode]; PositionedGraphNode edgeTarget = treeNodeFor[targetObjectNode];
property.Edge = new TreeGraphEdge property.Edge = new TreeGraphEdge
@ -84,62 +64,55 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
} }
} }
} }
resultGraph.Root = treeNodeFor[objectGraph.Root]; resultGraph.Root = treeNodeFor[objectGraph.Root];
return resultGraph; 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 // determines which edges are tree edges, and calculates subtree size for each node
private void calculateSubtreeSizes(TreeGraphNode root) private void CalculateSubtreeSizes(TreeGraphNode root, HashSet<PositionedGraphNode> seenNodes)
{ {
seenNodes.Add(root); seenNodes.Add(root);
double subtreeSize = 0; double subtreeSize = 0;
foreach (PositionedNodeProperty property in root.Properties) foreach (PositionedNodeProperty property in root.Properties) {
{
var edge = property.Edge as TreeGraphEdge; // we know that these egdes are TreeEdges var edge = property.Edge as TreeGraphEdge; // we know that these egdes are TreeEdges
if (edge != null) if (edge != null) {
{
var neigborNode = (TreeGraphNode)edge.Target; var neigborNode = (TreeGraphNode)edge.Target;
if (seenNodes.Contains(neigborNode)) if (seenNodes.Contains(neigborNode)) {
{
edge.IsTreeEdge = false; edge.IsTreeEdge = false;
} } else {
else
{
edge.IsTreeEdge = true; edge.IsTreeEdge = true;
calculateSubtreeSizes(neigborNode); CalculateSubtreeSizes(neigborNode, seenNodes);
subtreeSize += neigborNode.SubtreeSize; subtreeSize += neigborNode.SubtreeSize;
} }
} }
} }
root.Measure(); root.Measure();
root.SubtreeSize = Math.Max(root.LateralSizeWithMargin, subtreeSize); root.SubtreeSize = Math.Max(root.LateralSizeWithMargin, subtreeSize);
} }
private TreeGraphNode createNewTreeGraphNode(ObjectGraphNode objectGraphNode)
{
var newGraphNode = TreeGraphNode.Create(this.layoutDirection, objectGraphNode);
newGraphNode.HorizontalMargin = horizNodeMargin;
newGraphNode.VerticalMargin = vertNodeMargin;
return newGraphNode;
}
/// <summary> /// <summary>
/// Given SubtreeSize for each node, positions the nodes, in a left-to-right or top-to-bottom fashion. /// Given SubtreeSize for each node, positions the nodes, in a left-to-right or top-to-bottom fashion.
/// </summary> /// </summary>
/// <param name="node"></param> /// <param name="node"></param>
/// <param name="lateralStart"></param> /// <param name="lateralStart"></param>
/// <param name="mainStart"></param> /// <param name="mainStart"></param>
private void calculateNodePosRecursive(TreeGraphNode node, double lateralStart, double mainStart) private void CalculateNodePosRecursive(TreeGraphNode node, double lateralStart, double mainStart)
{ {
double childsSubtreeSize = node.Childs.Sum(child => child.SubtreeSize); double childsSubtreeSize = node.Childs.Sum(child => child.SubtreeSize);
// center this node // center this node
double center = node.ChildEdges.Count() == 0 ? 0 : 0.5 * (childsSubtreeSize - (node.LateralSizeWithMargin)); double center = node.ChildEdges.Count() == 0 ? 0 : 0.5 * (childsSubtreeSize - (node.LateralSizeWithMargin));
if (center < 0) if (center < 0) {
{
// if root is larger than subtree, it would be shifted below lateralStart // if root is larger than subtree, it would be shifted below lateralStart
// -> make whole layout start at lateralStart // -> make whole layout start at lateralStart
lateralStart -= center; lateralStart -= center;
@ -155,9 +128,8 @@ namespace Debugger.AddIn.Visualizers.Graph.Layout
double childLateral = lateralStart; double childLateral = lateralStart;
double childsMainFixed = node.MainCoord + node.MainSizeWithMargin; double childsMainFixed = node.MainCoord + node.MainSizeWithMargin;
foreach (TreeGraphNode child in node.Childs) foreach (TreeGraphNode child in node.Childs) {
{ CalculateNodePosRecursive(child, childLateral, childsMainFixed);
calculateNodePosRecursive(child, childLateral, childsMainFixed);
childLateral += child.SubtreeSize; childLateral += child.SubtreeSize;
} }
} }

3
src/AddIns/Debugger/Debugger.AddIn/Visualizers/Graph/NodeControlCache.cs

@ -25,7 +25,8 @@ namespace Debugger.AddIn.Visualizers.Graph
public void ReturnForReuse(PositionedGraphNodeControl controlToReuse) public void ReturnForReuse(PositionedGraphNodeControl controlToReuse)
{ {
controls.Push(controlToReuse); // not used now, avoid leaking!
//controls.Push(controlToReuse);
} }
public PositionedGraphNodeControl GetNodeControl() public PositionedGraphNodeControl GetNodeControl()

Loading…
Cancel
Save