码迷,mamicode.com
首页 > 其他好文 > 详细

[转]一个四叉树Demo学习

时间:2016-06-18 01:23:15      阅读:255      评论:0      收藏:0      [点我收藏+]

标签:

程序代码:

http://www.codeproject.com/Articles/30535/A-Simple-QuadTree-Implementation-in-C

 四叉树:

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Diagnostics;

namespace QuadTreeLib
{
    /// <summary>
    /// A Quadtree is a structure designed to partition space so
    /// that it‘s faster to find out what is inside or outside a given 
    /// area. See http://en.wikipedia.org/wiki/Quadtree
    /// This QuadTree contains items that have an area (RectangleF)
    /// it will store a reference to the item in the quad 
    /// that is just big enough to hold it. Each quad has a bucket that 
    /// contain multiple items.
    /// </summary>
    public class QuadTree<T> where T : IHasRect
    {
        /// <summary>
        /// The root QuadTreeNode
        /// 根节点
        /// </summary>
        QuadTreeNode<T> m_root;

        /// <summary>
        /// The bounds of this QuadTree
        /// 四叉树的包围盒,根节点的范围
        /// </summary>
        RectangleF m_rectangle;

        /// <summary>
        /// An delegate that performs an action on a QuadTreeNode
        /// </summary>
        /// <param name="obj"></param>
        public delegate void QTAction(QuadTreeNode<T> obj);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rectangle"></param>
        public QuadTree(RectangleF rectangle)
        {
            m_rectangle = rectangle;
            m_root = new QuadTreeNode<T>(m_rectangle);//初始化根节点
        }

        /// <summary>
        /// Get the count of items in the QuadTree
        /// 四叉树节点的数目
        /// </summary>
        public int Count { get { return m_root.Count; } }

        /// <summary>
        /// Insert the feature into the QuadTree
        /// 插入数据项
        /// </summary>
        /// <param name="item"></param>
        public void Insert(T item)
        {
            m_root.Insert(item);//往四叉树插入数据项,其实是通过根节点插入数据项
        }

        /// <summary>
        /// Query the QuadTree, returning the items that are in the given area
        /// 查询四叉树,返回给定区域的数据项
        /// </summary>
        /// <param name="area"></param>
        /// <returns></returns>
        public List<T> Query(RectangleF area)
        {
            return m_root.Query(area);
        }
        
        /// <summary>
        /// Do the specified action for each item in the quadtree
        /// 执行四叉树中特定的行为
        /// </summary>
        /// <param name="action"></param>
        public void ForEach(QTAction action)
        {
            m_root.ForEach(action);
        }

    }

}

QuadTree

四叉树节点:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;

