一个四叉树Demo学习
程序代码:
http://www.codeproject.com/Articles/30535/A-Simple-QuadTree-Implementation-in-C
四叉树:

1 using System;
2 using System.Drawing;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5
6 namespace QuadTreeLib
7 {
8 /// <summary>
9 /// A Quadtree is a structure designed to partition space so
10 /// that it's faster to find out what is inside or outside a given
11 /// area. See http://en.wikipedia.org/wiki/Quadtree
12 /// This QuadTree contains items that have an area (RectangleF)
13 /// it will store a reference to the item in the quad
14 /// that is just big enough to hold it. Each quad has a bucket that
15 /// contain multiple items.
16 /// </summary>
17 public class QuadTree<T> where T : IHasRect
18 {
19 /// <summary>
20 /// The root QuadTreeNode
21 /// 根节点
22 /// </summary>
23 QuadTreeNode<T> m_root;
24
25 /// <summary>
26 /// The bounds of this QuadTree
27 /// 四叉树的包围盒,根节点的范围
28 /// </summary>
29 RectangleF m_rectangle;
30
31 /// <summary>
32 /// An delegate that performs an action on a QuadTreeNode
33 /// </summary>
34 /// <param name="obj"></param>
35 public delegate void QTAction(QuadTreeNode<T> obj);
36
37 /// <summary>
38 ///
39 /// </summary>
40 /// <param name="rectangle"></param>
41 public QuadTree(RectangleF rectangle)
42 {
43 m_rectangle = rectangle;
44 m_root = new QuadTreeNode<T>(m_rectangle);//初始化根节点
45 }
46
47 /// <summary>
48 /// Get the count of items in the QuadTree
49 /// 四叉树节点的数目
50 /// </summary>
51 public int Count { get { return m_root.Count; } }
52
53 /// <summary>
54 /// Insert the feature into the QuadTree
55 /// 插入数据项
56 /// </summary>
57 /// <param name="item"></param>
58 public void Insert(T item)
59 {
60 m_root.Insert(item);//往四叉树插入数据项,其实是通过根节点插入数据项
61 }
62
63 /// <summary>
64 /// Query the QuadTree, returning the items that are in the given area
65 /// 查询四叉树,返回给定区域的数据项
66 /// </summary>
67 /// <param name="area"></param>
68 /// <returns></returns>
69 public List<T> Query(RectangleF area)
70 {
71 return m_root.Query(area);
72 }
73
74 /// <summary>
75 /// Do the specified action for each item in the quadtree
76 /// 执行四叉树中特定的行为
77 /// </summary>
78 /// <param name="action"></param>
79 public void ForEach(QTAction action)
80 {
81 m_root.ForEach(action);
82 }
83
84 }
85
86 }
四叉树节点:

