/// <summary> /// Performs the insertion algorithm. /// </summary> /// <param name="item">Item to insert</param> public void Insert(LeafRecord <T> item) { List <NodeRecord <T> > leafPath = new List <NodeRecord <T> >(); // I1. // Track the nodes traversed to get to the point that we // want to add the node. This list of nodes will be used // to propagate changes up the tree. NodeRecord <T> insertNode = chooseLeaf(item, leafPath); NodeRecord <T> splitNode = null; // I2. // Attempts to insert the item in the given node. // If it fails, we split the node. Store the new // node in splitNode, so it can be propagated up // later. if (!insertNode.TryInsert(item)) { // Split. splitNode = insertNode.Split(item); } // I3. // Propagate resizing up the tree. Propagate split node // if necessary. if (adjustTree(leafPath, insertNode, ref splitNode)) { // I4. // Create a new root if the root was split. This new root is not a leaf. NodeRecord <T> newRoot = new NodeRecord <T>() { Node = new RTreeNode <T>(Leaf: false) }; newRoot.TryInsert(root); newRoot.TryInsert(splitNode); root = newRoot; } }
/// <summary> /// Propagates resizing of the nodes upwards. Additionally, performs /// any splitting of nodes along the way if a node needed to split. /// Returns whether the root node was split. /// </summary> /// <param name="adjustPath">Path along which the nodes need be adjusted</param> /// <param name="node">Node above which needs to be adjusted</param> /// <param name="splitNode">Node that needs to be inserted.</param> /// <returns>True if the root node was split</returns> private bool adjustTree(List <NodeRecord <T> > adjustPath, NodeRecord <T> node, ref NodeRecord <T> splitNode) { // AT1. NodeRecord <T> N = node; NodeRecord <T> NN = splitNode; while (true) { // AT2. if (N == root) { return(NN != null); } // AT3. N.ResizeBBox(); // AT4. // Try to add the extra node to this parent node. // If the parent has no more room, split the parent, // and propagate the split node upwards to continue // looking for an insertion point. int level = adjustPath.IndexOf(N); NodeRecord <T> P = adjustPath[level - 1]; if (NN != null) { if (!P.TryInsert(NN)) { NodeRecord <T> PP = P.Split(NN); NN = PP; } else { NN = null; } } N = P; } }
/// <summary> /// Utilizes the Quadradic split algorithm to determine where each /// of the records go - either to group one or group two. /// </summary> /// <param name="newItem"></param> /// <returns></returns> public NodeRecord <T> SplitQuadradic(IndexRecord <T> newItem) { List <IndexRecord <T> > itemsToBeAssigned = records; itemsToBeAssigned.Add(newItem); IndexRecord <T> A, B; // QS1. pickSeeds(itemsToBeAssigned, out A, out B); itemsToBeAssigned.Remove(A); itemsToBeAssigned.Remove(B); NodeRecord <T> groupOne = new NodeRecord <T>() { BBox = A.BBox, Node = new RTreeNode <T>(isLeaf) }; NodeRecord <T> groupTwo = new NodeRecord <T>() { BBox = B.BBox, Node = new RTreeNode <T>(isLeaf) }; groupOne.Node.TryInsert(A); groupTwo.Node.TryInsert(B); // QS2. while (itemsToBeAssigned.Count > 0) { // QS3. int d1, d2; var next = pickNext(itemsToBeAssigned, groupOne, groupTwo, out d1, out d2); if (d1 < d2) { groupOne.TryInsert(next); } else if (d2 < d1) { groupTwo.TryInsert(next); } else { // Insert to whichever is smaller. if (groupOne.Node.GetRecordCount() < groupTwo.Node.GetRecordCount()) { groupOne.TryInsert(next); } else { groupTwo.TryInsert(next); } } itemsToBeAssigned.Remove(next); // QS2. if (groupOne.Node.GetRecordCount() + itemsToBeAssigned.Count <= m) { while (itemsToBeAssigned.Count > 0) { groupOne.TryInsert(itemsToBeAssigned.First()); itemsToBeAssigned.Remove(itemsToBeAssigned.First()); } } // QS2. if (groupTwo.Node.GetRecordCount() + itemsToBeAssigned.Count <= m) { while (itemsToBeAssigned.Count > 0) { groupTwo.TryInsert(itemsToBeAssigned.First()); itemsToBeAssigned.Remove(itemsToBeAssigned.First()); } } } // Set this equal to group two. records = groupTwo.Node.records; return(groupOne); }