namespace QuadTreeLib
{
    /// <summary>
    /// The QuadTreeNode
    /// 四叉树节点
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class QuadTreeNode<T> where T : IHasRect
    {
        /// <summary>
        /// Construct a quadtree node with the given bounds 
        /// 根据给定的范围构建四叉树节点
        /// </summary>
        /// <param name="area"></param>
        public QuadTreeNode(RectangleF bounds)
        {
            m_bounds = bounds;
        }

        /// <summary>
        /// The area of this node
        /// </summary>
        RectangleF m_bounds;

        /// <summary>
        /// The contents of this node.
        /// Note that the contents have no limit: this is not the standard way to impement a QuadTree
        /// </summary>
        List<T> m_contents = new List<T>();

        /// <summary>
        /// The child nodes of the QuadTree
        /// 四叉树的子节点
        /// </summary>
        List<QuadTreeNode<T>> m_nodes = new List<QuadTreeNode<T>>(4);

        /// <summary>
        /// Is the node empty
        /// </summary>
        public bool IsEmpty { get { return m_bounds.IsEmpty || m_nodes.Count == 0; } }

        /// <summary>
        /// Area of the quadtree node
        /// 四叉树节点的范围
        /// </summary>
        public RectangleF Bounds { get { return m_bounds; } }

        /// <summary>
        /// Total number of nodes in the this node and all SubNodes
        /// 
        /// </summary>
        public int Count
        {
            get
            {
                int count = 0;

                foreach (QuadTreeNode<T> node in m_nodes)
                    count += node.Count;

                count += this.Contents.Count;

                return count;
            }
        }

        /// <summary>
        /// Return the contents of this node and all subnodes in the true below this one.
        /// </summary>
        public List<T> SubTreeContents
        {
            get
            {
                List<T> results = new List<T>();

                foreach (QuadTreeNode<T> node in m_nodes)
                    results.AddRange(node.SubTreeContents);

                results.AddRange(this.Contents);
                return results;
            }
        }

        public List<T> Contents { get { return m_contents; } }

        /// <summary>
        /// Query the QuadTree for items that are in the given area
        /// 查询给定范围的数据项
        /// </summary>
        /// <param name="queryArea"></pasram>
        /// <returns></returns>
        public List<T> Query(RectangleF queryArea)
        {
            // create a list of the items that are found
            List<T> results = new List<T>();

            // this quad contains items that are not entirely contained by
            // it‘s four sub-quads. Iterate through the items in this quad 
            // to see if they intersect.
            foreach (T item in this.Contents)
            {
                if (queryArea.IntersectsWith(item.Rectangle))
                    results.Add(item);
            }

            foreach (QuadTreeNode<T> node in m_nodes)
            {
                if (node.IsEmpty)
                    continue;

                // Case 1: search area completely contained by sub-quad
                // if a node completely contains the query area, go down that branch
                // and skip the remaining nodes (break this loop)
                if (node.Bounds.Contains(queryArea))
                {
                    results.AddRange(node.Query(queryArea));
                    break;
                }

                // Case 2: Sub-quad completely contained by search area 
                // if the query area completely contains a sub-quad,
                // just add all the contents of that quad and it‘s children 
                // to the result set. You need to continue the loop to test 
                // the other quads
                if (queryArea.Contains(node.Bounds))
                {
                    results.AddRange(node.SubTreeContents);
                    continue;
                }

                // Case 3: search area intersects with sub-quad
                // traverse into this quad, continue the loop to search other
                // quads
                if (node.Bounds.IntersectsWith(queryArea))
                {
                    results.AddRange(node.Query(queryArea));
                }
            }


            return results;
        }

        /// <summary>
        /// Insert an item to this node
        /// 将数据项递归插入该四叉树节点
        /// </summary>
        /// <param name="item"></param>
        public void Insert(T item)
        {
            // if the item is not contained in this quad, there‘s a problem
            //数据项不在当前四叉树节点范围内,返回
            if (!m_bounds.Contains(item.Rectangle))
            {
                Trace.TraceWarning("feature is out of the bounds of this quadtree node");
                return;
            }

            // if the subnodes are null create them. may not be sucessfull: see below
            // we may be at the smallest allowed size in which case the subnodes will not be created
            if (m_nodes.Count == 0)
                CreateSubNodes();//分割四叉树,将当前节点分为四个子节点

            // for each subnode:
            // if the node contains the item, add the item to that node and return
            // this recurses into the node that is just large enough to fit this item
            foreach (QuadTreeNode<T> node in m_nodes)
            {
                if (node.Bounds.Contains(item.Rectangle))//四叉树在当前节点范围内,插入
                {
                    node.Insert(item);
                    return;
                }
            }

            // if we make it to here, either
            // 1) none of the subnodes completely contained the item. or
            // 2) we‘re at the smallest subnode size allowed 
            // add the item to this node‘s contents.
            //考虑这一块,如果所有的子节点都不完全包含本数据项,或者达到了子节点的最小限制,将数据项添加到本节点
            this.Contents.Add(item);
        }
        //递归遍历本节点的子节点
        public void ForEach(QuadTree<T>.QTAction action)
        {
            action(this);

            // draw the child quads
            foreach (QuadTreeNode<T> node in this.m_nodes)
                node.ForEach(action);
        }

        /// <summary>
        /// Internal method to create the subnodes (partitions space)
        /// 私有方法,创建子节点
        /// </summary>
        private void CreateSubNodes()
        {
            // the smallest subnode has an area 
            if ((m_bounds.Height * m_bounds.Width) <= 10)
                return;

            float halfWidth = (m_bounds.Width / 2f);
            float halfHeight = (m_bounds.Height / 2f);

            m_nodes.Add(new QuadTreeNode<T>(new RectangleF(m_bounds.Location, new SizeF(halfWidth, halfHeight))));
            m_nodes.Add(new QuadTreeNode<T>(new RectangleF(new PointF(m_bounds.Left, m_bounds.Top + halfHeight), new SizeF(halfWidth, halfHeight))));
            m_nodes.Add(new QuadTreeNode<T>(new RectangleF(new PointF(m_bounds.Left + halfWidth, m_bounds.Top), new SizeF(halfWidth, halfHeight))));
            m_nodes.Add(new QuadTreeNode<T>(new RectangleF(new PointF(m_bounds.Left + halfWidth, m_bounds.Top + halfHeight), new SizeF(halfWidth, halfHeight))));
        }

    }
}

