├── .gitignore
├── LICENSE
├── Properties
└── AssemblyInfo.cs
├── QuadTree.cs
├── QuadTree.csproj
├── QuadTree.sln
├── README.md
└── quadtree.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | #Autosave files
2 | *~
3 |
4 | #build
5 | [Oo]bj/
6 | [Bb]in/
7 | packages/
8 | TestResults/
9 |
10 | # globs
11 | Makefile.in
12 | *.DS_Store
13 | *.sln.cache
14 | *.suo
15 | *.cache
16 | *.pidb
17 | *.userprefs
18 | *.usertasks
19 | config.log
20 | config.make
21 | config.status
22 | aclocal.m4
23 | install-sh
24 | autom4te.cache/
25 | *.user
26 | *.tar.gz
27 | tarballs/
28 | test-results/
29 | Thumbs.db
30 |
31 | #Mac bundle stuff
32 | *.dmg
33 | *.app
34 |
35 | #resharper
36 | *_Resharper.*
37 | *.Resharper
38 |
39 | #dotCover
40 | *.dotCover
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 ChevyRay
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 |
4 | // Information about this assembly is defined by the following attributes.
5 | // Change them to the values specific to your project.
6 |
7 | [assembly: AssemblyTitle("QuadTree")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("")]
12 | [assembly: AssemblyCopyright("Chevy")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
19 |
20 | [assembly: AssemblyVersion("1.0.*")]
21 |
22 | // The following attributes are used to specify the signing key for the assembly,
23 | // if desired. See the Mono documentation for more information about signing.
24 |
25 | //[assembly: AssemblyDelaySign(false)]
26 | //[assembly: AssemblyKeyFile("")]
27 |
28 |
--------------------------------------------------------------------------------
/QuadTree.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace QuadTree
5 | {
6 | ///
7 | /// A quad tree where leaf nodes contain a quad and a unique instance of T.
8 | /// For example, if you are developing a game, you might use QuadTree
9 | /// for collisions, or QuadTree if you just want to populate it with IDs.
10 | ///
11 | public class QuadTree
12 | {
13 | internal static Stack branchPool = new Stack();
14 | internal static Stack leafPool = new Stack();
15 |
16 | Branch root;
17 | internal int splitCount;
18 | internal int depthLimit;
19 | internal Dictionary leafLookup = new Dictionary();
20 |
21 | ///
22 | /// Creates a new QuadTree.
23 | ///
24 | /// How many leaves a branch can hold before it splits into sub-branches.
25 | /// Maximum distance a node can be from the tree root.
26 | /// The region that your quadtree occupies, all inserted quads should fit into this.
27 | public QuadTree(int splitCount, int depthLimit, ref Quad region)
28 | {
29 | this.splitCount = splitCount;
30 | this.depthLimit = depthLimit;
31 | root = CreateBranch(this, null, 0, ref region);
32 | }
33 | ///
34 | /// Creates a new QuadTree.
35 | ///
36 | /// How many leaves a branch can hold before it splits into sub-branches.
37 | /// Maximum distance a node can be from the tree root.
38 | /// The region that your quadtree occupies, all inserted quads should fit into this.
39 | public QuadTree(int splitCount, int depthLimit, Quad region)
40 | : this(splitCount, depthLimit, ref region)
41 | {
42 |
43 | }
44 | ///
45 | /// Creates a new QuadTree.
46 | ///
47 | /// How many leaves a branch can hold before it splits into sub-branches.
48 | /// Maximum distance a node can be from the tree root.
49 | /// X position of the region.
50 | /// Y position of the region.
51 | /// Width of the region.
52 | /// Height of the region.
53 | public QuadTree(int splitCount, int depthLimit, float x, float y, float width, float height)
54 | : this(splitCount, depthLimit, new Quad(x, y, x + width, y + height))
55 | {
56 |
57 | }
58 |
59 | ///
60 | /// Clear the QuadTree. This will remove all leaves and branches. If you have a lot of moving objects,
61 | /// you probably want to call Clear() every frame, and re-insert every object. Branches and leaves are pooled.
62 | ///
63 | public void Clear()
64 | {
65 | root.Clear();
66 | root.Tree = this;
67 | leafLookup.Clear();
68 | }
69 |
70 | ///
71 | /// QuadTree internally keeps pools of Branches and Leaves. If you want to clear these to clean up memory,
72 | /// you can call this function. Most of the time you'll want to leave this alone, though.
73 | ///
74 | public static void ClearPools()
75 | {
76 | branchPool = new Stack();
77 | leafPool = new Stack();
78 | }
79 |
80 | ///
81 | /// Insert a new leaf node into the QuadTree.
82 | ///
83 | /// The leaf value.
84 | /// The leaf size.
85 | public void Insert(T value, ref Quad quad)
86 | {
87 | Leaf leaf;
88 | if (!leafLookup.TryGetValue(value, out leaf))
89 | {
90 | leaf = CreateLeaf(value, ref quad);
91 | leafLookup.Add(value, leaf);
92 | }
93 | root.Insert(leaf);
94 | }
95 | ///
96 | /// Insert a new leaf node into the QuadTree.
97 | ///
98 | /// The leaf value.
99 | /// The leaf quad.
100 | public void Insert(T value, Quad quad)
101 | {
102 | Insert(value, ref quad);
103 | }
104 | ///
105 | /// Insert a new leaf node into the QuadTree.
106 | ///
107 | /// The leaf value.
108 | /// X position of the leaf.
109 | /// Y position of the leaf.
110 | /// Width of the leaf.
111 | /// Height of the leaf.
112 | public void Insert(T value, float x, float y, float width, float height)
113 | {
114 | var quad = new Quad(x, y, x + width, y + height);
115 | Insert(value, ref quad);
116 | }
117 |
118 | ///
119 | /// Find all values contained in the specified area.
120 | ///
121 | /// True if any values were found.
122 | /// The area to search.
123 | /// A list to populate with the results. If null, this function will create the list for you.
124 | public bool SearchArea(ref Quad quad, ref List values)
125 | {
126 | if (values != null)
127 | values.Clear();
128 | else
129 | values = new List();
130 | root.SearchQuad(ref quad, values);
131 | return values.Count > 0;
132 | }
133 | ///
134 | /// Find all values contained in the specified area.
135 | ///
136 | /// True if any values were found.
137 | /// The area to search.
138 | /// A list to populate with the results. If null, this function will create the list for you.
139 | public bool SearchArea(Quad quad, ref List values)
140 | {
141 | return SearchArea(ref quad, ref values);
142 | }
143 | ///
144 | /// Find all values contained in the specified area.
145 | ///
146 | /// True if any values were found.
147 | /// X position to search.
148 | /// Y position to search.
149 | /// Width of the search area.
150 | /// Height of the search area.
151 | /// A list to populate with the results. If null, this function will create the list for you.
152 | public bool SearchArea(float x, float y, float width, float height, ref List values)
153 | {
154 | var quad = new Quad(x, y, x + width, y + height);
155 | return SearchArea(ref quad, ref values);
156 | }
157 |
158 | ///
159 | /// Find all values overlapping the specified point.
160 | ///
161 | /// True if any values were found.
162 | /// The x coordinate.
163 | /// The y coordinate.
164 | /// A list to populate with the results. If null, this function will create the list for you.
165 | public bool SearchPoint(float x, float y, ref List values)
166 | {
167 | if (values != null)
168 | values.Clear();
169 | else
170 | values = new List();
171 | root.SearchPoint(x, y, values);
172 | return values.Count > 0;
173 | }
174 |
175 | ///
176 | /// Find all other values whose areas are overlapping the specified value.
177 | ///
178 | /// True if any collisions were found.
179 | /// The value to check collisions against.
180 | /// A list to populate with the results. If null, this function will create the list for you.
181 | public bool FindCollisions(T value, ref List values)
182 | {
183 | if (values != null)
184 | values.Clear();
185 | else
186 | values = new List(leafLookup.Count);
187 |
188 | Leaf leaf;
189 | if (leafLookup.TryGetValue(value, out leaf))
190 | {
191 | var branch = leaf.Branch;
192 |
193 | //Add the leaf's siblings (prevent it from colliding with itself)
194 | if (branch.Leaves.Count > 0)
195 | for (int i = 0; i < branch.Leaves.Count; ++i)
196 | if (leaf != branch.Leaves[i] && leaf.Quad.Intersects(ref branch.Leaves[i].Quad))
197 | values.Add(branch.Leaves[i].Value);
198 |
199 | //Add the branch's children
200 | if (branch.Split)
201 | for (int i = 0; i < 4; ++i)
202 | if (branch.Branches[i] != null)
203 | branch.Branches[i].SearchQuad(ref leaf.Quad, values);
204 |
205 | //Add all leaves back to the root
206 | branch = branch.Parent;
207 | while (branch != null)
208 | {
209 | if (branch.Leaves.Count > 0)
210 | for (int i = 0; i < branch.Leaves.Count; ++i)
211 | if (leaf.Quad.Intersects(ref branch.Leaves[i].Quad))
212 | values.Add(branch.Leaves[i].Value);
213 | branch = branch.Parent;
214 | }
215 | }
216 | return false;
217 | }
218 |
219 | ///
220 | /// Count how many branches are in the QuadTree.
221 | ///
222 | public int CountBranches()
223 | {
224 | int count = 0;
225 | CountBranches(root, ref count);
226 | return count;
227 | }
228 | void CountBranches(Branch branch, ref int count)
229 | {
230 | ++count;
231 | if (branch.Split)
232 | for (int i = 0; i < 4; ++i)
233 | if (branch.Branches[i] != null)
234 | CountBranches(branch.Branches[i], ref count);
235 | }
236 |
237 | static Branch CreateBranch(QuadTree tree, Branch parent, int branchDepth, ref Quad quad)
238 | {
239 | var branch = branchPool.Count > 0 ? branchPool.Pop() : new Branch();
240 | branch.Tree = tree;
241 | branch.Parent = parent;
242 | branch.Split = false;
243 | branch.Depth = branchDepth;
244 | float midX = quad.MinX + (quad.MaxX - quad.MinX) * 0.5f;
245 | float midY = quad.MinY + (quad.MaxY - quad.MinY) * 0.5f;
246 | branch.Quads[0].Set(quad.MinX, quad.MinY, midX, midY);
247 | branch.Quads[1].Set(midX, quad.MinY, quad.MaxX, midY);
248 | branch.Quads[2].Set(midX, midY, quad.MaxX, quad.MaxY);
249 | branch.Quads[3].Set(quad.MinX, midY, midX, quad.MaxY);
250 | return branch;
251 | }
252 |
253 | static Leaf CreateLeaf(T value, ref Quad quad)
254 | {
255 | var leaf = leafPool.Count > 0 ? leafPool.Pop() : new Leaf();
256 | leaf.Value = value;
257 | leaf.Quad = quad;
258 | return leaf;
259 | }
260 |
261 | internal class Branch
262 | {
263 | internal QuadTree Tree;
264 | internal Branch Parent;
265 | internal Quad[] Quads = new Quad[4];
266 | internal Branch[] Branches = new Branch[4];
267 | internal List Leaves = new List();
268 | internal bool Split;
269 | internal int Depth;
270 |
271 | internal void Clear()
272 | {
273 | Tree = null;
274 | Parent = null;
275 | Split = false;
276 |
277 | for (int i = 0; i < 4; ++i)
278 | {
279 | if (Branches[i] != null)
280 | {
281 | branchPool.Push(Branches[i]);
282 | Branches[i].Clear();
283 | Branches[i] = null;
284 | }
285 | }
286 |
287 | for (int i = 0; i < Leaves.Count; ++i)
288 | {
289 | leafPool.Push(Leaves[i]);
290 | Leaves[i].Branch = null;
291 | Leaves[i].Value = default(T);
292 | }
293 |
294 | Leaves.Clear();
295 | }
296 |
297 | internal void Insert(Leaf leaf)
298 | {
299 | //If this branch is already split
300 | if (Split)
301 | {
302 | for (int i = 0; i < 4; ++i)
303 | {
304 | if (Quads[i].Contains(ref leaf.Quad))
305 | {
306 | if (Branches[i] == null)
307 | Branches[i] = CreateBranch(Tree, this, Depth+1, ref Quads[i]);
308 | Branches[i].Insert(leaf);
309 | return;
310 | }
311 | }
312 |
313 | Leaves.Add(leaf);
314 | leaf.Branch = this;
315 | }
316 | else
317 | {
318 | //Add the leaf to this node
319 | Leaves.Add(leaf);
320 | leaf.Branch = this;
321 |
322 | //Once I have reached capacity, split the node
323 | if (Leaves.Count >= Tree.splitCount && Depth < Tree.depthLimit)
324 | {
325 | Split = true;
326 | }
327 | }
328 | }
329 |
330 | internal void SearchQuad(ref Quad quad, List values)
331 | {
332 | if (Leaves.Count > 0)
333 | for (int i = 0; i < Leaves.Count; ++i)
334 | if (quad.Intersects(ref Leaves[i].Quad))
335 | values.Add(Leaves[i].Value);
336 | for (int i = 0; i < 4; ++i)
337 | if (Branches[i] != null)
338 | Branches[i].SearchQuad(ref quad, values);
339 | }
340 |
341 | internal void SearchPoint(float x, float y, List values)
342 | {
343 | if (Leaves.Count > 0)
344 | for (int i = 0; i < Leaves.Count; ++i)
345 | if (Leaves[i].Quad.Contains(x, y))
346 | values.Add(Leaves[i].Value);
347 | for (int i = 0; i < 4; ++i)
348 | if (Branches[i] != null)
349 | Branches[i].SearchPoint(x, y, values);
350 | }
351 | }
352 |
353 | internal class Leaf
354 | {
355 | internal Branch Branch;
356 | internal T Value;
357 | internal Quad Quad;
358 | }
359 | }
360 |
361 | ///
362 | /// Used by the QuadTree to represent a rectangular area.
363 | ///
364 | public struct Quad
365 | {
366 | public float MinX;
367 | public float MinY;
368 | public float MaxX;
369 | public float MaxY;
370 |
371 | ///
372 | /// Construct a new Quad.
373 | ///
374 | /// Minimum x.
375 | /// Minimum y.
376 | /// Max x.
377 | /// Max y.
378 | public Quad(float minX, float minY, float maxX, float maxY)
379 | {
380 | MinX = minX;
381 | MinY = minY;
382 | MaxX = maxX;
383 | MaxY = maxY;
384 | }
385 |
386 | ///
387 | /// Set the Quad's position.
388 | ///
389 | /// Minimum x.
390 | /// Minimum y.
391 | /// Max x.
392 | /// Max y.
393 | public void Set(float minX, float minY, float maxX, float maxY)
394 | {
395 | MinX = minX;
396 | MinY = minY;
397 | MaxX = maxX;
398 | MaxY = maxY;
399 | }
400 |
401 | ///
402 | /// Check if this Quad intersects with another.
403 | ///
404 | public bool Intersects(ref Quad other)
405 | {
406 | return MinX < other.MaxX && MinY < other.MaxY && MaxX > other.MinX && MaxY > other.MinY;
407 | }
408 |
409 | ///
410 | /// Check if this Quad can completely contain another.
411 | ///
412 | public bool Contains(ref Quad other)
413 | {
414 | return other.MinX >= MinX && other.MinY >= MinY && other.MaxX <= MaxX && other.MaxY <= MaxY;
415 | }
416 |
417 | ///
418 | /// Check if this Quad contains the point.
419 | ///
420 | public bool Contains(float x, float y)
421 | {
422 | return x > MinX && y > MinY && x < MaxX && y < MaxY;
423 | }
424 | }
425 | }
426 |
427 |
--------------------------------------------------------------------------------
/QuadTree.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {EE15F6DA-9F5B-4554-BAAF-4BFC8618CE76}
7 | Library
8 | QuadTree
9 | QuadTree
10 | v4.5
11 |
12 |
13 | true
14 | full
15 | false
16 | bin\Debug
17 | DEBUG;
18 | prompt
19 | 4
20 | false
21 |
22 |
23 | full
24 | true
25 | bin\Release
26 | prompt
27 | 4
28 | false
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/QuadTree.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuadTree", "QuadTree.csproj", "{EE15F6DA-9F5B-4554-BAAF-4BFC8618CE76}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Any CPU = Debug|Any CPU
9 | Release|Any CPU = Release|Any CPU
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {EE15F6DA-9F5B-4554-BAAF-4BFC8618CE76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
13 | {EE15F6DA-9F5B-4554-BAAF-4BFC8618CE76}.Debug|Any CPU.Build.0 = Debug|Any CPU
14 | {EE15F6DA-9F5B-4554-BAAF-4BFC8618CE76}.Release|Any CPU.ActiveCfg = Release|Any CPU
15 | {EE15F6DA-9F5B-4554-BAAF-4BFC8618CE76}.Release|Any CPU.Build.0 = Release|Any CPU
16 | EndGlobalSection
17 | EndGlobal
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QuadTree
2 | A C# QuadTree implementation specifically designed for testing AABB collisions in 2D space.
3 |
4 | 
5 |
6 | ## What does it do?
7 | A [QuadTree](https://en.wikipedia.org/wiki/Quadtree) is a way of partitioning 2D space by recursively subdividing it into quads. This implementation allows you to insert rectangles into a QuadTree and check for collisions against them. Doing this can massively reduce collision checking time compared to having all your rectangles in a single list and checking for collision against each of them individually.
8 |
9 | ## Example Code
10 | Here is some samples of how to use this QuadTree. In this example, I am using `QuadTree`, but if you were developing a game you might use more convenient values, such as `QuadTree` or `QuadTree`.
11 |
12 | ```csharp
13 | //Create our quad tree and a list of quads
14 | var tree = new QuadTree(50, 0f, 0f, 1000f, 1000f);
15 | var quads = new List();
16 |
17 | //Insert our first quad at position 0
18 | var firstQuad = new Quad(50f, 50f, 100f, 100f);
19 | tree.Insert(0, ref firstQuad);
20 | quads.Add(firstQuad);
21 |
22 | //Insert 1000 more random quads into the tree at positions 1-1000
23 | var random = new Random();
24 | for (int i = 0; i < 1000; ++i)
25 | {
26 | var x = (float)random.NextDouble() * 950f;
27 | var y = (float)random.NextDouble() * 950f;
28 | var w = 10f + (float)random.NextDouble() * 40f;
29 | var h = 10f + (float)random.NextDouble() * 40f;
30 | var quad = new Quad(x, y, x + w, y + h);
31 | tree.Insert(quads.Count, ref quad);
32 | quads.Add(quad);
33 | }
34 |
35 | //Find all the quads in the tree that collide with quad 0 (our first quad)
36 | var collisions = new List();
37 | if (tree.FindCollisions(0, ref collisions))
38 | {
39 | //Print out all the colliding quads
40 | for (int i = 0; i < collisions.Count; ++i)
41 | {
42 | Console.WriteLine("Colliding quads: 0 -> {0}", collisions[0]);
43 | }
44 | }
45 |
46 | //Find all the quads in the tree that overlap a specific position
47 | if (tree.SearchPoint(750f, 600f, ref collisions))
48 | {
49 | //Print out all the colliding quads
50 | for (int i = 0; i < collisions.Count; ++i)
51 | {
52 | Console.WriteLine("Point inside quad: {0}", collisions[0]);
53 | }
54 | }
55 | ```
56 |
--------------------------------------------------------------------------------
/quadtree.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/futurechris/QuadTree/5c22763a1ab6c28241583d8fbef066366afb53a4/quadtree.gif
--------------------------------------------------------------------------------