From d54c349b2ca0954544681bcd78ffdf0fed21475d Mon Sep 17 00:00:00 2001 From: dmatetelki Date: Wed, 9 Nov 2016 16:07:00 +0100 Subject: [PATCH] quad tree: moving definitions after class declaration --- lib/graph/quad_tree.hpp | 309 ++++++++++++++++++++++------------------ 1 file changed, 173 insertions(+), 136 deletions(-) diff --git a/lib/graph/quad_tree.hpp b/lib/graph/quad_tree.hpp index b95f980..a362f51 100644 --- a/lib/graph/quad_tree.hpp +++ b/lib/graph/quad_tree.hpp @@ -27,173 +27,210 @@ struct AABB const P m_center; const typename P::value_type m_halfDimension; - constexpr AABB(const P& center, typename P::value_type halfDimension) - : m_center(center) - , m_halfDimension(halfDimension) {} + constexpr AABB(const P& center, typename P::value_type halfDimension); - bool containsPoint(const P& p) const { - return (std::fabs(m_center.x - p.x) <= m_halfDimension) && - (std::fabs(m_center.y - p.y) <= m_halfDimension); - } - - bool intersectsAABB(const AABB& other) const { - return (std::fabs(m_center.x - other.m_center.x) <= m_halfDimension + other.m_halfDimension) && - (std::fabs(m_center.y - other.m_center.y) <= m_halfDimension + other.m_halfDimension); - } + bool containsPoint(const P& p) const; + bool intersectsAABB(const AABB& other) const; }; template class QuadTree { public: - QuadTree(const AABB

& boundary) - : m_boundary(boundary) - , m_points() - , m_northWest(0) - , m_northEast(0) - , m_southWest(0) - , m_southEast(0) { m_points.reserve(m_node_capacity); } - - QuadTree(const QuadTree& other) - : m_boundary (other.m_boundary) - , m_points (other.m_points) - , m_northWest(other.m_northWest) - , m_northEast(other.m_northEast) - , m_southWest(other.m_southWest) - , m_southEast(other.m_southEast) {} - - QuadTree(QuadTree&& other) - : m_boundary (std::move(other.m_boundary)) - , m_points (std::move(other.m_points)) - , m_northWest(std::move(other.m_northWest)) - , m_northEast(std::move(other.m_northEast)) - , m_southWest(std::move(other.m_southWest)) - , m_southEast(std::move(other.m_southEast)) {} - + QuadTree(const AABB

& boundary); + QuadTree(const QuadTree& other); + QuadTree(QuadTree&& other); QuadTree& operator=(const QuadTree other) { swap(other); return *this; } - void swap(QuadTree& other) { - std::swap(m_boundary, other.m_boundary); - std::swap(m_points, other.m_points); - std::swap(m_northWest, other.m_northWest); - std::swap(m_northEast, other.m_northEast); - std::swap(m_southWest, other.m_southWest); - std::swap(m_southEast, other.m_southEast); - } + void swap(QuadTree& other); + ~QuadTree(); - ~QuadTree() { - delete m_northWest; - delete m_northEast; - delete m_southWest; - delete m_southEast; - } + bool insert(const P& p); - bool insert(const P& p) { - // Ignore objects that do not belong in this quad tree - if (!m_boundary.containsPoint(p)) - return false; // object cannot be added - - // If there is space in this quad tree, add the object here - if (m_points.size() < m_node_capacity) { - m_points.push_back(p); - return true; - } - - // Otherwise, subdivide and then add the point to whichever node will accept it - if (m_northWest == 0) - subdivide(); - - if (m_northWest->insert(p)) return true; - if (m_northEast->insert(p)) return true; - if (m_southWest->insert(p)) return true; - if (m_southEast->insert(p)) return true; - - // Otherwise, the point cannot be inserted for some unknown reason (this should never happen) - assert(!"Programming error"); - return false; - } + std::vector

queryRange(const AABB

& range) const; + std::vector

points() const; - // create four children that fully divide this quad into four quads of equal area - void subdivide() { - const typename P::value_type h = m_boundary.m_halfDimension / 2; - const typename P::value_type x = m_boundary.m_center.x; - const typename P::value_type y = m_boundary.m_center.y; - m_northWest = new QuadTree

(AABB

(P(x - h, y - h), h)); - m_northEast = new QuadTree

(AABB

(P(x + h, y - h), h)); - m_southWest = new QuadTree

(AABB

(P(x - h, y + h), h)); - m_southEast = new QuadTree

(AABB

(P(x + h, y + h), h)); - } +private: + void subdivide(); + + // Arbitrary constant to indicate how many elements can be stored in this quad tree node + static const std::size_t m_node_capacity = 4; + + // Axis-aligned bounding box stored as a center with half-dimensions + // to represent the boundaries of this quad tree + const AABB

m_boundary; + + // Points in this quad tree node + std::vector

m_points; - std::vector

queryRange(const AABB

& range) const { + // Children + QuadTree* m_northWest; + QuadTree* m_northEast; + QuadTree* m_southWest; + QuadTree* m_southEast; +}; - // Prepare an array of results - std::vector

pointsInRange; - // Automatically abort if the range does not intersect this quad - if (!m_boundary.intersectsAABB(range)) - return pointsInRange; // empty list - // Check objects at this quad level - for (std::size_t p = 0; p < m_points.size(); ++p) - if (range.containsPoint(m_points[p])) - pointsInRange.push_back(m_points[p]); +// AABB implementation - // Terminate here, if there are no children - if (m_northWest == 0) - return pointsInRange; +template +constexpr AABB

::AABB(const P& center, typename P::value_type halfDimension) + : m_center(center) + , m_halfDimension(halfDimension) {} - // Otherwise, add the points from the children - const std::vector

nw = m_northWest->queryRange(range); - pointsInRange.insert(pointsInRange.end(), nw.begin(), nw.end()); - const std::vector

ne = m_northEast->queryRange(range); - pointsInRange.insert(pointsInRange.end(), ne.begin(), ne.end()); - const std::vector

sw = m_southWest->queryRange(range); - pointsInRange.insert(pointsInRange.end(), sw.begin(), sw.end()); - const std::vector

se = m_southEast->queryRange(range); - pointsInRange.insert(pointsInRange.end(), se.begin(), se.end()); +template +bool AABB

::containsPoint(const P& p) const { + return (std::fabs(m_center.x - p.x) <= m_halfDimension) && + (std::fabs(m_center.y - p.y) <= m_halfDimension); +} - return pointsInRange; +template +bool AABB

::intersectsAABB(const AABB& other) const { + return (std::fabs(m_center.x - other.m_center.x) <= m_halfDimension + other.m_halfDimension) && + (std::fabs(m_center.y - other.m_center.y) <= m_halfDimension + other.m_halfDimension); +} + + + +// QuadTree implementation + +template +QuadTree

::QuadTree(const AABB

& boundary) + : m_boundary(boundary) + , m_points() + , m_northWest(0) + , m_northEast(0) + , m_southWest(0) + , m_southEast(0) { + m_points.reserve(m_node_capacity); } - std::vector

points() const { +template +QuadTree

::QuadTree(const QuadTree& other) + : m_boundary (other.m_boundary) + , m_points (other.m_points) + , m_northWest(other.m_northWest) + , m_northEast(other.m_northEast) + , m_southWest(other.m_southWest) + , m_southEast(other.m_southEast) {} - std::vector

retval(m_points.begin(), m_points.end()); +template +QuadTree

::QuadTree(QuadTree&& other) + : m_boundary (std::move(other.m_boundary)) + , m_points (std::move(other.m_points)) + , m_northWest(std::move(other.m_northWest)) + , m_northEast(std::move(other.m_northEast)) + , m_southWest(std::move(other.m_southWest)) + , m_southEast(std::move(other.m_southEast)) {} - // Terminate here, if there are no children - if (m_northWest == 0) - return retval; +template +void QuadTree

::swap(QuadTree& other) { + std::swap(m_boundary, other.m_boundary); + std::swap(m_points, other.m_points); + std::swap(m_northWest, other.m_northWest); + std::swap(m_northEast, other.m_northEast); + std::swap(m_southWest, other.m_southWest); + std::swap(m_southEast, other.m_southEast); +} - // Otherwise, add the points from the children - const std::vector

nw = m_northWest->points(); - retval.insert(retval.end(), nw.begin(), nw.end()); - const std::vector

ne = m_northEast->points(); - retval.insert(retval.end(), ne.begin(), ne.end()); - const std::vector

sw = m_southWest->points(); - retval.insert(retval.end(), sw.begin(), sw.end()); - const std::vector

se = m_southEast->points(); - retval.insert(retval.end(), se.begin(), se.end()); +template +QuadTree

::~QuadTree() { + delete m_northWest; + delete m_northEast; + delete m_southWest; + delete m_southEast; +} - return retval; +template +bool QuadTree

::insert(const P& p) { + // Ignore objects that do not belong in this quad tree + if (!m_boundary.containsPoint(p)) + return false; // object cannot be added + + // If there is space in this quad tree, add the object here + if (m_points.size() < m_node_capacity) { + m_points.push_back(p); + return true; } + // Otherwise, subdivide and then add the point to whichever node will accept it + if (m_northWest == 0) + subdivide(); -private: + if (m_northWest->insert(p)) return true; + if (m_northEast->insert(p)) return true; + if (m_southWest->insert(p)) return true; + if (m_southEast->insert(p)) return true; - // Arbitrary constant to indicate how many elements can be stored in this quad tree node - static const std::size_t m_node_capacity = 4; + // Otherwise, the point cannot be inserted for some unknown reason (this should never happen) + assert(!"Programming error"); + return false; +} - // Axis-aligned bounding box stored as a center with half-dimensions - // to represent the boundaries of this quad tree - const AABB

m_boundary; +// create four children that fully divide this quad into four quads of equal area +template +void QuadTree

:: subdivide() { + const typename P::value_type h = m_boundary.m_halfDimension / 2; + const typename P::value_type x = m_boundary.m_center.x; + const typename P::value_type y = m_boundary.m_center.y; + m_northWest = new QuadTree

(AABB

(P(x - h, y - h), h)); + m_northEast = new QuadTree

(AABB

(P(x + h, y - h), h)); + m_southWest = new QuadTree

(AABB

(P(x - h, y + h), h)); + m_southEast = new QuadTree

(AABB

(P(x + h, y + h), h)); +} - // Points in this quad tree node - std::vector

m_points; +template +std::vector

QuadTree

::queryRange(const AABB

& range) const { - // Children - QuadTree* m_northWest; - QuadTree* m_northEast; - QuadTree* m_southWest; - QuadTree* m_southEast; -}; + // Prepare an array of results + std::vector

pointsInRange; + + // Automatically abort if the range does not intersect this quad + if (!m_boundary.intersectsAABB(range)) + return pointsInRange; // empty list + + // Check objects at this quad level + for (std::size_t p = 0; p < m_points.size(); ++p) + if (range.containsPoint(m_points[p])) + pointsInRange.push_back(m_points[p]); + + // Terminate here, if there are no children + if (m_northWest == 0) + return pointsInRange; + + // Otherwise, add the points from the children + const std::vector

nw = m_northWest->queryRange(range); + pointsInRange.insert(pointsInRange.end(), nw.begin(), nw.end()); + const std::vector

ne = m_northEast->queryRange(range); + pointsInRange.insert(pointsInRange.end(), ne.begin(), ne.end()); + const std::vector

sw = m_southWest->queryRange(range); + pointsInRange.insert(pointsInRange.end(), sw.begin(), sw.end()); + const std::vector

se = m_southEast->queryRange(range); + pointsInRange.insert(pointsInRange.end(), se.begin(), se.end()); + + return pointsInRange; +} + +template +std::vector

QuadTree

::points() const { + std::vector

retval(m_points.begin(), m_points.end()); + + // Terminate here, if there are no children + if (m_northWest == 0) + return retval; + + // Otherwise, add the points from the children + const std::vector

nw = m_northWest->points(); + retval.insert(retval.end(), nw.begin(), nw.end()); + const std::vector

ne = m_northEast->points(); + retval.insert(retval.end(), ne.begin(), ne.end()); + const std::vector

sw = m_southWest->points(); + retval.insert(retval.end(), sw.begin(), sw.end()); + const std::vector

se = m_southEast->points(); + retval.insert(retval.end(), se.begin(), se.end()); + + return retval; +} #endif // QUAD_TREE_HPP