QuadTreeNode

数据项,作为T传入:

namespace QuadTreeDemoApp
{
    /// <summary>
    /// An item with a position, a size and a random colour to test
    /// the QuadTree structure.
    /// 数据项
    /// </summary>
    class Item: IHasRect
    {
        /// <summary>
        /// Create an item at the given location with the given size.
        /// 数据项,在给定的位置构建特定大小的数据项
        /// </summary>
        /// <param name="p"></param>
        /// <param name="size"></param>
        public Item(Point p, int size)
        {
            m_size = size;
            m_rectangle = new RectangleF(p.X, p.Y, m_size, m_size);
            m_color = Utility.RandomColor;
        }

        /// <summary>
        /// Bounds of this item
        /// 数据项的范围
        /// </summary>
        RectangleF m_rectangle;

        /// <summary>
        ///默认大小
        /// </summary>
        int m_size = 2;

        /// <summary>
        /// 颜色
        /// </summary>
        Color m_color;

        /// <summary>
        /// Colour to draw the item for the QuadTree demo
        /// </summary>
        public Color Color { get { return m_color; } }

        #region IHasRect Members

        /// <summary>
        /// The rectangular bounds of this item
        /// 数据项的范围矩形
        /// </summary>
        public RectangleF Rectangle { get { return m_rectangle; } }

        #endregion
    }
}

Item

包围盒接口:

namespace QuadTreeLib
{
    /// <summary>
    /// An interface that defines and object with a rectangle
    /// 接口定义了对象的包围盒
    /// </summary>
    public interface IHasRect
    {
        RectangleF Rectangle { get; }
    }
}

IHasRect

渲染四叉树:

using System;
using System.Collections.Generic;
using System.Drawing;

using QuadTreeLib;

namespace QuadTreeDemoApp
{
    /// <summary>
    /// Class draws a QuadTree
    /// 绘制四叉树类
    /// </summary>
    class QuadTreeRenderer 
    {
        /// <summary>
        /// Create the renderer, give the QuadTree to render.
        /// 渲染四叉树
        /// </summary>
        /// <param name="quadTree"></param>
        public QuadTreeRenderer(QuadTree<Item> quadTree)
        {
            m_quadTree = quadTree;
        }
        
        QuadTree<Item> m_quadTree;

        /// <summary>
        /// Hashtable contains a colour for every node in the quad tree so that they are
        /// rendered with a consistant colour.
        /// </summary>
        Dictionary<QuadTreeNode<Item>, Color> m_dictionary = new Dictionary<QuadTreeNode<Item>, Color>();
        
        /// <summary>
        /// Get the colour for a QuadTreeNode from the hash table or else create a new colour
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        Color GetColor(QuadTreeNode<Item> node)
        {
            if (m_dictionary.ContainsKey(node))
                return m_dictionary[node];

            Color color = Utility.RandomColor;
            m_dictionary.Add(node, color);
            return color;
        }

