/// <summary> /// Creates the root node of the tree. /// </summary> private void CreateRootNode() { // Create the root node with some sensible default values. It doesn't really matter what // initial size the root node has. It will grow as needed when new nodes are added to the // tree. _rootNode = new SphereTreeNode <T>(Vector3.zero, 10.0f, this); _rootNode.SetFlag(SphereTreeNodeFlags.Root | SphereTreeNodeFlags.SuperSphere); }
/// <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); } } }
/// <summary> /// Adds a terminal node to the tree. /// </summary> /// <remarks> /// The function does not integrate the node inside the sphere hierarchy. It /// will only add it to the integration pending queue. The actual integration /// process will be performed inside 'PerformPendingUpdates'. /// </remarks> /// <param name="sphere"> /// The node's sphere. /// </param> /// <param name="data"> /// The node's data. /// </param> /// <returns> /// The node which was added to the tree. /// </returns> public SphereTreeNode <T> AddTerminalNode(Sphere sphere, T data) { // Create a new node and mark it as terminal var newTerminalNode = new SphereTreeNode <T>(sphere, this, data); newTerminalNode.SetFlag(SphereTreeNodeFlags.Terminal); // Add the node to the integration queue AddNodeToIntegrationQueue(newTerminalNode); return(newTerminalNode); }
/// <summary> /// Adds the specified node to the integration queue. /// </summary> private void AddNodeToIntegrationQueue(SphereTreeNode <T> node) { // Only terminal, non-root nodes are allowed. We also have to ensure that // the node hasn't already been added to the integration queue. if (node.IsSuperSphere || node.IsRoot || node.MustIntegrate) { return; } if (node.IsTerminal) { node.SetFlag(SphereTreeNodeFlags.MustIntegrate); _terminalNodesPendingIntegration.Enqueue(node); } }
/// <summary> /// Creates the tree nodes from the specified list of serialized nodes. /// </summary> /// <returns> /// The tree root node. /// </returns> private SphereTreeNode <T> CreateTreeNodesFromSerializedNodes <SerializableNodeType>(List <SerializableNodeType> serializedNodes) where SerializableNodeType : SerializableSphereTreeNode <T>, new() { var allNodes = new List <SphereTreeNode <T> >(); foreach (var serializedNode in serializedNodes) { // Create the actual node instance and store it in the node list var node = new SphereTreeNode <T>(new Sphere(serializedNode.SphereCenter, serializedNode.SphereRadius), this, serializedNode.NodeData); node.SetFlag(serializedNode.Flags); allNodes.Add(node); // If the node has a parent, establish the parent-child relationship if (serializedNode.ParentNodeIndex >= 0) { SphereTreeNode <T> parentNode = allNodes[serializedNode.ParentNodeIndex]; parentNode.AddChild(node); } } // Return the root node return(allNodes[0]); }