“通用” QuadTree实现

对于我正在从事的项目,我需要QuadTree来使我能够快速轻松地访问给定范围内的对象。 我学到了很多知识,并认为与Bytes社区共享代码可能是个好主意。

要使用它,请创建一个新的QuadTree并告诉它要覆盖的区域。 然后,只需使用Insert方法将对象插入其中即可。 您可以使用Remove方法将对象取出。 移除时,如果四边形的子级为空,则这些子级将被移除。

一些笔记,没有特别的顺序...

*此QuadTree可容纳任何类型,只要该类型继承自IHasRectangle并实现接口定义的属性即可。 这样,QuadTree就知道对象的边界是什么。

*在此实现中,一个四边形在容纳两个以上对象时会细分。 也就是说,一个四边形只会包含一个

完全包含。 如果四边形的子级不能包含对象,则该对象将位于四边形本身中。

*该代码是使用XNA矩形构建的。 修改代码以便与System.Drawing甚至自定义Rectangle对象一起使用应该很容易。 只要对象提供适当的方法(包含和相交),移植就需要进行最少的工作。

*此代码是免费提供的,请尽情享受!

*鉴于上述情况,请注意,不能保证它没有错误。 我已经对其进行了相当广泛的测试,但是很有可能我错过了一些东西。 我绝对愿意接受反馈,但请保持建设性。

*我仍在做一些工作,但这会使它与XNA有关。 这是对象的通用版本。 我可能会在以后发布更新。

