예제 #1
0
        /// <summary>
        /// Removes the specified node from the tree.
        /// </summary>
        public void RemoveNode(SphereTreeNode <T> node)
        {
            // If the node is not the root node and if it has a parent...
            if (!node.IsRoot && node.Parent != null)
            {
                // Remove the node from its parent
                SphereTreeNode <T> parentNode = node.Parent;
                parentNode.RemoveChild(node);

                // Move up the hierarhcy and remove all parents which don't have any children any more.
                // Note: We will always stop at the root node. The root node is allowed to exist even
                //       if it has no children.
                while (parentNode.Parent != null && parentNode.HasNoChildren && !parentNode.IsRoot)
                {
                    SphereTreeNode <T> newParent = parentNode.Parent;
                    newParent.RemoveChild(parentNode);
                    parentNode = newParent;
                }

                // At this point 'parentNode' references the deepest parent which has at least one child.
                // Because we have removed children from it, its volume must be recalculated, so we add
                // it to the recomputation queue.
                // Note: Even if this function was called from 'PerformPendingUpdates' we still get correct
                //       results because the node will be added to the recomputation queue and it will be
                //       processed inside the recomputation 'while' loop from where this method is called.
                AddNodeToRecomputationQueue(parentNode);
            }
        }
예제 #2
0
        /// <summary>
        /// This is a recursive method which is responsible for integration the specified
        /// node inside the tree.
        /// </summary>
        private void IntegrateTerminalNodeRecurse(SphereTreeNode <T> nodeToIntegrate, SphereTreeNode <T> parentNode)
        {
            // If this node still has room for children, we will add the integration node here. This 'if' statement
            // will also handle the special case in which only the root node currently exists inside the tree.
            if (parentNode.NumberOfChildren < _numberOfChildNodesPerNode)
            {
                // Add the node as a child of the parent node and ensure that the root node encapsulates it
                parentNode.AddChild(nodeToIntegrate);
                parentNode.EncapsulateChildNode(nodeToIntegrate);
            }
            else
            {
                // If there is no more room, we will proceed by choosing one of the parent's children which
                // is closest to the node that we want to integrate. We choose the closest node because when
                // the node will be added as a child of it, we want the parent to grow as little as possible.
                List <SphereTreeNode <T> > children     = parentNode.Children;
                SphereTreeNode <T>         closestChild = FindClosestNode(children, nodeToIntegrate);
                if (closestChild == null)
                {
                    return;
                }

                // If the closest child is not a terminal node, recurse.
                if (!closestChild.IsTerminal)
                {
                    IntegrateTerminalNodeRecurse(nodeToIntegrate, closestChild);
                }
                else
                {
                    // If we reached a terminal node, we create a new node which encapsulates 'closestChild' and 'nodeToIntegrate'
                    // and then we replace 'closestChild' with this new node. The first step is to create the sphere of this
                    // new node. It doesn't matter how big this sphere is initially because we will call 'EncapsulateChildNode'
                    // later.
                    Sphere newNodeSphere = closestChild.Sphere;

                    // Create the node using the sphere we just calculated
                    SphereTreeNode <T> newNode = new SphereTreeNode <T>(newNodeSphere, this, default(T));
                    newNode.SetFlag(SphereTreeNodeFlags.SuperSphere);

                    // Replace 'closestChild' with the new node and add both 'closestChild' and 'nodeToIntegrate' as children of it
                    parentNode.RemoveChild(closestChild);
                    parentNode.AddChild(newNode);
                    newNode.AddChild(nodeToIntegrate);
                    newNode.AddChild(closestChild);

                    // Encapsulate the children inside the new node
                    newNode.EncapsulateChildNode(closestChild);
                    newNode.EncapsulateChildNode(nodeToIntegrate);

                    // Ensure that the new node is fully contained inside the parent node
                    parentNode.EncapsulateChildNode(newNode);
                }
            }
        }
예제 #3
0
        /// <summary>
        /// This method is called when the sphere of a terminal node has been updated.
        /// </summary>
        private void OnTerminalNodeSphereUpdated(SphereTreeNode <T> terminalNode)
        {
            // If the node is already marked for reintegration, there is nothing left for us to do
            if (terminalNode.MustIntegrate)
            {
                return;
            }

            // If the node is now outside of its parent, it may now be closer to another parent and associating
            // it with that new parent may provide better space/volume savings. So we remove the node from its
            // parent and add it to the integration queue so that it can be reintegrated. During the integration
            // process, the algorithm may find a more optimal parent or the same one if a more optimal one doesn't
            // exist.
            // Note: We are only removing the child from its parent if it went outside of its parent volume. It may
            //       probably be a better idea to always check the surrounding parents and see if a more optimal one
            //       exists even if the node doesn't pierce its parent's skin. For the moment however, we will only
            //       remove the child from its parent if it pierced its skin.
            SphereTreeNode <T> parentNode       = terminalNode.Parent;
            float distanceToParentNodeExitPoint = parentNode.GetDistanceToNodeExitPoint(terminalNode);

            if (distanceToParentNodeExitPoint > parentNode.Radius)
            {
                // Note: It may be a good idea to check if the node contains only one child after removal. In that
                //       case the node itself can be removed and its child moved up the hierarchy. However, for the
                //       moment we'll keep things simple.
                // Remove the child from its parent and add it to the integration queue. Adding it to
                // the integration queue is necessary in order to ensure that it gets reassigned to
                // the correct parrent based on its current position.
                parentNode.RemoveChild(terminalNode);
                AddNodeToIntegrationQueue(terminalNode);
            }

            // Whenever a terminal node is updated, it's parent must gave its volume recomputed. We always do this
            // regardless of whether or not the node was removed from the parent or not.
            AddNodeToRecomputationQueue(parentNode);
        }