示例#1
0
        // Depth first search
        // Check if each connected Node is already in the tree. For any that aren't, they are this treeNode's children. Recurse on.
        // If they are already in the tree and the node in question isn't a parent, then add it to UnusedConnections.

        // Can call twice to update with new connections to previously-unconnected components
        private void Build(SpanningTreeNode treeNode, SpanningTreeNode parentTreeNode)
        {
            foreach (var connection in treeNode.Node.ConnectedNodes)
            {
                if (treeNode.UnusedConnections.Contains(connection.name))
                {
                    // already noted on a previous Build call
                }
                else if (Contains(connection.name))
                {
                    // Connection in question is stored elsewhere in our tree. If it's not the parent, note this.
                    if (parentTreeNode != null && parentTreeNode.Node != connection)
                    {
                        treeNode.UnusedConnections.Add(connection.name);
                    }
                }
                else
                {
                    var child = new SpanningTreeNode(connection);
                    _unconnectedNodes.Remove(connection);
                    treeNode.Children.Add(child);
                    Build(child, treeNode);
                }
            }
        }
示例#2
0
        // The raison d'etre of this class (mostly). Add edges if an edge to a child is identified as a bridge, preferring using grandchildren to this node's parent
        private void FixBridges(SpanningTreeNode treeNode, SpanningTreeNode parent)
        {
            foreach (var child in treeNode.Children)
            {
                if (child.HighestWithJump <= child.Postorder && child.LowestWithJump > child.Postorder - child.NumDescendants)
                {
                    // Bring out the bridge brigade, yo
                    if (parent != null)
                    {
                        parent.Node.ConnectedNodes.Add(child.Node);
                        child.Node.ConnectedNodes.Add(parent.Node);
                        _log.Write("Fixed bridge between {0} and {1} by connecting nodes {2} and {3}", treeNode.Node.name, child.Node.name, parent.Node.name, child.Node.name);
                    }
                    else if (child.Children.Count > 0)
                    {
                        treeNode.Node.ConnectedNodes.Add(child.Children[0].Node);
                        child.Children[0].Node.ConnectedNodes.Add(treeNode.Node);
                        _log.Write("Fixed bridge between {0} and {1} by connecting nodes {2} and {3}", treeNode.Node.name, child.Node.name, treeNode.Node.name, child.Children[0].Node.name);
                    }
                    else  // panic and grab a random node
                    {
                        var panicNode = _originalNodes.Where(n => n != child.Node && n != treeNode.Node).First();

                        panicNode.ConnectedNodes.Add(child.Node);
                        child.Node.ConnectedNodes.Add(panicNode);
                        _log.Write("Fixed bridge between {0} and {1} by connecting nodes {2} and {3}", treeNode.Node.name, child.Node.name, panicNode.name, child.Node.name);
                    }
                }

                FixBridges(child, treeNode);
            }
        }
示例#3
0
 private void PrintNodeDetail(SpanningTreeNode treeNode)
 {
     _log.Write("Node {0} Postorder {1} numDesc {2} low {3} high {4}",
                treeNode.Node.name, treeNode.Postorder, treeNode.NumDescendants, treeNode.LowestWithJump, treeNode.HighestWithJump);
     foreach (var child in treeNode.Children)
     {
         PrintNodeDetail(child);
     }
 }
示例#4
0
 private void MarkPostorder(SpanningTreeNode treeNode)
 {
     foreach (var child in treeNode.Children)
     {
         MarkPostorder(child);
     }
     treeNode.Postorder = _counter;
     _counter++;
 }
示例#5
0
        private void MarkMaxLowJump(SpanningTreeNode treeNode, int max)
        {
            treeNode.LowestWithJump = max;

            foreach (var child in treeNode.Children)
            {
                MarkMaxLowJump(child, max);
            }
        }
示例#6
0
 private void PrintConnections(SpanningTreeNode treeNode)
 {
     _log.Write("Node {0} w/ graph connections {1}",
                treeNode.Node.name,
                treeNode.Node.ConnectedNodes.Count > 0 ? treeNode.Node.ConnectedNodes.Select(c => c.name).Aggregate((x, y) => x + ", " + y) : "NONE");
     foreach (var child in treeNode.Children)
     {
         PrintConnections(child);
     }
 }
示例#7
0
 private void Print(SpanningTreeNode treeNode)
 {
     _log.Write("Node {0} w/ children {1}, unused connections to {2}",
                treeNode.Node.name,
                treeNode.Children.Count > 0 ? treeNode.Children.Select(c => c.Node.name).Aggregate((x, y) => x + ", " + y) : "NONE",
                treeNode.UnusedConnections.Count > 0 ? treeNode.UnusedConnections.Aggregate((x, y) => x + ", " + y) : "NONE");
     foreach (var child in treeNode.Children)
     {
         Print(child);
     }
 }
示例#8
0
        private int CountDescendants(SpanningTreeNode treeNode)
        {
            int sum = 0;

            foreach (var child in treeNode.Children)
            {
                sum += CountDescendants(child);
            }

            treeNode.NumDescendants = sum + 1;  // Includes self
            return(treeNode.NumDescendants);
        }
