diff --git a/Quadtree/Quadtree.cs b/Quadtree/Quadtree.cs index 89c9c3f..5577a6a 100644 --- a/Quadtree/Quadtree.cs +++ b/Quadtree/Quadtree.cs @@ -32,11 +32,6 @@ namespace SpatialCollections /// private readonly Func _getPositionCallback; - /// - /// Callback to determine the bounding box of an entity. - /// - private readonly Func _getBoundingBoxCallback; - /// /// The list of quadtree vertices. The root vertex is always the first item. The four child vertices of a branch /// are always created together and stored contiguously. @@ -50,11 +45,6 @@ namespace SpatialCollections private Rectangle _rootBoundingBox; - /// - /// The maximum bounding radius of all stored entities, used to inflate the bounding box for queries. - /// - private float _maxEntityBoundingRadius; - /// /// Stack of vertex indices with their bounding boxes. It is reused for each query. /// @@ -65,31 +55,24 @@ namespace SpatialCollections /// private readonly Stack _vertexStack; - public Quadtree(int maxLeafSize, int maxTreeDepth, Func getPositionCallback, - Func getBoundingBoxCallback) + public Quadtree(int maxLeafSize, int maxTreeDepth, Func getPositionCallback) { _maxLeafSize = maxLeafSize; _maxTreeDepth = maxTreeDepth; _getPositionCallback = getPositionCallback; - _getBoundingBoxCallback = getBoundingBoxCallback; _vertices = new List() { new(-1, 0) }; _items = new List>(); _rootBoundingBox = new Rectangle(Vector2.Zero, Vector2.Zero); - _maxEntityBoundingRadius = 0f; _queryStack = new Stack(); _vertexStack = new Stack(); } public int Count => _items.Count; - public void Add(T entity, float boundingRadius) + public void Add(T entity) { var itemIndex = _items.Count; _items.Add(new QuadtreeItem(entity)); - if (_maxEntityBoundingRadius < boundingRadius) - { - _maxEntityBoundingRadius = boundingRadius; - } if (_vertices.Count == 1 && _vertices[0].ChildCount < _maxLeafSize) { @@ -165,10 +148,7 @@ namespace SpatialCollections public void Query(Rectangle box, List resultList) { - Rectangle inflatedBox = box; - inflatedBox.Expand(_maxEntityBoundingRadius); - - QueryProcessChildVertex(inflatedBox, box, 0, _rootBoundingBox, resultList, _queryStack); + QueryProcessChildVertex(box, 0, _rootBoundingBox, resultList, _queryStack); while (_queryStack.Count > 0) { @@ -177,16 +157,16 @@ namespace SpatialCollections int childIndex = _vertices[query.VertexIndex].FirstChildIndex; Rectangle halfBounds = new(query.VertexBounds.Center, query.VertexBounds.Max); Vector2 halfSize = halfBounds.Size; - QueryProcessChildVertex(inflatedBox, box, childIndex++, halfBounds, resultList, _queryStack); + QueryProcessChildVertex(box, childIndex++, halfBounds, resultList, _queryStack); halfBounds.Translate(new Vector2(-halfSize.X, 0f)); - QueryProcessChildVertex(inflatedBox, box, childIndex++, halfBounds, resultList, _queryStack); + QueryProcessChildVertex(box, childIndex++, halfBounds, resultList, _queryStack); halfBounds.Translate(new Vector2(halfSize.X, -halfSize.Y)); - QueryProcessChildVertex(inflatedBox, box, childIndex++, halfBounds, resultList, _queryStack); + QueryProcessChildVertex(box, childIndex++, halfBounds, resultList, _queryStack); halfBounds.Translate(new Vector2(-halfSize.X, 0f)); - QueryProcessChildVertex(inflatedBox, box, childIndex, halfBounds, resultList, _queryStack); + QueryProcessChildVertex(box, childIndex, halfBounds, resultList, _queryStack); } } @@ -196,7 +176,6 @@ namespace SpatialCollections _vertices.Add(new QuadtreeVertex(-1, 0)); _items.Clear(); _rootBoundingBox = new Rectangle(Vector2.Zero, Vector2.Zero); - _maxEntityBoundingRadius = 0f; } /// @@ -342,21 +321,21 @@ namespace SpatialCollections return (vertexIndex, depth, bounds); } - private void QueryProcessChildVertex(Rectangle inflatedBox, Rectangle box, int vertexIndex, - Rectangle vertexBounds, List resultList, Stack stack) + private void QueryProcessChildVertex(Rectangle box, int vertexIndex, Rectangle vertexBounds, + List resultList, Stack stack) { - switch (inflatedBox.Intersects(vertexBounds)) + switch (box.Intersects(vertexBounds)) { case IntersectionType.Contains: if (_vertices[vertexIndex].ChildCount == -1) { // Found contained vertex, appends items of all child vertices. - QueryAppendContainedItems(vertexIndex, box, resultList); + QueryAppendItems(vertexIndex, resultList); } else { // Found leaf, appends items. - QueryAppendContainedLeafItems(vertexIndex, box, resultList); + QueryAppendLeafItems(vertexIndex, resultList); } break; case IntersectionType.Intersects: @@ -367,14 +346,14 @@ namespace SpatialCollections } else { - // Found leaf, appends items. + // Found leaf, appends contained items. QueryAppendContainedLeafItems(vertexIndex, box, resultList); } break; } } - private void QueryAppendContainedItems(int vertexIndex, Rectangle box, List resultList) + private void QueryAppendItems(int vertexIndex, List resultList) { _vertexStack.Push(vertexIndex); @@ -393,22 +372,34 @@ namespace SpatialCollections else { // Found leaf, appends items. - QueryAppendContainedLeafItems(current, box, resultList); + QueryAppendLeafItems(current, resultList); } } } + private void QueryAppendLeafItems(int vertexIndex, List resultList) + { + int index = _vertices[vertexIndex].FirstChildIndex; + while (index != -1) + { + var item = _items[index]; + resultList.Add(item.Entity); + index = item.Next; + } + } + private void QueryAppendContainedLeafItems(int vertexIndex, Rectangle box, List resultList) { int index = _vertices[vertexIndex].FirstChildIndex; while (index != -1) { - T entity = _items[index].Entity; - if (box.Intersects(_getBoundingBoxCallback(entity)) != IntersectionType.Disjoint) + var item = _items[index]; + T entity = item.Entity; + if (box.Contains(_getPositionCallback(entity))) { resultList.Add(entity); } - index = _items[index].Next; + index = item.Next; } } diff --git a/QuadtreeTests/Tests.cs b/QuadtreeTests/Tests.cs index 5865896..bf03783 100644 --- a/QuadtreeTests/Tests.cs +++ b/QuadtreeTests/Tests.cs @@ -1,4 +1,3 @@ -using NSubstitute; using SpatialCollections; using System.Numerics; @@ -6,13 +5,9 @@ namespace QuadtreeTests { public class Tests { - private const float BoundingRadius = 1.0f; - private class TestEntity(Vector2 position) { public Vector2 Position { get; } = position; - - public Rectangle BoundingBox { get; } = new Rectangle(position - Vector2.One, position + Vector2.One); } private List _positions; @@ -26,7 +21,7 @@ namespace QuadtreeTests { _positions = [new Vector2(1.0f, 1.0f), new Vector2(3.0f, 3.0f), new Vector2(5.0f, 8.0f)]; _entities = new(); - _quadtree = new(20, 4, e => e.Position, e => e.BoundingBox); + _quadtree = new(20, 4, e => e.Position); for (int i = 0; i < _positions.Count; i++) { @@ -58,7 +53,7 @@ namespace QuadtreeTests { AddObjectsAndAssertCount(); List result = new(); - _quadtree.Query(new Rectangle(new Vector2(3.5f, 3.5f), new Vector2(10.0f, 10.0f)), result); + _quadtree.Query(new Rectangle(new Vector2(2.5f, 2.5f), new Vector2(10.0f, 10.0f)), result); Assert.That(result.Count, Is.EqualTo(2)); } @@ -66,7 +61,7 @@ namespace QuadtreeTests { for (int i = 0; i < _entities.Count; i++) { - _quadtree.Add(_entities[i], BoundingRadius); + _quadtree.Add(_entities[i]); Assert.That(_quadtree.Count, Is.EqualTo(i + 1)); } }