        /// <summary>
        /// Render the QuadTree into the given Graphics context
        /// 在给定的图形设备渲染四叉树
        /// </summary>
        /// <param name="graphics"></param>
        internal void Render(Graphics graphics)
        {
            //遍历节点触发委托方法
            m_quadTree.ForEach(delegate(QuadTreeNode<Item> node)
            {

                // draw the contents of this quad
                if (node.Contents != null)
                {
                    foreach (Item item in node.Contents)
                    {
                        using (Brush b = new SolidBrush(item.Color))
                            graphics.FillEllipse(b, Rectangle.Round(item.Rectangle));
                    }
                }

                // draw this quad

                // Draw the border
                //绘制包围盒
                Color color = GetColor(node);
                graphics.DrawRectangle(Pens.Black, Rectangle.Round(node.Bounds));
            
                // draw the inside of the border in a distinct colour
                using (Pen p = new Pen(color))
                {
                    Rectangle inside = Rectangle.Round(node.Bounds);
                    inside.Inflate(-1, -1);
                    graphics.DrawRectangle(p, inside);
                }

            });

        }
    }
}

QuadTreeRenderer

主窗体调用:

public partial class MainForm : Form
    {
        QuadTree<Item> m_quadTree;
  
        QuadTreeRenderer m_renderer;
  
        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            Init();
        }

        /// <summary>
        /// Resize the window re-initializes the QuadTree to the new size
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_Resize(object sender, EventArgs e)
        {
            Init();
        }

        /// <summary>
        /// Draw the QuadTree and the selection rectangle
        /// Also highlight the selecte items.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_Paint(object sender, PaintEventArgs e)
        {
            // draw the QuadTree
            m_renderer.Render(e.Graphics);

            // draw the selection rectangle 
            if (!m_selectionRect.IsEmpty)
            {
                // draw the selection rect in semi-transparent yellow
                using (Brush b = new SolidBrush(Color.FromArgb(128, Color.Yellow)))
                    e.Graphics.FillRectangle(b, Rectangle.Round(m_selectionRect));
            }

            // draw the selected items with a red ring around them
            if (m_selectedItems != null)
            {
                foreach (Item obj in m_selectedItems)
                {
                    Rectangle selectedRect = Rectangle.Round(obj.Rectangle);
                    selectedRect.Inflate(1, 1);
                    using (Pen p = new Pen(Color.Red, 2))
                        e.Graphics.DrawEllipse(p, selectedRect);
                }
            }
        }

        /// <summary>
        /// Initialize the QuadTree to the size of the window.
        /// Initialize the QuadTreeRenderer
        /// </summary>
        private void Init()
        {
            m_quadTree = new QuadTree<Item>(this.ClientRectangle);//构造客户区范围大小的四叉树
            m_renderer = new QuadTreeRenderer(m_quadTree);
        }

        #region mouse interaction code
        
        bool m_dragging = false;
        RectangleF m_selectionRect;
        Point m_startPoint;
        List<Item> m_selectedItems;

        /// <summary>
        /// MouseUp: 
        /// - if you‘re using the left mouse button insert a new item into
        ///   the QuadTree at the click point
        /// - if you‘re dragging with the right mouse button, query the 
        ///   QuadTree with the selection rectangle defined by the current 
        ///   point and the point when the mouseDown event happened.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_MouseUp(object sender, MouseEventArgs e)
        {
            if (m_dragging && e.Button== MouseButtons.Right)
            {
                m_selectedItems = m_quadTree.Query(m_selectionRect);
                m_dragging = false;
            }
            else
            {
                Random rand = new Random(DateTime.Now.Millisecond);

                m_quadTree.Insert(new Item(e.Location, rand.Next(25) + 4));
            }

            Invalidate();

        }

        /// <summary>
        /// If the it‘s a right click, record the start point and start drag operation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                m_dragging = true;
                m_startPoint = e.Location;
            }
        }

        /// <summary>
        /// MouseMove: if we‘re dragging the update the area of the selection
        /// rectangle using the start point and the current point.
        /// Invalidate causes the form to redraw.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_MouseMove(object sender, MouseEventArgs e)
        {
            if (m_dragging)
            {
                m_selectionRect = RectangleF.FromLTRB(
                    Math.Min(e.Location.X, m_startPoint.X),
                    Math.Min(e.Location.Y, m_startPoint.Y),
                    Math.Max(e.Location.X, m_startPoint.X),
                    Math.Max(e.Location.Y, m_startPoint.Y));

                Invalidate();
            }
        }
        #endregion

    }

MainForm

运行结果:

技术分享

[转]一个四叉树Demo学习

标签:

原文地址:http://www.cnblogs.com/rainbow70626/p/5595481.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!