├── .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 | ![QuadTree Animation](quadtree.gif) 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 --------------------------------------------------------------------------------