/// <summary> /// Internal helper function for computing intersections. /// </summary> private static bool IntersectHorizontal(ref RCNumRectangle lRect, ref RCNumRectangle rRect, out RCNumber intersectLeft, out RCNumber intersectRight) { if ((lRect.Left <= rRect.Left && rRect.Left < lRect.Right && lRect.Right <= rRect.Right)) { intersectLeft = rRect.Left; intersectRight = lRect.Right; return(true); } else if (rRect.Left <= lRect.Left && lRect.Left < lRect.Right && lRect.Right <= rRect.Right) { intersectLeft = lRect.Left; intersectRight = lRect.Right; return(true); } else if (rRect.Left <= lRect.Left && lRect.Left < rRect.Right && rRect.Right <= lRect.Right) { intersectLeft = lRect.Left; intersectRight = rRect.Right; return(true); } else { intersectLeft = 0; intersectRight = 0; return(false); } }
/// <summary> /// Internal helper function for computing intersections. /// </summary> private static bool IntersectVertical(ref RCNumRectangle lRect, ref RCNumRectangle rRect, out RCNumber intersectTop, out RCNumber intersectBottom) { if (lRect.Top <= rRect.Top && rRect.Top < lRect.Bottom && lRect.Bottom <= rRect.Bottom) { intersectTop = rRect.Top; intersectBottom = lRect.Bottom; return(true); } else if (rRect.Top <= lRect.Top && lRect.Top < lRect.Bottom && lRect.Bottom <= rRect.Bottom) { intersectTop = lRect.Top; intersectBottom = lRect.Bottom; return(true); } else if (rRect.Top <= lRect.Top && lRect.Top < rRect.Bottom && rRect.Bottom <= lRect.Bottom) { intersectTop = lRect.Top; intersectBottom = rRect.Bottom; return(true); } else { intersectTop = 0; intersectBottom = 0; return(false); } }
/// <summary> /// Subdivides this BspSearchTreeNode if necessary. /// </summary> /// <remarks>Subdivision is only allowed in case of leaf-nodes.</remarks> private void Subdivide() { if (!this.isLeaf) { throw new InvalidOperationException("Only leaf-nodes can be subdivided!"); } this.isLeaf = false; if (this.firstChild == null || this.secondChild == null) { /// Children have to be created. RCNumRectangle firstChildArea = this.area.Width > this.area.Height ? new RCNumRectangle(this.area.X, this.area.Y, this.area.Width / 2, this.area.Height) : new RCNumRectangle(this.area.X, this.area.Y, this.area.Width, this.area.Height / 2); RCNumRectangle secondChildArea = this.area.Width > this.area.Height ? firstChildArea + new RCNumVector(firstChildArea.Width, 0) : firstChildArea + new RCNumVector(0, firstChildArea.Height); this.firstChild = new BspSearchTreeNode <T>(firstChildArea, this.capacity, this.minSize); this.secondChild = new BspSearchTreeNode <T>(secondChildArea, this.capacity, this.minSize); } else { /// Children already exist. this.firstChild.isLeaf = true; this.secondChild.isLeaf = true; this.firstChild.contents.Clear(); this.secondChild.contents.Clear(); } }
/// <summary> /// Returns a third RCNumRectangle that represents the intersection of two other RCRectangles. /// If there is no intersection, RCNumRectangle.Undefined is returned. /// </summary> /// <param name="lRect">An RCNumRectangle to intersect.</param> /// <param name="rRect">An RCNumRectangle to intersect.</param> /// <returns> /// An RCNumRectangle that is the intersection of lRect and rRect or RCNumRectangle.Undefined if there is no intersection. /// </returns> public static RCNumRectangle Intersect(RCNumRectangle lRect, RCNumRectangle rRect) { if (!lRect.isDefined) { throw new ArgumentNullException("lRect"); } if (!rRect.isDefined) { throw new ArgumentNullException("rRect"); } RCNumber intersectLeft, intersectRight, intersectTop, intersectBottom; /// Compute the horizontal coordinates of the intersection if (!IntersectHorizontal(ref lRect, ref rRect, out intersectLeft, out intersectRight) && !IntersectHorizontal(ref rRect, ref lRect, out intersectLeft, out intersectRight)) { return(RCNumRectangle.Undefined); } /// Compute the vertical coordinates of the intersection if (!IntersectVertical(ref lRect, ref rRect, out intersectTop, out intersectBottom) && !IntersectVertical(ref rRect, ref lRect, out intersectTop, out intersectBottom)) { return(RCNumRectangle.Undefined); } return(new RCNumRectangle(intersectLeft, intersectTop, intersectRight - intersectLeft, intersectBottom - intersectTop)); }
/// <summary> /// Collects every content attached to this BspSearchTreeNode inside the given rectangular area. /// </summary> /// <param name="area">The rectangular area.</param> /// <param name="outputList"> /// A list that contains every content attached to this BspSearchTreeNode inside the given rectangular area. /// </param> public void CollectContents(RCNumRectangle area, ref RCSet <T> outputList) { if (this.isLeaf) { /// Leaf node -> collect the contents inside the given selection box. foreach (T content in this.contents) { if (content.BoundingBox.IntersectsWith(area)) { outputList.Add(content); } } } else { /// Not a leaf node -> propagate the call to the appropriate child(ren). if (this.firstChild.area.IntersectsWith(area)) { this.firstChild.CollectContents(area, ref outputList); } if (this.secondChild.area.IntersectsWith(area)) { this.secondChild.CollectContents(area, ref outputList); } } }
/// <summary> /// Constructs a BspSearchTreeNode instance. /// </summary> /// <param name="area">The rectangular area covered by this BspSearchTreeNode.</param> /// <param name="capacity">The maximum number of contents this node can hold without subdivision.</param> /// <param name="minSize">The minimum size of a BSP-node.</param> public BspSearchTreeNode(RCNumRectangle area, int capacity, int minSize) { this.contents = new RCSet <T>(); this.isLeaf = true; this.capacity = capacity; this.minSize = minSize; this.area = area; this.firstChild = null; this.secondChild = null; }
/// <see cref="ISearchTree<T>.GetContents"/> public RCSet <T> GetContents(RCNumRectangle area) { if (area == RCNumRectangle.Undefined) { throw new ArgumentNullException("area"); } RCSet <T> retList = new RCSet <T>(); this.rootNode.CollectContents(area, ref retList); return(retList); }
/// <summary> /// Replaces this RCNumRectangle with the intersection of itself and the specified Rectangle. /// </summary> /// <param name="other">The other RCNumRectangle to intersect with.</param> /// <remarks> /// If this and other RCRectangles don't intersect each other, then this RCNumRectangle becomes RCNumRectangle.Undefined. /// </remarks> public void Intersect(RCNumRectangle other) { if (!this.isDefined) { throw new InvalidOperationException("Illegal use of undefined RCNumRectangle!"); } if (!other.isDefined) { throw new ArgumentNullException("other"); } this = RCNumRectangle.Intersect(this, other); }
/// <summary> /// Determines if this RCNumRectangle intersects with rect. /// </summary> /// <param name="rect">The RCNumRectangle to test.</param> /// <returns>True if there is any intersection, false otherwise.</returns> public bool IntersectsWith(RCNumRectangle rect) { if (!this.isDefined) { throw new InvalidOperationException("Illegal use of undefined RCNumRectangle!"); } if (!rect.isDefined) { throw new ArgumentNullException("rect"); } return((this.Left < rect.Right) && (this.Right > rect.Left) && (this.Top < rect.Bottom) && (this.Bottom > rect.Top)); }
/// <summary> /// Initializes a new RCNumRectangle with the specified RCNumRectangle. /// </summary> /// <param name="other">The RCNumRectangle to initialize with.</param> public RCNumRectangle(RCNumRectangle other) { if (!other.isDefined) { throw new ArgumentNullException("other"); } this.isDefined = true; this.x = other.x; this.y = other.y; this.width = other.width; this.height = other.height; }
/// <summary> /// Determines if the rectangular region represented by rect is entirely contained within this RCNumRectangle. /// </summary> /// <param name="rect">The RCNumRectangle to test.</param> /// <returns>True if the rectangular region represented by rect is entirely contained within this RCNumRectangle, false otherwise.</returns> public bool Contains(RCNumRectangle rect) { if (!this.isDefined) { throw new InvalidOperationException("Illegal use of undefined RCNumRectangle!"); } if (!rect.isDefined) { throw new ArgumentNullException("rect"); } return(this.Contains(rect.x, rect.y) && this.Contains(rect.x + rect.width - new RCNumber(1), rect.y + rect.height - new RCNumber(1))); }
/// <summary> /// Constructs a BspSearchTree instance. /// </summary> /// <param name="area">The rectangular area covered by this search tree.</param> /// <param name="nodeCapacity">The maximum number of contents a BSP-tree node can hold without subdivision.</param> /// <param name="minNodeSize">The minimum size of a BSP-node.</param> public BspSearchTree(RCNumRectangle area, int nodeCapacity, int minNodeSize) { if (area == RCNumRectangle.Undefined) { throw new ArgumentNullException("area"); } if (nodeCapacity <= 0) { throw new ArgumentOutOfRangeException("nodeCapacity", "Node capacity must be greater than 0!"); } if (minNodeSize <= 0) { throw new ArgumentOutOfRangeException("minNodeSize", "Minimum BSP-node size must be positive!"); } this.attachedContents = new RCSet <T>(); this.rootNode = new BspSearchTreeNode <T>(area, nodeCapacity, minNodeSize); }
/// <summary> /// Checks whether this RCNumRectangle has the same location and size as the specified RCNumRectangle. /// </summary> /// <param name="other">The RCNumRectangle to test.</param> /// <returns>True if other RCNumRectangle has same location and size as this RCNumRectangle.</returns> public bool Equals(RCNumRectangle other) { return((!this.isDefined && !other.isDefined) || (this.isDefined && other.isDefined && this.x == other.x && this.y == other.y && this.width == other.width && this.height == other.height)); }