private static void CenterNodesBetween(TreeNodeModel <T> leftNode, TreeNodeModel <T> rightNode) { var leftIndex = leftNode.Parent.Children.IndexOf(rightNode); var rightIndex = leftNode.Parent.Children.IndexOf(leftNode); var numNodesBetween = rightIndex - leftIndex - 1; if (numNodesBetween > 0) { var distanceBetweenNodes = (leftNode.X - rightNode.X) / (numNodesBetween + 1); var count = 1; for (var i = leftIndex + 1; i < rightIndex; i++) { var middleNode = leftNode.Parent.Children[i]; var desiredX = rightNode.X + (distanceBetweenNodes * count); var offset = desiredX - middleNode.X; middleNode.X += offset; middleNode.Mod += offset; count++; } CheckForConflicts(leftNode); } }
private static void CalculateInitialX(TreeNodeModel <T> node) { foreach (var child in node.Children) { CalculateInitialX(child); } // if no children if (node.IsLeaf()) { // if there is a previous sibling in this set, set X to previous sibling + designated distance if (!node.IsLeftMost()) { node.X = node.GetPreviousSibling().X + NodeSize + SiblingDistance; } else { // if this is the first node in a set, set X to 0 node.X = 0; } } // if there is only one child else if (node.Children.Count == 1) { // if this is the first node in a set, set it's X value equal to it's child's X value if (node.IsLeftMost()) { node.X = node.Children[0].X; } else { node.X = node.GetPreviousSibling().X + NodeSize + SiblingDistance; node.Mod = node.X - node.Children[0].X; } } else { var leftChild = node.GetLeftMostChild(); var rightChild = node.GetRightMostChild(); var mid = (leftChild.X + rightChild.X) / 2; if (node.IsLeftMost()) { node.X = mid; } else { node.X = node.GetPreviousSibling().X + NodeSize + SiblingDistance; node.Mod = node.X - mid; } } if (node.Children.Count > 0 && !node.IsLeftMost()) { // Since subtrees can overlap, check for conflicts and shift tree right if needed CheckForConflicts(node); } }
// converts list of sample items to hierarchical list of TreeNodeModels private static TreeNodeModel <NodeModel> GetSampleTree(List <NodeModel> data) { var root = data.FirstOrDefault(p => p.ParentId == string.Empty); var rootTreeNode = new TreeNodeModel <NodeModel>(root, null); // add tree node children recursively rootTreeNode.Children = GetChildNodes(data, rootTreeNode); return(rootTreeNode); }
// recursively initialize x, y, and mod values of nodes private static void InitializeNodes(TreeNodeModel <T> node, int depth) { node.X = -1; node.Y = depth; node.Mod = 0; foreach (var child in node.Children) { InitializeNodes(child, depth + 1); } }
private static Size GetSize(TreeNodeModel <NodeModel> tree) { // tree sizes are 0-based, so add 1 var treeWidth = tree.Width + 1; var treeHeight = tree.Height + 1; var size = new Size(Convert.ToInt32(treeWidth * NodeWidth + (treeWidth + 1) * NodeMarginX), treeHeight * NodeHeight + (treeHeight + 1) * NodeMarginY); return(size); }
public static void CalculateNodePositions(TreeNodeModel <T> rootNode) { // initialize node x, y, and mod values InitializeNodes(rootNode, 0); // assign initial X and Mod values for nodes CalculateInitialX(rootNode); // ensure no node is being drawn off screen CheckAllChildrenOnScreen(rootNode); // assign final X values to nodes CalculateFinalPositions(rootNode, 0); }
private static List <TreeNodeModel <NodeModel> > GetChildNodes(List <NodeModel> data, TreeNodeModel <NodeModel> parent) { var nodes = new List <TreeNodeModel <NodeModel> >(); foreach (var item in data.Where(p => p.ParentId == parent.Item.Id)) { var treeNode = new TreeNodeModel <NodeModel>(item, parent); treeNode.Children = GetChildNodes(data, treeNode); nodes.Add(treeNode); } return(nodes); }
private static void GetRightContour(TreeNodeModel <T> node, float modSum, ref Dictionary <int, float> values) { if (!values.ContainsKey(node.Y)) { values.Add(node.Y, node.X + modSum); } else { values[node.Y] = Math.Max(values[node.Y], node.X + modSum); } modSum += node.Mod; foreach (var child in node.Children) { GetRightContour(child, modSum, ref values); } }
private static void CalculateFinalPositions(TreeNodeModel <T> node, float modSum) { node.X += modSum; modSum += node.Mod; foreach (var child in node.Children) { CalculateFinalPositions(child, modSum); } if (node.Children.Count == 0) { node.Width = node.X; node.Height = node.Y; } else { node.Width = node.Children.OrderByDescending(p => p.Width).First().Width; node.Height = node.Children.OrderByDescending(p => p.Height).First().Height; } }
private static void CheckForConflicts(TreeNodeModel <T> node) { const float minDistance = TreeDistance + NodeSize; var shiftValue = 0F; var nodeContour = new Dictionary <int, float>(); GetLeftContour(node, 0, ref nodeContour); var sibling = node.GetLeftMostSibling(); while (sibling != null && sibling != node) { var siblingContour = new Dictionary <int, float>(); GetRightContour(sibling, 0, ref siblingContour); for (var level = node.Y + 1; level <= Math.Min(siblingContour.Keys.Max(), nodeContour.Keys.Max()); level++) { var distance = nodeContour[level] - siblingContour[level]; if (distance + shiftValue < minDistance) { shiftValue = minDistance - distance; } } if (shiftValue > 0) { node.X += shiftValue; node.Mod += shiftValue; CenterNodesBetween(node, sibling); shiftValue = 0; } sibling = sibling.GetNextSibling(); } }
private static void CheckAllChildrenOnScreen(TreeNodeModel <T> node) { var nodeContour = new Dictionary <int, float>(); GetLeftContour(node, 0, ref nodeContour); float shiftAmount = 0; foreach (var y in nodeContour.Keys) { if (nodeContour[y] + shiftAmount < 0) { shiftAmount = nodeContour[y] * -1; } } if (shiftAmount > 0) { node.X += shiftAmount; node.Mod += shiftAmount; } }
public TreeNodeModel(T item, TreeNodeModel <T> parent) { Item = item; Parent = parent; Children = new List <TreeNodeModel <T> >(); }
private static void DrawNode(TreeNodeModel <NodeModel> node, Graphics graphic) { // Set the pen var nodePen = node.Item.Colour; // rectangle where node will be positioned var nodeRect = new Rectangle(Convert.ToInt32(NodeMarginX + node.X * (NodeWidth + NodeMarginX)), NodeMarginY + node.Y * (NodeHeight + NodeMarginY), NodeWidth, NodeHeight); // draw box if (!node.Item.IsCompressed) { graphic.DrawRectangle(nodePen, nodeRect); } // draw content var font = new Font(FontFamily.GenericMonospace, 8); var stringSize = graphic.MeasureString(node.ToString(), font); graphic.DrawString(node.ToString(), font, nodePen.Brush, nodeRect.X + NodeWidth / 2 - stringSize.Width / 2, nodeRect.Y + NodeHeight / 2 - stringSize.Height / 2); // draw line to parent if (node.Parent != null) { var nodeTopMiddle = new Point(nodeRect.X + nodeRect.Width / 2, nodeRect.Y); var nodeBottomMiddle = new Point(nodeTopMiddle.X, nodeTopMiddle.Y - NodeMarginY / 2); graphic.DrawLine(nodePen, nodeTopMiddle, nodeBottomMiddle); } // draw line to children if (node.Children.Count > 0) { var nodeBottomMiddle = new Point(nodeRect.X + nodeRect.Width / 2, nodeRect.Y + nodeRect.Height); var nodeTopMiddle = new Point(nodeBottomMiddle.X, nodeBottomMiddle.Y + NodeMarginY / 2); graphic.DrawLine(nodePen, nodeBottomMiddle, nodeTopMiddle); // draw line over children if (node.Children.Count > 1) { var childrenLineStartX = Convert.ToInt32(NodeMarginX + node.GetRightMostChild().X * // ReSharper disable once PossibleLossOfFraction (NodeWidth + NodeMarginX) + NodeWidth / 2); var childrenLineStartY = nodeBottomMiddle.Y + NodeMarginY / 2; var childrenLineStart = new Point(childrenLineStartX, childrenLineStartY); var childrenLineEndX = Convert.ToInt32(NodeMarginX + node.GetLeftMostChild().X *(NodeWidth + NodeMarginX) + // ReSharper disable once PossibleLossOfFraction NodeWidth / 2); var childrenLineEndY = nodeBottomMiddle.Y + NodeMarginY / 2; var childrenLineEnd = new Point(childrenLineEndX, childrenLineEndY); graphic.DrawLine(Pens.Black, childrenLineStart, childrenLineEnd); } } foreach (var item in node.Children) { DrawNode(item, graphic); } }
private static void PaintTree(Graphics graphic, TreeNodeModel <NodeModel> tree) { graphic.Clear(Color.White); DrawNode(tree, graphic); }