1 using System;
2 using System.Collections.Generic;
3 using System.Drawing;
4 using System.Diagnostics;
5
6 namespace QuadTreeLib
7 {
8 /// <summary>
9 /// The QuadTreeNode
10 /// 四叉树节点
11 /// </summary>
12 /// <typeparam name="T"></typeparam>
13 public class QuadTreeNode<T> where T : IHasRect
14 {
15 /// <summary>
16 /// Construct a quadtree node with the given bounds
17 /// 根据给定的范围构建四叉树节点
18 /// </summary>
19 /// <param name="area"></param>
20 public QuadTreeNode(RectangleF bounds)
21 {
22 m_bounds = bounds;
23 }
24
25 /// <summary>
26 /// The area of this node
27 /// </summary>
28 RectangleF m_bounds;
29
30 /// <summary>
31 /// The contents of this node.
32 /// Note that the contents have no limit: this is not the standard way to impement a QuadTree
33 /// </summary>
34 List<T> m_contents = new List<T>();
35
36 /// <summary>
37 /// The child nodes of the QuadTree
38 /// 四叉树的子节点
39 /// </summary>
40 List<QuadTreeNode<T>> m_nodes = new List<QuadTreeNode<T>>(4);
41
42 /// <summary>
43 /// Is the node empty
44 /// </summary>
45 public bool IsEmpty { get { return m_bounds.IsEmpty || m_nodes.Count == 0; } }
46
47 /// <summary>
48 /// Area of the quadtree node
49 /// 四叉树节点的范围
50 /// </summary>
51 public RectangleF Bounds { get { return m_bounds; } }
52
53 /// <summary>
54 /// Total number of nodes in the this node and all SubNodes
55 ///
56 /// </summary>
57 public int Count
58 {
59 get
60 {
61 int count = 0;
62
63 foreach (QuadTreeNode<T> node in m_nodes)
64 count += node.Count;
65
66 count += this.Contents.Count;
67
68 return count;
69 }
70 }
71
72 /// <summary>
73 /// Return the contents of this node and all subnodes in the true below this one.
74 /// </summary>
75 public List<T> SubTreeContents
76 {
77 get
78 {
79 List<T> results = new List<T>();
80
81 foreach (QuadTreeNode<T> node in m_nodes)
82 results.AddRange(node.SubTreeContents);
83
84 results.AddRange(this.Contents);
85 return results;
86 }
87 }
88
89 public List<T> Contents { get { return m_contents; } }
90
91 /// <summary>
92 /// Query the QuadTree for items that are in the given area
93 /// 查询给定范围的数据项
94 /// </summary>
95 /// <param name="queryArea"></pasram>
96 /// <returns></returns>
97 public List<T> Query(RectangleF queryArea)
98 {
99 // create a list of the items that are found
100 List<T> results = new List<T>();
101
102 // this quad contains items that are not entirely contained by
103 // it's four sub-quads. Iterate through the items in this quad
104 // to see if they intersect.
105 foreach (T item in this.Contents)
106 {
107 if (queryArea.IntersectsWith(item.Rectangle))
108 results.Add(item);
109 }
110
111 foreach (QuadTreeNode<T> node in m_nodes)
112 {
113 if (node.IsEmpty)
114 continue;
115
116 // Case 1: search area completely contained by sub-quad
117 // if a node completely contains the query area, go down that branch
118 // and skip the remaining nodes (break this loop)
119 if (node.Bounds.Contains(queryArea))
120 {
121 results.AddRange(node.Query(queryArea));
122 break;
123 }
124
125 // Case 2: Sub-quad completely contained by search area
126 // if the query area completely contains a sub-quad,
127 // just add all the contents of that quad and it's children
128 // to the result set. You need to continue the loop to test
129 // the other quads
130 if (queryArea.Contains(node.Bounds))
131 {
132 results.AddRange(node.SubTreeContents);
133 continue;
134 }
135
136 // Case 3: search area intersects with sub-quad
137 // traverse into this quad, continue the loop to search other
138 // quads
139 if (node.Bounds.IntersectsWith(queryArea))
140 {
141 results.AddRange(node.Query(queryArea));
142 }
143 }
144
145
146 return results;
147 }
148
149 /// <summary>
150 /// Insert an item to this node
151 /// 将数据项递归插入该四叉树节点
152 /// </summary>
153 /// <param name="item"></param>
154 public void Insert(T item)
155 {
156 // if the item is not contained in this quad, there's a problem
157 //数据项不在当前四叉树节点范围内,返回
158 if (!m_bounds.Contains(item.Rectangle))
159 {
160 Trace.TraceWarning("feature is out of the bounds of this quadtree node");
161 return;
162 }
163
164 // if the subnodes are null create them. may not be sucessfull: see below
165 // we may be at the smallest allowed size in which case the subnodes will not be created
166 if (m_nodes.Count == 0)
167 CreateSubNodes();//分割四叉树,将当前节点分为四个子节点
168
169 // for each subnode:
170 // if the node contains the item, add the item to that node and return
171 // this recurses into the node that is just large enough to fit this item
172 foreach (QuadTreeNode<T> node in m_nodes)
173 {
174 if (node.Bounds.Contains(item.Rectangle))//四叉树在当前节点范围内,插入
175 {
176 node.Insert(item);
177 return;
178 }
179 }
180
181 // if we make it to here, either
182 // 1) none of the subnodes completely contained the item. or
183 // 2) we're at the smallest subnode size allowed
184 // add the item to this node's contents.
185 //考虑这一块,如果所有的子节点都不完全包含本数据项,或者达到了子节点的最小限制,将数据项添加到本节点
186 this.Contents.Add(item);
187 }
188 //递归遍历本节点的子节点
189 public void ForEach(QuadTree<T>.QTAction action)
190 {
191 action(this);
192
193 // draw the child quads
194 foreach (QuadTreeNode<T> node in this.m_nodes)
195 node.ForEach(action);
196 }
197
198 /// <summary>
199 /// Internal method to create the subnodes (partitions space)
200 /// 私有方法,创建子节点
201 /// </summary>
202 private void CreateSubNodes()
203 {
204 // the smallest subnode has an area
205 if ((m_bounds.Height * m_bounds.Width) <= 10)
206 return;
207
208 float halfWidth = (m_bounds.Width / 2f);
209 float halfHeight = (m_bounds.Height / 2f);
210
211 m_nodes.Add(new QuadTreeNode<T>(new RectangleF(m_bounds.Location, new SizeF(halfWidth, halfHeight))));
212 m_nodes.Add(new QuadTreeNode<T>(new RectangleF(new PointF(m_bounds.Left, m_bounds.Top + halfHeight), new SizeF(halfWidth, halfHeight))));
213 m_nodes.Add(new QuadTreeNode<T>(new RectangleF(new PointF(m_bounds.Left + halfWidth, m_bounds.Top), new SizeF(halfWidth, halfHeight))));
214 m_nodes.Add(new QuadTreeNode<T>(new RectangleF(new PointF(m_bounds.Left + halfWidth, m_bounds.Top + halfHeight), new SizeF(halfWidth, halfHeight))));
215 }
216
217 }
218 }
数据项,作为T传入:

1 namespace QuadTreeDemoApp
2 {
3 /// <summary>
4 /// An item with a position, a size and a random colour to test
5 /// the QuadTree structure.
6 /// 数据项
7 /// </summary>
8 class Item: IHasRect
9 {
10 /// <summary>
11 /// Create an item at the given location with the given size.
12 /// 数据项,在给定的位置构建特定大小的数据项
13 /// </summary>
14 /// <param name="p"></param>
15 /// <param name="size"></param>
16 public Item(Point p, int size)
17 {
18 m_size = size;
19 m_rectangle = new RectangleF(p.X, p.Y, m_size, m_size);
20 m_color = Utility.RandomColor;
21 }
22
23 /// <summary>
24 /// Bounds of this item
25 /// 数据项的范围
26 /// </summary>
27 RectangleF m_rectangle;
28
29 /// <summary>
30 ///默认大小
31 /// </summary>
32 int m_size = 2;
33
34 /// <summary>
35 /// 颜色
36 /// </summary>
37 Color m_color;
38
39 /// <summary>
40 /// Colour to draw the item for the QuadTree demo
41 /// </summary>
42 public Color Color { get { return m_color; } }
43
44 #region IHasRect Members
45
46 /// <summary>
47 /// The rectangular bounds of this item
48 /// 数据项的范围矩形
49 /// </summary>
50 public RectangleF Rectangle { get { return m_rectangle; } }
51
52 #endregion
53 }
54 }
包围盒接口:

1 namespace QuadTreeLib
2 {
3 /// <summary>
4 /// An interface that defines and object with a rectangle
5 /// 接口定义了对象的包围盒
6 /// </summary>
7 public interface IHasRect
8 {
9 RectangleF Rectangle { get; }
10 }
11 }
渲染四叉树:

1 using System;
2 using System.Collections.Generic;
3 using System.Drawing;
4
5 using QuadTreeLib;
6
7 namespace QuadTreeDemoApp
8 {
9 /// <summary>
10 /// Class draws a QuadTree
11 /// 绘制四叉树类
12 /// </summary>
13 class QuadTreeRenderer
14 {
15 /// <summary>
16 /// Create the renderer, give the QuadTree to render.
17 /// 渲染四叉树
18 /// </summary>
19 /// <param name="quadTree"></param>
20 public QuadTreeRenderer(QuadTree<Item> quadTree)
21 {
22 m_quadTree = quadTree;
23 }
24
25 QuadTree<Item> m_quadTree;
26
27 /// <summary>
28 /// Hashtable contains a colour for every node in the quad tree so that they are
29 /// rendered with a consistant colour.
30 /// </summary>
31 Dictionary<QuadTreeNode<Item>, Color> m_dictionary = new Dictionary<QuadTreeNode<Item>, Color>();
32
33 /// <summary>
34 /// Get the colour for a QuadTreeNode from the hash table or else create a new colour
35 /// </summary>
36 /// <param name="node"></param>
37 /// <returns></returns>
38 Color GetColor(QuadTreeNode<Item> node)
39 {
40 if (m_dictionary.ContainsKey(node))
41 return m_dictionary[node];
42
43 Color color = Utility.RandomColor;
44 m_dictionary.Add(node, color);
45 return color;
46 }
47
48 /// <summary>
49 /// Render the QuadTree into the given Graphics context
50 /// 在给定的图形设备渲染四叉树
51 /// </summary>
52 /// <param name="graphics"></param>
53 internal void Render(Graphics graphics)
54 {
55 //遍历节点触发委托方法
56 m_quadTree.ForEach(delegate(QuadTreeNode<Item> node)
57 {
58
59 // draw the contents of this quad
60 if (node.Contents != null)
61 {
62 foreach (Item item in node.Contents)
63 {
64 using (Brush b = new SolidBrush(item.Color))
65 graphics.FillEllipse(b, Rectangle.Round(item.Rectangle));
66 }
67 }
68
69 // draw this quad
70
71 // Draw the border
72 //绘制包围盒
73 Color color = GetColor(node);
74 graphics.DrawRectangle(Pens.Black, Rectangle.Round(node.Bounds));
75
76 // draw the inside of the border in a distinct colour
77 using (Pen p = new Pen(color))
78 {
79 Rectangle inside = Rectangle.Round(node.Bounds);
80 inside.Inflate(-1, -1);
81 graphics.DrawRectangle(p, inside);
82 }
83
84 });
85
86 }
87 }
88 }
主窗体调用:

