/// <summary> /// Given a node object, calculate the node Minimum Bounding Rectangle from it's entries. Used in consistency checking /// </summary> /// <returns></returns> internal Rectangle calculateMinimumBoundingRectangle() { minimumBoundingRectangle = new Rectangle(true); Update(); for (int i = 0; i < entryCount; i++) { if (entries[i].Value.MinX < minimumBoundingRectangle.MinX) { minimumBoundingRectangle.MinX = entries[i].Value.MinX; } if (entries[i].Value.MinY < minimumBoundingRectangle.MinY) { minimumBoundingRectangle.MinY = entries[i].Value.MinY; } if (entries[i].Value.MaxX > minimumBoundingRectangle.MaxX) { minimumBoundingRectangle.MaxX = entries[i].Value.MaxX; } if (entries[i].Value.MaxY > minimumBoundingRectangle.MaxY) { minimumBoundingRectangle.MaxY = entries[i].Value.MaxY; } } return minimumBoundingRectangle; }
/// <summary> /// Start at the Root Node /// Select the child that needs the least enlargement in order to fit the new geometry. /// Repeat until at a leaf node. /// If leaf node has available space insert Else split the entry into two nodes /// Update parent nodes /// Update the entry that pointed to the node with a new minimum bounding rectangle /// Add a new entry for the second new node /// If there is no space in the parent node, split and repeat /// </summary> /// <param name="r">the rectangle being added</param> public void Add(Rectangle r) { AddInternal(r); size++; #if RtreeCheck checkConsistency(); #endif }
internal void addEntry(ref Rectangle r) { Update(); entries[entryCount++] = r; if (r.MinX < minimumBoundingRectangle.MinX) minimumBoundingRectangle.MinX = r.MinX; if (r.MinY < minimumBoundingRectangle.MinY) minimumBoundingRectangle.MinY = r.MinY; if (r.MaxX > minimumBoundingRectangle.MaxX) minimumBoundingRectangle.MaxX = r.MaxX; if (r.MaxY > minimumBoundingRectangle.MaxY) minimumBoundingRectangle.MaxY = r.MaxY; }
internal Rectangle calculateMBR() { Rectangle mbr = new Rectangle(true); for (int i = 0; i < entryCount; i++) { if (entries[i].Value.MinX < mbr.MinX) mbr.MinX = entries[i].Value.MinX; if (entries[i].Value.MinY < mbr.MinY) mbr.MinY = entries[i].Value.MinY; if (entries[i].Value.MaxX > mbr.MaxX) mbr.MaxX = entries[i].Value.MaxX; if (entries[i].Value.MaxY > mbr.MaxY) mbr.MaxY = entries[i].Value.MaxY; } return mbr; }
/// <summary> /// Computes the union of this rectangle and the passed rectangle, storing the result in this rectangle. /// </summary> /// <param name="r">Rectangle to add to this rectangle</param> public void Add(Rectangle r) { if (r.minX < minX) minX = r.minX; if (r.maxX > maxX) maxX = r.maxX; if (r.minY < minY) minY = r.minY; if (r.maxY > maxY) maxY = r.maxY; }
/// <summary> /// Calculate the area by which this rectangle would be enlarged if added to the passed rectangle. Neither rectangle is altered. /// </summary> /// <param name="r">Rectangle to union with this rectangle, in order to compute the difference in area of the union and the original rectangle</param> /// <returns>enlargement</returns> public double Enlargement(ref Rectangle r) { double enlargedArea = (Math.Max(maxX, r.maxX) - Math.Min(minX, r.minX)) * (Math.Max(maxY, r.maxY) - Math.Min(minY, r.minY)); return enlargedArea - Area; }
/// <summary> /// Determine whether this rectangle is contained by the passed rectangle /// </summary> /// <param name="r">The rectangle that might contain this rectangle</param> /// <returns>return true if the passed rectangle contains this rectangle, false if it does not</returns> public bool containedBy(Rectangle r) { return r.maxX >= maxX && r.minX <= minX && r.maxY >= maxY && r.minY <= minY; }
/// <summary> /// Determine whether this rectangle intersects the passed rectangle /// </summary> /// <param name="r">The rectangle that might intersect this rectangle</param> /// <returns>return true if the rectangles intersect, false if they do not intersect</returns> public bool Intersects(ref Rectangle? r) { return maxX >= r.Value.minX && minX <= r.Value.maxX && maxY >= r.Value.minY && minY <= r.Value.maxY; }
/// <summary> /// Determine whether an edge of this rectangle overlies the equivalent edge of the passed rectangle /// </summary> /// <param name="r"></param> /// <returns></returns> public bool edgeOverlaps(Rectangle r) { return minX == r.minX || maxX == r.maxX || minY == r.minY || maxY == r.maxY; }
internal NodeLeaf splitNode(RTree rTree, Rectangle r) { // [Pick first entry for each group] Apply algorithm pickSeeds to // choose two entries to be the first elements of the groups. Assign // each to a group. // debug code /*double initialArea = 0; if (log.isDebugEnabled()) { double unionMinX = Math.Min(n.mbrMinX, newRectMinX); double unionMinY = Math.Min(n.mbrMinY, newRectMinY); double unionMaxX = Math.Max(n.mbrMaxX, newRectMaxX); double unionMaxY = Math.Max(n.mbrMaxY, newRectMaxY); initialArea = (unionMaxX - unionMinX) * (unionMaxY - unionMinY); }*/ System.Array.Copy(rTree.initialEntryStatus, 0, rTree.entryStatus, 0, rTree.maxNodeEntries); Update(); NodeLeaf newNode = null; newNode = new NodeLeaf(level, rTree.maxNodeEntries); pickSeeds(rTree, ref r, newNode); // this also sets the entryCount to 1 // [Check if done] If all entries have been assigned, stop. If one group has so few entries that all the rest must be assigned to it in // order for it to have the minimum number m, assign them and stop. while (entryCount + newNode.entryCount < rTree.maxNodeEntries + 1) { if (rTree.maxNodeEntries + 1 - newNode.entryCount == rTree.minNodeEntries) { // assign all remaining entries to original node for (int i = 0; i < rTree.maxNodeEntries; i++) { if (rTree.entryStatus[i] == ((byte)RTree.EntryStatus.unassigned)) { rTree.entryStatus[i] = ((byte)RTree.EntryStatus.assigned); if (entries[i].Value.MinX < minimumBoundingRectangle.MinX) minimumBoundingRectangle.MinX = entries[i].Value.MinX; if (entries[i].Value.MinY < minimumBoundingRectangle.MinY) minimumBoundingRectangle.MinY = entries[i].Value.MinY; if (entries[i].Value.MaxX > minimumBoundingRectangle.MaxX) minimumBoundingRectangle.MaxX = entries[i].Value.MaxX; if (entries[i].Value.MaxY > minimumBoundingRectangle.MaxY) minimumBoundingRectangle.MaxY = entries[i].Value.MaxY; entryCount++; } } break; } if (rTree.maxNodeEntries + 1 - entryCount == rTree.minNodeEntries) { // assign all remaining entries to new node for (int i = 0; i < rTree.maxNodeEntries; i++) { if (rTree.entryStatus[i] == ((byte)RTree.EntryStatus.unassigned)) { rTree.entryStatus[i] = ((byte)RTree.EntryStatus.assigned); Rectangle entryR = entries[i].Value; newNode.addEntry(ref entryR); entries[i] = null; } } break; } // [Select entry to assign] Invoke algorithm pickNext to choose the next entry to assign. Add it to the group whose covering rectangle // will have to be enlarged least to accommodate it. Resolve ties by adding the entry to the group with smaller area, then to the // the one with fewer entries, then to either. Repeat from S2 pickNext(rTree, newNode); } reorganize(rTree); // check that the MBR stored for each node is correct. #if RtreeCheck if (!this.minimumBoundingRectangle.Equals(calculateMBR())) { throw new UnexpectedException("Error: splitNode old node MBR wrong"); } if (!newNode.minimumBoundingRectangle.Equals(newNode.calculateMBR())) { throw new UnexpectedException("Error: splitNode new node MBR wrong"); } #endif #if RtreeCheck double newArea = minimumBoundingRectangle.Area + newNode.minimumBoundingRectangle.Area; double percentageIncrease = (100 * (newArea - initialArea)) / initialArea; Console.WriteLine("Node " + this + " split. New area increased by " + percentageIncrease + "%"); #endif return newNode; }
// Return the index of the found entry, or -1 if not found internal virtual int findEntry(ref Rectangle r) { for (int i = 0; i < entryCount; i++) { if (entries[i].Value.Equals(r)) return i; } return -1; }
private void pickSeeds(RTree rTree, ref Rectangle r, NodeLeaf newNode) { // Find extreme rectangles along all dimension. Along each dimension, find the entry whose rectangle has the highest low side, and the one // with the lowest high side. Record the separation. double maxNormalizedSeparation = -1; // initialize to -1 so that even overlapping rectangles will be considered for the seeds int highestLowIndex = -1; int lowestHighIndex = -1; Update(); // for the purposes of picking seeds, take the MBR of the node to include the new rectangle aswell. if (r.MinX < minimumBoundingRectangle.MinX) minimumBoundingRectangle.MinX = r.MinX; if (r.MinY < minimumBoundingRectangle.MinY) minimumBoundingRectangle.MinY = r.MinY; if (r.MaxX > minimumBoundingRectangle.MaxX) minimumBoundingRectangle.MaxX = r.MaxX; if (r.MaxY > minimumBoundingRectangle.MaxY) minimumBoundingRectangle.MaxY = r.MaxY; double mbrLenX = minimumBoundingRectangle.MaxX - minimumBoundingRectangle.MinX; double mbrLenY = minimumBoundingRectangle.MaxY - minimumBoundingRectangle.MinY; #if RtreeCheck Console.WriteLine("pickSeeds(): Node = " + this); #endif double tempHighestLow = r.MinX; int tempHighestLowIndex = -1; // -1 indicates the new rectangle is the seed double tempLowestHigh = r.MaxX; int tempLowestHighIndex = -1; // -1 indicates the new rectangle is the seed for (int i = 0; i < entryCount; i++) { double tempLow = entries[i].Value.MinX; if (tempLow >= tempHighestLow) { tempHighestLow = tempLow; tempHighestLowIndex = i; } // ensure that the same index cannot be both lowestHigh and highestLow else { double tempHigh = entries[i].Value.MaxX; if (tempHigh <= tempLowestHigh) { tempLowestHigh = tempHigh; tempLowestHighIndex = i; } } // PS2 [Adjust for shape of the rectangle cluster] Normalize the separations by dividing by the widths of the entire set along the corresponding dimension double normalizedSeparation = mbrLenX == 0 ? 1 : (tempHighestLow - tempLowestHigh) / mbrLenX; if (normalizedSeparation > 1 || normalizedSeparation < -1) { Console.WriteLine("Invalid normalized separation X"); } #if RtreeCheck Console.WriteLine("Entry " + i + ", dimension X: HighestLow = " + tempHighestLow + " (index " + tempHighestLowIndex + ")" + ", LowestHigh = " + tempLowestHigh + " (index " + tempLowestHighIndex + ", NormalizedSeparation = " + normalizedSeparation); #endif // PS3 [Select the most extreme pair] Choose the pair with the greatest normalized separation along any dimension. // Note that if negative it means the rectangles overlapped. However still include overlapping rectangles if that is the only choice available. if (normalizedSeparation >= maxNormalizedSeparation) { highestLowIndex = tempHighestLowIndex; lowestHighIndex = tempLowestHighIndex; maxNormalizedSeparation = normalizedSeparation; } } // Repeat for the Y dimension tempHighestLow = r.MinY; tempHighestLowIndex = -1; // -1 indicates the new rectangle is the seed tempLowestHigh = r.MaxY; tempLowestHighIndex = -1; // -1 indicates the new rectangle is the seed for (int i = 0; i < entryCount; i++) { double tempLow = entries[i].Value.MinY; if (tempLow >= tempHighestLow) { tempHighestLow = tempLow; tempHighestLowIndex = i; } // ensure that the same index cannot be both lowestHigh and highestLow else { double tempHigh = entries[i].Value.MaxY; if (tempHigh <= tempLowestHigh) { tempLowestHigh = tempHigh; tempLowestHighIndex = i; } } // PS2 [Adjust for shape of the rectangle cluster] Normalize the separations by dividing by the widths of the entire set along the corresponding dimension double normalizedSeparation = mbrLenY == 0 ? 1 : (tempHighestLow - tempLowestHigh) / mbrLenY; if (normalizedSeparation > 1 || normalizedSeparation < -1) { throw new UnexpectedException("Invalid normalized separation Y"); } #if RtreeCheck Console.WriteLine("Entry " + i + ", dimension Y: HighestLow = " + tempHighestLow + " (index " + tempHighestLowIndex + ")" + ", LowestHigh = " + tempLowestHigh + " (index " + tempLowestHighIndex + ", NormalizedSeparation = " + normalizedSeparation); #endif // PS3 [Select the most extreme pair] Choose the pair with the greatest normalized separation along any dimension. // Note that if negative it means the rectangles overlapped. However still include overlapping rectangles if that is the only choice available. if (normalizedSeparation >= maxNormalizedSeparation) { highestLowIndex = tempHighestLowIndex; lowestHighIndex = tempLowestHighIndex; maxNormalizedSeparation = normalizedSeparation; } } // At this point it is possible that the new rectangle is both highestLow and lowestHigh. This can happen if all rectangles in the node overlap the new rectangle. // Resolve this by declaring that the highestLowIndex is the lowest Y and, the lowestHighIndex is the largest X (but always a different rectangle) if (highestLowIndex == lowestHighIndex) { highestLowIndex = -1; double tempMinY = r.MinY; lowestHighIndex = 0; double tempMaxX = entries[0].Value.MaxX; for (int i = 1; i < entryCount; i++) { if (entries[i].Value.MinY < tempMinY) { tempMinY = entries[i].Value.MinY; highestLowIndex = i; } else if (entries[i].Value.MaxX > tempMaxX) { tempMaxX = entries[i].Value.MaxX; lowestHighIndex = i; } } } // highestLowIndex is the seed for the new node. if (highestLowIndex == -1) newNode.addEntry(ref r); else { Rectangle entryRectangle = entries[highestLowIndex].Value; newNode.addEntry(ref entryRectangle); entries[highestLowIndex] = r; // move the new rectangle into the space vacated by the seed for the new node } // lowestHighIndex is the seed for the original node. if (lowestHighIndex == -1) lowestHighIndex = highestLowIndex; rTree.entryStatus[lowestHighIndex] = ((byte)RTree.EntryStatus.assigned); entryCount = 1; minimumBoundingRectangle = entries[lowestHighIndex].Value; }
internal virtual void recalculateMBR() { Update(); minimumBoundingRectangle = entries[0].Value; for (int i = 1; i < entryCount; i++) { if (entries[i].Value.MinX < minimumBoundingRectangle.MinX) minimumBoundingRectangle.MinX = entries[i].Value.MinX; if (entries[i].Value.MinY < minimumBoundingRectangle.MinY) minimumBoundingRectangle.MinY = entries[i].Value.MinY; if (entries[i].Value.MaxX > minimumBoundingRectangle.MaxX) minimumBoundingRectangle.MaxX = entries[i].Value.MaxX; if (entries[i].Value.MaxY > minimumBoundingRectangle.MaxY) minimumBoundingRectangle.MaxY = entries[i].Value.MaxY; } }
/// <summary> /// Adds a new entry at a specified level in the tree /// </summary> /// <param name="r">the rectangle added</param> internal void AddInternal(Rectangle r) { // I1 [Find position for new record] Invoke ChooseLeaf to select a leaf node L in which to place r NodeLeaf n = (NodeLeaf) chooseNode(r, 1); NodeLeaf newLeaf = null; // I2 [Add record to leaf node] If L has room for another entry, install E. Otherwise invoke SplitNode to obtain L and LL containing E and all the old entries of L if (n.entryCount < maxNodeEntries) n.addEntry(ref r); else newLeaf = n.splitNode(this, r); // I3 [Propagate changes upwards] Invoke AdjustTree on L, also passing LL if a split was performed NodeBase newNode = n.adjustTree(this, newLeaf); // I4 [Grow tree taller] If node split propagation caused the root to split, create a new root whose children are the two resulting nodes. if (newNode != null) { NodeBase oldRoot = rootNode; NodeInternal root = new NodeInternal(++treeHeight, maxNodeEntries); rootNode = root; root.addEntry(ref newNode.minimumBoundingRectangle, newNode); root.addEntry(ref oldRoot.minimumBoundingRectangle, oldRoot); } }
/// <summary> /// Removes a rectangle from the Rtree /// </summary> /// <param name="r">the rectangle to delete</param> /// <returns>true if rectangle deleted otherwise false</returns> public bool Remove(Rectangle r) { // FindLeaf algorithm inlined here. Note the "official" algorithm searches all overlapping entries. This seems inefficient, // as an entry is only worth searching if it contains (NOT overlaps) the rectangle we are searching for. // FL1 [Search subtrees] If root is not a leaf, check each entry to determine if it contains r. For each entry found, invoke // findLeaf on the node pointed to by the entry, until r is found or all entries have been checked. parents.Clear(); parents.Push(rootNode); parentsEntry.Clear(); parentsEntry.Push(-1); NodeBase n = null; int foundIndex = -1; // index of entry to be deleted in leaf while (foundIndex == -1 && parents.Count > 0) { n = parents.Peek(); int startIndex = parentsEntry.Peek() + 1; if (!n.IsLeaf) { NodeInternal internalNode = n as NodeInternal; bool Contains = false; for (int i = startIndex; i < n.entryCount; i++) { if (n.entries[i].Value.Contains(r)) { parents.Push(internalNode.childNodes[i]); parentsEntry.Pop(); parentsEntry.Push(i); // this becomes the start index when the child has been searched parentsEntry.Push(-1); Contains = true; break; // ie go to next iteration of while() } } if (Contains) continue; } else { NodeLeaf leaf = n as NodeLeaf; foundIndex = leaf.findEntry(ref r); } parents.Pop(); parentsEntry.Pop(); } // while not found if (foundIndex != -1) { NodeLeaf leaf = n as NodeLeaf; leaf.deleteEntry(foundIndex); leaf.condenseTree(this); size--; } // shrink the tree if possible (i.e. if root node has exactly one entry, and that entry is not a leaf node, delete the root (it's entry becomes the new root) NodeBase root = rootNode; while (root.entryCount == 1 && treeHeight > 1) { NodeInternal rootInternal = root as NodeInternal; root.entryCount = 0; rootNode = rootInternal.childNodes[0]; treeHeight--; } // if the tree is now empty, then set the MBR of the root node back to it's original state (this is only needed when the tree is empty, // as this is the only state where an empty node is not eliminated) if (size == 0) rootNode.minimumBoundingRectangle = new Rectangle(true); #if RtreeCheck checkConsistency(); #endif return (foundIndex != -1); }
/// <summary> /// Sets the size of this rectangle to equal the passed rectangle. /// </summary> /// <param name="r"></param> public void set(Rectangle r) { minX = r.minX; minY = r.minY; maxX = r.maxX; maxY = r.maxY; }
/// <summary> /// Finds all rectangles that intersect the passed rectangle. /// </summary> /// <param name="r">the rectangle we are intersecting with</param> /// <param name="v">if returns true, search containues else search is ended</param> /// <returns>true if at least one intersection was found and v returns true</returns> public bool Intersects(Rectangle r, Func<Rectangle, bool> v) { return intersects(r, v, rootNode); }
public bool Equals(Rectangle r) { return minX == r.minX && minY == r.minY && maxX == r.maxX && maxY == r.maxY; }
/// <summary> /// Finds all rectangles contained by the passed rectangle /// </summary> /// <param name="r">The rectangle for which this method finds contained rectangles.</param> /// <param name="v">if return true, continue seach</param> public void Contains(Rectangle r, Func<Rectangle, bool> v) { // find all rectangles in the tree that are contained by the passed rectangle written to be non-recursive (should model other searches on this?) parents.Clear(); parents.Push(rootNode); parentsEntry.Clear(); parentsEntry.Push(-1); // TODO: possible shortcut here - could test for intersection with the MBR of the root node. If no intersection, return immediately. while (parents.Count > 0) { NodeBase n = parents.Peek(); int startIndex = parentsEntry.Peek() + 1; if (!n.IsLeaf) { NodeInternal nodeInternal = n as NodeInternal; // go through every entry in the index node to check if it intersects the passed rectangle. If so, it could contain entries that are contained. bool intersects = false; for (int i = startIndex; i < n.entryCount; i++) { if (r.Intersects(ref n.entries[i])) { parents.Push(nodeInternal.childNodes[i]); parentsEntry.Pop(); parentsEntry.Push(i); // this becomes the start index when the child has been searched parentsEntry.Push(-1); intersects = true; break; // ie go to next iteration of while() } } if (intersects) { continue; } } else { // go through every entry in the leaf to check if it is contained by the passed rectangle for (int i = 0; i < n.entryCount; i++) { if (r.Contains(n.entries[i].Value)) { if (!v(n.entries[i].Value)) { return; } } } } parents.Pop(); parentsEntry.Pop(); } }
/// <summary> /// Determine whether this rectangle contains the passed rectangle /// </summary> /// <param name="r">The rectangle that might be contained by this rectangle</param> /// <returns>return true if this rectangle contains the passed rectangle, false if it does not</returns> public bool Contains(Rectangle r) { return maxX >= r.maxX && minX <= r.minX && maxY >= r.maxY && minY <= r.minY; }
/// <summary> /// Recursively searches the tree for all intersecting entries. /// Calls the passed function when a matching entry is found. Return if the passed function returns false; /// </summary> /// <param name="r"></param> /// <param name="v"></param> /// <param name="n"></param> /// <returns></returns> // TODO rewrite this to be non-recursive. private bool intersects(Rectangle r, Func<Rectangle, bool> v, NodeBase n) { for (int i = 0; i < n.entryCount; i++) { if (r.Intersects(ref n.entries[i])) { if (n.IsLeaf) { if (!v(n.entries[i].Value)) { return false; } } else { NodeInternal nodeInternal = n as NodeInternal; NodeInternal childNode = nodeInternal.childNodes[i] as NodeInternal; if (!intersects(r, v, childNode)) { return false; } } } } return true; }
/// <summary> /// Return the distance between this rectangle and the passed rectangle. If the rectangles overlap, the distance is zero. /// </summary> /// <param name="r">Rectangle to find the distance to</param> /// <returns>return distance between this rectangle and the passed rectangle</returns> public double Distance(Rectangle r) { double distanceSquared = 0; double greatestMin = Math.Max(minX, r.minX); double leastMax = Math.Min(maxX, r.maxX); if (greatestMin > leastMax) { distanceSquared += ((greatestMin - leastMax) * (greatestMin - leastMax)); } greatestMin = Math.Max(minY, r.minY); leastMax = Math.Min(maxY, r.maxY); if (greatestMin > leastMax) { distanceSquared += ((greatestMin - leastMax) * (greatestMin - leastMax)); } return (double)Math.Sqrt(distanceSquared); }
/// <summary> /// Used by add(). Chooses a leaf to add the rectangle to. /// </summary> /// <param name="r"></param> /// <param name="level"></param> /// <returns></returns> private NodeBase chooseNode(Rectangle r, int level) { // CL1 [Initialize] Set N to be the root node NodeBase n = rootNode; parents.Clear(); parentsEntry.Clear(); // CL2 [Leaf check] If N is a leaf, return N while (true) { if (n == null) throw new UnexpectedException("Could not get root node (" + rootNode + ")"); if (n.level == level) return n; NodeInternal nodeInternal = n as NodeInternal; // CL3 [Choose subtree] If N is not at the desired level, let F be the entry in N whose rectangle FI needs least enlargement to include EI. Resolve // ties by choosing the entry with the rectangle of smaller area. double leastEnlargement = n.entries[0].Value.Enlargement(ref r); int index = 0; // index of rectangle in subtree for (int i = 1; i < n.entryCount; i++) { double tempEnlargement = n.entries[i].Value.Enlargement(ref r); if (tempEnlargement < leastEnlargement || (tempEnlargement == leastEnlargement && n.entries[i].Value.Area < n.entries[index].Value.Area)) { index = i; leastEnlargement = tempEnlargement; } } parents.Push(n); parentsEntry.Push(index); // CL4 [Descend until a leaf is reached] Set N to be the child node pointed to by Fp and repeat from CL2 n = nodeInternal.childNodes[index]; } }
/// <summary> /// Calculate the area by which a rectangle would be enlarged if added to the passed rectangle /// </summary> /// <param name="r1">minimum X coordinate of rectangle 1</param> /// <param name="r2">minimum X coordinate of rectangle 2</param> /// <returns>return enlargement</returns> public static double enlargement(Rectangle r1, Rectangle r2) { double r1Area = (r1.maxX - r1.minX) * (r1.maxY - r1.minY); if (r1Area == double.PositiveInfinity) return 0; // cannot enlarge an infinite rectangle... double r1r2UnionArea = Math.Max(r1.maxX, r2.maxX) - Math.Min(r1.minX, r2.minX) * (Math.Max(r1.maxY, r2.maxY) - Math.Min(r1.minY, r2.minY)); if (r1r2UnionArea == double.PositiveInfinity) return double.PositiveInfinity; // if a finite rectangle is enlarged and becomes infinite, then the enlargement must be infinite. return r1r2UnionArea - r1Area; }
private bool checkConsistency(NodeBase n, int expectedLevel, Rectangle? expectedMBR) { // go through the tree, and check that the internal data structures of the tree are not corrupted. if (n == null) throw new UnexpectedException("Error: Could not read node " + this); // if tree is empty, then there should be exactly one node, at level 1 // TODO: also check the MBR is as for a new node if (n == rootNode && Count == 0) { if (n.level != 1) throw new UnexpectedException("Error: tree is empty but root node is not at level 1"); } if (n.level != expectedLevel) throw new UnexpectedException("Error: Node " + this + ", expected level " + expectedLevel + ", actual level " + n.level); Rectangle calculatedMBR = n.calculateMinimumBoundingRectangle(); Rectangle actualMBR = n.minimumBoundingRectangle; if (!actualMBR.Equals(calculatedMBR)) { if (actualMBR.MinX != n.minimumBoundingRectangle.MinX) throw new UnexpectedException(" actualMinX=" + actualMBR.MinX + ", calc=" + calculatedMBR.MinX); if (actualMBR.MinY != n.minimumBoundingRectangle.MinY) throw new UnexpectedException(" actualMinY=" + actualMBR.MinY + ", calc=" + calculatedMBR.MinY); if (actualMBR.MaxX != n.minimumBoundingRectangle.MaxX) throw new UnexpectedException(" actualMaxX=" + actualMBR.MaxX + ", calc=" + calculatedMBR.MaxX); if (actualMBR.MaxY != n.minimumBoundingRectangle.MaxY) throw new UnexpectedException(" actualMaxY=" + actualMBR.MaxY + ", calc=" + calculatedMBR.MaxY); throw new UnexpectedException("Error: Node " + this + ", calculated MBR does not equal stored MBR"); } if (expectedMBR != null && !actualMBR.Equals(expectedMBR)) throw new UnexpectedException("Error: Node " + this + ", expected MBR (from parent) does not equal stored MBR"); for (int i = 0; i < n.entryCount; i++) { if (n.level > 1) // if not a leaf { NodeInternal nodeInternal = n as NodeInternal; if (nodeInternal.childNodes[i] == null) throw new UnexpectedException("Error: Node " + this + ", Entry " + i + " is null"); if (!checkConsistency(nodeInternal.childNodes[i], n.level - 1, n.entries[i])) { return false; } } } return true; }
/// <summary> /// Find the the union of this rectangle and the passed rectangle.Neither rectangle is altered /// </summary> /// <param name="r">The rectangle to union with this rectangle</param> /// <returns></returns> public Rectangle union(Rectangle r) { Rectangle union = this; union.Add(r); return union; }
// deletedMin/MaxX/Y is a rectangle that has just been deleted or made smaller. Thus, the MBR is only recalculated if the deleted rectangle influenced the old MBR internal virtual void recalculateMBRIfInfluencedBy(ref Rectangle r) { if (minimumBoundingRectangle.MinX == r.MinX || minimumBoundingRectangle.MinY == r.MinY || minimumBoundingRectangle.MaxX == r.MaxX || minimumBoundingRectangle.MaxY == r.MaxY) recalculateMBR(); }