示例#9
0
        // Creates a spanning tree from graph of nodes. Will add in edges if needed.
        public SpanningTree(List <Node> nodes)
        {
            _originalNodes = nodes;

            _unconnectedNodes = new List <Node>();     // track nodes not connected to the tree, so that we can add unconnected components later
            foreach (var node in nodes)
            {
                _unconnectedNodes.Add(node);
            }

            _root = new SpanningTreeNode(_originalNodes[0]);
            _unconnectedNodes.Remove(_originalNodes[0]);
            Build(_root, null);

            _log.Write("");
            // We might have two (or more) unconnected components in this graph. If that's the case, then build a bridge.
            // Another edge will be added to fix this new bridge later.
            // Right now, there's no order to the unconnected nodes. Later on we could order by distance that to ensure the smallest edges are added.
            while (_unconnectedNodes.Count > 0)
            {
                var node          = _unconnectedNodes[0];
                var closeTreeNode = ClosestTo(node, _root);
                closeTreeNode.Node.ConnectedNodes.Add(node);        // Possible improvement: avoid bad overlapping angles. See seed 208382959 on WorkTutorial
                _log.Write("Adding edge between {0} and {1} to connect components", closeTreeNode.Node.name, node.name);
                Build(closeTreeNode, null);
            }

            _log.Write("Spanning tree");
            Print(_root);

            // Tarjan magic

            _counter = 1;
            MarkPostorder(_root);
            CountDescendants(_root);

            MarkMaxLowJump();
            CountLowJump(_root, false);
            CountHighJump(_root, false);

            _log.Write("Tree node details");
            PrintNodeDetail(_root);

            FixBridges(_root, null);
            _log.Write("Graph connections, w/ new connections and no bridges");
            PrintConnections(_root);
            _log.Write("");
        }
示例#10
0
        private SpanningTreeNode ClosestTo(Node node, SpanningTreeNode treeNode)
        {
            SpanningTreeNode closest  = treeNode;
            float            distance = Vector3.Distance(treeNode.Node.transform.position, node.transform.position);

            foreach (var child in treeNode.Children)
            {
                var   closestChild  = ClosestTo(node, child);
                float childDistance = Vector3.Distance(closestChild.Node.transform.position, node.transform.position);
                if (childDistance < distance)
                {
                    closest  = closestChild;
                    distance = childDistance;
                }
            }
            return(closest);
        }
示例#11
0
        private bool Contains(string name, SpanningTreeNode treeNode)
        {
            if (treeNode.Node.name == name)
            {
                return(true);
            }

            bool childContains = false;

            foreach (var child in treeNode.Children)
            {
                if (Contains(name, child))
                {
                    childContains = true;
                }
            }
            return(childContains);
        }
示例#12
0
        private SpanningTreeNode Find(string name, SpanningTreeNode treeNode)
        {
            if (treeNode.Node.name == name)
            {
                return(treeNode);
            }

            foreach (var child in treeNode.Children)
            {
                var childResult = Find(name, child);
                if (childResult != null)
                {
                    return(childResult);
                }
            }

            return(null);
        }
示例#13
0
        // analog to CountLowJump
        private int CountHighJump(SpanningTreeNode treeNode, bool hasJumped)
        {
            int highestMark = 0;

            if (!hasJumped)
            {
                foreach (var child in treeNode.Children)
                {
                    int childHigh = CountHighJump(child, hasJumped);
                    if (childHigh > highestMark)
                    {
                        highestMark = childHigh;
                    }
                }
                foreach (var connection in treeNode.Node.ConnectedNodes)
                {
                    if (treeNode.UnusedConnections.Contains(connection.name))
                    {
                        var jumpNode = Find(connection.name);
                        int jumpHigh = CountHighJump(jumpNode, true);
                        if (jumpHigh > highestMark)
                        {
                            highestMark = jumpHigh;
                        }
                    }
                }
            }
            if (treeNode.Postorder > highestMark)
            {
                highestMark = treeNode.Postorder;
            }
            if (highestMark > treeNode.HighestWithJump)
            {
                treeNode.HighestWithJump = highestMark;
            }
            return(highestMark);
        }
示例#14
0
        // Calculates the lowest NumDescendants value of the node, any children, and any connected nodes not connected in the spanning tree.
        // HasJumped prevents cyclical checks by tracking the one non-tree edge jump.
        private int CountLowJump(SpanningTreeNode treeNode, bool hasJumped)
        {
            int lowestMark = _root.NumDescendants + 1;    // 1 more than max value here

            if (!hasJumped)
            {
                foreach (var child in treeNode.Children)
                {
                    int childLow = CountLowJump(child, hasJumped);
                    if (childLow < lowestMark)
                    {
                        lowestMark = childLow;
                    }
                }
                foreach (var connection in treeNode.Node.ConnectedNodes)
                {
                    if (treeNode.UnusedConnections.Contains(connection.name))
                    {
                        var jumpNode = Find(connection.name);
                        int jumpLow  = CountLowJump(jumpNode, true);
                        if (jumpLow < lowestMark)
                        {
                            lowestMark = jumpLow;
                        }
                    }
                }
            }
            if (treeNode.Postorder < lowestMark)
            {
                lowestMark = treeNode.Postorder;
            }
            if (lowestMark < treeNode.LowestWithJump)
            {
                treeNode.LowestWithJump = lowestMark;
            }
            return(lowestMark);
        }