zoukankan      html  css  js  c++  java
  • [转]一个四叉树Demo学习

    程序代码:

    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

    运行结果:

  • 相关阅读:
    C#语句2——循环语句(for循环与for循环嵌套)
    C#语言基础
    C#几种截取字符串的方法小结
    SQL提取数据库表名,字段名等信息
    Winform绑定图片的三种方式
    C#中遍历各类数据集合的方法总结
    c#winform图片绘制与图片验证码
    DataGridView根据条件给单元格绑定图片
    C#Winform 父窗体 子窗体 传值
    一条Sql语句分组排序并且限制显示的数据条数
  • 原文地址:https://www.cnblogs.com/rainbow70626/p/5595481.html
Copyright © 2011-2022 走看看