private void Expand(LayoutNode <T> lRoot, Dictionary <T, LayoutNode <T> > map) { map.Add(lRoot.Content, lRoot); var children = GetChildren(lRoot.Content).ToList(); if (!children.Any()) { return; } lRoot.Children = new List <LayoutNode <T> >(children.Count); for (int i = 0; i < children.Count; ++i) { var node = new LayoutNode <T> { Content = children[i], Number = i, Parent = lRoot, Level = lRoot.Level + 1, Width = NodeWidth, Height = NodeHeight }; node.Ancestor = node; lRoot.Children.Add(node); Expand(node, map); } }
public IEnumerable <VisualTreeNode <T> > CalculateLayout(T root, float width, float height) { Dictionary <T, LayoutNode <T> > layoutNodeMap = new Dictionary <T, LayoutNode <T> >(); var layoutRoot = new LayoutNode <T> { Content = root, Width = NodeWidth, Height = NodeHeight, }; layoutRoot.Ancestor = layoutRoot; Expand(layoutRoot, layoutNodeMap); FirstWalk(layoutRoot); SecondWalk(layoutRoot, -layoutRoot.Prelim); NormalizeCoordinates(layoutNodeMap.Values); if (width > 0 && height > 0) { FitToBounds(width, height, layoutNodeMap.Values); Center(width, height, layoutNodeMap.Values); } return(layoutNodeMap.Values.Select(x => new VisualTreeNode <T>(x.Content) { Width = (int)Math.Round(x.Width), Height = (int)Math.Round(x.Height), X = (int)Math.Round(x.X), Y = (int)Math.Round(x.Y) })); }
private LayoutNode <T> Ancestor(LayoutNode <T> u, LayoutNode <T> v) { var ancestor = u.Ancestor; if (ancestor == null) { return(null); } return(ancestor.Parent == v.Parent ? ancestor : null); }
private void SecondWalk(LayoutNode <T> v, float m) { v.X = v.Prelim + m; v.Y = v.Level * (minVerticalSpacing + NodeHeight); if (v.IsLeaf) { return; } foreach (var child in v.Children) { SecondWalk(child, m + v.Mod); } }
private void MoveSubtree(LayoutNode <T> wm, LayoutNode <T> wp, float shift) { int subtrees = wp.Number - wm.Number; // TODO: Investigate possible bug (if the value ever happens to be zero) - happens when the tree is actually a graph (but that's outside the use case of this algorithm which only works with trees) if (subtrees == 0) { throw new Exception("MoveSubtree failed: check if object is really a tree (no cycles)"); } wp.Change -= shift / subtrees; wp.Shift += shift; wm.Change += shift / subtrees; wp.Prelim += shift; wp.Mod += shift; }
private void Apportion(LayoutNode <T> v, ref LayoutNode <T> defaultAncestor) { var w = v.LeftSibling; if (w == null) { return; } LayoutNode <T> vip = v; LayoutNode <T> vop = v; LayoutNode <T> vim = w; LayoutNode <T> vom = vip.LeftmostSibling; float sip = vip.Mod; float sop = vop.Mod; float sim = vim.Mod; float som = vom.Mod; while (vim.NextRight != null && vip.NextLeft != null) { vim = vim.NextRight; vip = vip.NextLeft; vom = vom.NextLeft; vop = vop.NextRight; vop.Ancestor = v; float shift = (vim.Prelim + sim) - (vip.Prelim + sip) + minHorizontalSpacing + NodeWidth; if (shift > 0) { var ancestor = Ancestor(vim, v) ?? defaultAncestor; MoveSubtree(ancestor, v, shift); sip += shift; sop += shift; } sim += vim.Mod; sip += vip.Mod; som += vom.Mod; sop += vop.Mod; } if (vim.NextRight != null && vop.NextRight == null) { vop.Thread = vim.NextRight; vop.Mod += (sim - sop); } if (vip.NextLeft != null && vom.NextLeft == null) { vom.Thread = vip.NextLeft; vom.Mod += (sip - som); defaultAncestor = v; } }
private void ExecuteShifts(LayoutNode <T> v) { if (v.IsLeaf) { return; } float shift = 0; float change = 0; for (int i = v.Children.Count - 1; i >= 0; --i) { var w = v.Children[i]; w.Prelim += shift; w.Mod += shift; change += w.Change; shift += (w.Shift + change); } }
private void FirstWalk(LayoutNode <T> v) { LayoutNode <T> w; if (v.IsLeaf) { w = v.LeftSibling; if (w != null) { v.Prelim = w.Prelim + minHorizontalSpacing + NodeWidth; } } else { var defaultAncestor = v.Children[0]; // leftmost child foreach (var child in v.Children) { FirstWalk(child); Apportion(child, ref defaultAncestor); } ExecuteShifts(v); var leftmost = v.Children.First(); var rightmost = v.Children.Last(); float midPoint = (leftmost.Prelim + rightmost.Prelim) / 2; w = v.LeftSibling; if (w != null) { v.Prelim = w.Prelim + minHorizontalSpacing + NodeWidth; v.Mod = v.Prelim - midPoint; } else { v.Prelim = midPoint; } } }