1 public partial class MainForm : Form
2 {
3 QuadTree<Item> m_quadTree;
4
5 QuadTreeRenderer m_renderer;
6
7 public MainForm()
8 {
9 InitializeComponent();
10 }
11
12 private void MainForm_Load(object sender, EventArgs e)
13 {
14 Init();
15 }
16
17 /// <summary>
18 /// Resize the window re-initializes the QuadTree to the new size
19 /// </summary>
20 /// <param name="sender"></param>
21 /// <param name="e"></param>
22 private void MainForm_Resize(object sender, EventArgs e)
23 {
24 Init();
25 }
26
27 /// <summary>
28 /// Draw the QuadTree and the selection rectangle
29 /// Also highlight the selecte items.
30 /// </summary>
31 /// <param name="sender"></param>
32 /// <param name="e"></param>
33 private void MainForm_Paint(object sender, PaintEventArgs e)
34 {
35 // draw the QuadTree
36 m_renderer.Render(e.Graphics);
37
38 // draw the selection rectangle
39 if (!m_selectionRect.IsEmpty)
40 {
41 // draw the selection rect in semi-transparent yellow
42 using (Brush b = new SolidBrush(Color.FromArgb(128, Color.Yellow)))
43 e.Graphics.FillRectangle(b, Rectangle.Round(m_selectionRect));
44 }
45
46 // draw the selected items with a red ring around them
47 if (m_selectedItems != null)
48 {
49 foreach (Item obj in m_selectedItems)
50 {
51 Rectangle selectedRect = Rectangle.Round(obj.Rectangle);
52 selectedRect.Inflate(1, 1);
53 using (Pen p = new Pen(Color.Red, 2))
54 e.Graphics.DrawEllipse(p, selectedRect);
55 }
56 }
57 }
58
59 /// <summary>
60 /// Initialize the QuadTree to the size of the window.
61 /// Initialize the QuadTreeRenderer
62 /// </summary>
63 private void Init()
64 {
65 m_quadTree = new QuadTree<Item>(this.ClientRectangle);//构造客户区范围大小的四叉树
66 m_renderer = new QuadTreeRenderer(m_quadTree);
67 }
68
69 #region mouse interaction code
70
71 bool m_dragging = false;
72 RectangleF m_selectionRect;
73 Point m_startPoint;
74 List<Item> m_selectedItems;
75
76 /// <summary>
77 /// MouseUp:
78 /// - if you're using the left mouse button insert a new item into
79 /// the QuadTree at the click point
80 /// - if you're dragging with the right mouse button, query the
81 /// QuadTree with the selection rectangle defined by the current
82 /// point and the point when the mouseDown event happened.
83 /// </summary>
84 /// <param name="sender"></param>
85 /// <param name="e"></param>
86 private void MainForm_MouseUp(object sender, MouseEventArgs e)
87 {
88 if (m_dragging && e.Button== MouseButtons.Right)
89 {
90 m_selectedItems = m_quadTree.Query(m_selectionRect);
91 m_dragging = false;
92 }
93 else
94 {
95 Random rand = new Random(DateTime.Now.Millisecond);
96
97 m_quadTree.Insert(new Item(e.Location, rand.Next(25) + 4));
98 }
99
100 Invalidate();
101
102 }
103
104 /// <summary>
105 /// If the it's a right click, record the start point and start drag operation
106 /// </summary>
107 /// <param name="sender"></param>
108 /// <param name="e"></param>
109 private void MainForm_MouseDown(object sender, MouseEventArgs e)
110 {
111 if (e.Button == MouseButtons.Right)
112 {
113 m_dragging = true;
114 m_startPoint = e.Location;
115 }
116 }
117
118 /// <summary>
119 /// MouseMove: if we're dragging the update the area of the selection
120 /// rectangle using the start point and the current point.
121 /// Invalidate causes the form to redraw.
122 /// </summary>
123 /// <param name="sender"></param>
124 /// <param name="e"></param>
125 private void MainForm_MouseMove(object sender, MouseEventArgs e)
126 {
127 if (m_dragging)
128 {
129 m_selectionRect = RectangleF.FromLTRB(
130 Math.Min(e.Location.X, m_startPoint.X),
131 Math.Min(e.Location.Y, m_startPoint.Y),
132 Math.Max(e.Location.X, m_startPoint.X),
133 Math.Max(e.Location.Y, m_startPoint.Y));
134
135 Invalidate();
136 }
137 }
138 #endregion
139
140 }
运行结果:
作者:太一吾鱼水
文章未经说明均属原创,学习笔记可能有大段的引用,一般会注明参考文献。
欢迎大家留言交流,转载请注明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2013-03-26 [GL]行星运行2——功能实现
2012-03-26 (2)MFC+openGL单文档框架glFrame
2012-03-26 VC++2008&OpenGL入门——(1)