杂乱无章...这是代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework; 
namespace QuadTree
{
    /// <summary>
    /// Interface to define Rect, so that QuadTree knows how to store the object.
    /// </summary>
    public interface IHasRectangle
    {
        /// <summary>
        /// The rectangle that defines the object's boundaries.
        /// </summary>
        Rectangle Rect { get; }
    } 
    /// <summary>
    /// A QuadTree Object that provides fast and efficient storage of objects in a world space.
    /// </summary>
    /// <typeparam name="T">Any object iheriting from IHasRect.</typeparam>
    public class QuadTree<T> where T : IHasRectangle
    {
        #region Constants
        // How many objects can exist in a QuadTree before it sub divides itself
        private const int MAX_OBJECTS_PER_NODE = 2;
        #endregion 
        #region Private Members
        private List<T> m_objects = null;       // The objects in this QuadTree
        private Rectangle m_rect;               // The area this QuadTree represents 
        private QuadTree<T> m_childTL = null;   // Top Left Child
        private QuadTree<T> m_childTR = null;   // Top Right Child
        private QuadTree<T> m_childBL = null;   // Bottom Left Child
        private QuadTree<T> m_childBR = null;   // Bottom Right Child
        #endregion 
        #region Public Properties
        /// <summary>
        /// The area this QuadTree represents.
        /// </summary>
        public Rectangle QuadRect { get { return m_rect; } } 
        /// <summary>
        /// The top left child for this QuadTree
        /// </summary>
        public QuadTree<T> TopLeftChild { get { return m_childTL; } } 
        /// <summary>
        /// The top right child for this QuadTree
        /// </summary>
        public QuadTree<T> TopRightChild { get { return m_childTR; } } 
        /// <summary>
        /// The bottom left child for this QuadTree
        /// </summary>
        public QuadTree<T> BottomLeftChild { get { return m_childBL; } } 
        /// <summary>
        /// The bottom right child for this QuadTree
        /// </summary>
        public QuadTree<T> BottomRightChild { get { return m_childBR; } } 
        /// <summary>
        /// The objects contained in this QuadTree at it's level (ie, excludes children)
        /// </summary>
        public List<T> Objects { get { return m_objects; } } 
        /// <summary>
        /// How many total objects are contained within this QuadTree (ie, includes children)
        /// </summary>
        public int Count { get { return this.ObjectCount(); } }
        #endregion 
        #region Constructor
        /// <summary>
        /// Creates a QuadTree for the specified area.
        /// </summary>
        /// <param name="rect">The area this QuadTree object will encompass.</param>
        public QuadTree(Rectangle rect)
        {
            m_rect = rect;
        } 
        /// <summary>
        /// Creates a QuadTree for the specified area.
        /// </summary>
        /// <param name="x">The top-left position of the area rectangle.</param>
        /// <param name="y">The top-right position of the area reactangle.</param>
        /// <param name="width">The width of the area rectangle.</param>
        /// <param name="height">The height of the area rectangle.</param>
        public QuadTree(int x, int y, int width, int height)
        {
            m_rect = new Rectangle(x, y, width, height);
        }
        #endregion 
        #region Private Members
        /// <summary>
        /// Add an item to the object list.
        /// </summary>
        /// <param name="item">The item to add.</param>
        private void Add(T item)
        {
            if (m_objects == null)
                m_objects = new List<T>(); 
            m_objects.Add(item);
        } 
        /// <summary>
        /// Remove an item from the object list.
        /// </summary>
        /// <param name="item">The object to remove.</param>
        private void Remove(T item)
        {
            if (m_objects != null && m_objects.Contains(item))
                m_objects.Remove(item);
        } 
        /// <summary>
        /// Get the total for all objects in this QuadTree, including children.
        /// </summary>
        /// <returns>The number of objects contained within this QuadTree and its children.</returns>
        private int ObjectCount()
        {
            int count = 0; 
            // Add the objects at this level
            if (m_objects != null) count += m_objects.Count; 
            // Add the objects that are contained in the children
            if (m_childTL != null)
            {
                count += m_childTL.ObjectCount();
                count += m_childTR.ObjectCount();
                count += m_childBL.ObjectCount();
                count += m_childBR.ObjectCount();
            } 
            return count;
        } 
        /// <summary>
        /// Subdivide this QuadTree and move it's children into the appropriate Quads where applicable.
        /// </summary>
        private void Subdivide()
        {
            // We've reached capacity, subdivide...
            Point size = new Point(m_rect.Width / 2, m_rect.Height / 2);
            Point mid = new Point(m_rect.X + size.X, m_rect.Y + size.Y); 
            m_childTL = new QuadTree<T>(new Rectangle(m_rect.Left, m_rect.Top, size.X, size.Y));
            m_childTR = new QuadTree<T>(new Rectangle(mid.X, m_rect.Top, size.X, size.Y));
            m_childBL = new QuadTree<T>(new Rectangle(m_rect.Left, mid.Y, size.X, size.Y));
            m_childBR = new QuadTree<T>(new Rectangle(mid.X, mid.Y, size.X, size.Y)); 
            // If they're completely contained by the quad, bump objects down
            for (int i = 0; i < m_objects.Count; i++)
            {
                QuadTree<T> destTree = GetDestinationTree(m_objects[i]); 
                if (destTree != this)
                {
                    // Insert to the appropriate tree, remove the object, and back up one in the loop
                    destTree.Insert(m_objects[i]);
                    Remove(m_objects[i]);
                    i--;
                }
            }
        } 
        /// <summary>
        /// Get the child Quad that would contain an object.
        /// </summary>
        /// <param name="item">The object to get a child for.</param>
        /// <returns></returns>
        private QuadTree<T> GetDestinationTree(T item)
        {
            // If a child can't contain an object, it will live in this Quad
            QuadTree<T> destTree = this; 
            if (m_childTL.QuadRect.Contains(item.Rect))
            {
                destTree = m_childTL;
            }
            else if (m_childTR.QuadRect.Contains(item.Rect))
            {
                destTree = m_childTR;
            }
            else if (m_childBL.QuadRect.Contains(item.Rect))
            {
                destTree = m_childBL;
            }
            else if (m_childBR.QuadRect.Contains(item.Rect))
            {
                destTree = m_childBR;
            } 
            return destTree;
        }
        #endregion 
        #region Public Methods
        /// <summary>
        /// Clears the QuadTree of all objects, including any objects living in its children.
        /// </summary>
        public void Clear()
        {
            // Clear out the children, if we have any
            if (m_childTL != null)
            {
                m_childTL.Clear();
                m_childTR.Clear();
                m_childBL.Clear();
                m_childBR.Clear();
            } 
            // Clear any objects at this level
            if (m_objects != null)
            {
                m_objects.Clear();
                m_objects = null;
            } 
            // Set the children to null
            m_childTL = null;
            m_childTR = null;
            m_childBL = null;
            m_childBR = null;
        } 
        /// <summary>
        /// Deletes an item from this QuadTree. If the object is removed causes this Quad to have no objects in its children, it's children will be removed as well.
        /// </summary>
        /// <param name="item">The item to remove.</param>
        public void Delete(T item)
        {
            // If this level contains the object, remove it
            bool objectRemoved = false;
            if (m_objects != null && m_objects.Contains(item))
            {
                Remove(item);
                objectRemoved = true;
            } 
            // If we didn't find the object in this tree, try to delete from its children
            if (m_childTL != null && !objectRemoved)
            {
                m_childTL.Delete(item);
                m_childTR.Delete(item);
                m_childBL.Delete(item);
                m_childBR.Delete(item);
            } 
            if (m_childTL != null)
            {
                // If all the children are empty, delete all the children
                if (m_childTL.Count == 0 &&
                    m_childTR.Count == 0 &&
                    m_childBL.Count == 0 &&
                    m_childBR.Count == 0)
                {
                    m_childTL = null;
                    m_childTR = null;
                    m_childBL = null;
                    m_childBR = null;
                }
            }
        } 
        /// <summary>
        /// Insert an item into this QuadTree object.
        /// </summary>
        /// <param name="item">The item to insert.</param>
        public void Insert(T item)
        {
            // If this quad doesn't intersect the items rectangle, do nothing
            if (!m_rect.Intersects(item.Rect))
                return; 
            if (m_objects == null || 
                (m_childTL == null && m_objects.Count + 1 <= MAX_OBJECTS_PER_NODE))
            {
                // If there's room to add the object, just add it
                Add(item);
            }
            else
            {
                // No quads, create them and bump objects down where appropriate
                if (m_childTL == null)
                {
                    Subdivide();
                } 
                // Find out which tree this object should go in and add it there
                QuadTree<T> destTree = GetDestinationTree(item);
                if (destTree == this)
                {
                    Add(item);
                }
                else
                {
                    destTree.Insert(item);
                }
            }
        } 
        /// <summary>
        /// Get the objects in this tree that intersect with the specified rectangle.
        /// </summary>
        /// <param name="rect">The rectangle to find objects in.</param>
        /// <param name="results">A reference to a list that will be populated with the results.</param>
        public void GetObjects(Rectangle rect, ref List<T> results)
        {
            // We can't do anything if the results list doesn't exist
            if (results != null)
            {
                if (rect.Contains(m_rect))
                {
                    // If the search area completely contains this quad, just get every object this quad and all it's children have
                    GetAllObjects(ref results);
                }
                else if (rect.Intersects(m_rect))
                {
                    // Otherwise, if the quad isn't fully contained, only add objects that intersect with the search rectangle
                    if (m_objects != null)
                    {
                        for (int i = 0; i < m_objects.Count; i++)
                        {
                            if (rect.Intersects(m_objects[i].Rect))
                            {
                                results.Add(m_objects[i]);
                            }
                        }
                    } 
                    // Get the objects for the search rectangle from the children
                    if (m_childTL != null)
                    {
                        m_childTL.GetObjects(rect, ref results);
                        m_childTR.GetObjects(rect, ref results);
                        m_childBL.GetObjects(rect, ref results);
                        m_childBR.GetObjects(rect, ref results);
                    }
                }
            }
        } 
        /// <summary>
        /// Get all objects in this Quad, and it's children.
        /// </summary>
        /// <param name="results">A reference to a list in which to store the objects.</param>
        public void GetAllObjects(ref List<T> results)
        {
            // If this Quad has objects, add them
            if (m_objects != null)
                results.AddRange(m_objects); 
            // If we have children, get their objects too
            if (m_childTL != null)
            {
                m_childTL.GetAllObjects(ref results);
                m_childTR.GetAllObjects(ref results);
                m_childBL.GetAllObjects(ref results);
                m_childBR.GetAllObjects(ref results);
            }
        }
        #endregion
    }
} 

From: https://bytes.com/topic/c-sharp/insights/880968-generic-quadtree-implementation

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值