← API Reference Class

template<typename T> asge::spatial::QuadTree

Region quadtree over axis-aligned rectangles, rebuilt from scratch via Reset().

#include <QuadTree.hpp>

Nodes and the blocks holding their elements live in a fixed-size arena, so Insert never allocates from the heap. A node's elements sit in contiguous fixed-size blocks (bounds and values in separate arrays) chained together when the node overflows. An element sits in the deepest node whose quadrant fully contains it, so elements straddling a split line stay in the parent. Elements cannot be removed individually.

Static Private Attributes

#static constexpr std::size_t s_BlockCapacity = QuadNode::s_MaxNofElementsprivate
#static constexpr std::size_t s_QueryStackSize = 3 * QuadNode::s_MaxDepth + 4private

Private Attributes

#mem::ArenaAllocator m_Allocatorprivate
#QuadNode * m_Root { nullptr }private
#QuadBlockHeader * m_FreeBlocks { nullptr }private
#std::size_t m_MaxElementsprivate
#std::size_t m_Size { 0 }private
#std::size_t m_NodeCount { 0 }private
#std::uint16_t m_MaxDepth { 0 }private
#bool m_Exhausted { false }private

Private Member Functions

#QuadNode * createNode(math::Rect const &inBounds, std::uint16_t inDepth) noexceptprivate

Allocates a node from the arena, or returns nullptr if the arena is full.

#Block * acquireBlock() noexceptprivate

Returns an empty block from the free list or the arena, or nullptr if the arena is full.

#void releaseBlock(QuadBlockHeader *inBlock) noexceptprivate

Pushes the block on the free list for later reuse.

#bool addElementToNode(QuadNode *inNode, T inElement, math::Rect const &inBounds) noexceptprivate

Appends the element to the node's last block, chaining a new one if full.

False if the arena is full.

#int getQuadrantIndex(QuadNode const *inNode, math::Rect const &inBounds) const noexceptprivate

Returns 0..3 if the input rect fits one quadrant, -1 otherwise.

#bool insert(QuadNode *inNode, T inElement, math::Rect const &inBounds) noexceptprivate

Recursively inserts an element into the tree.

False only if the arena has no room for it.

#void subdivide(QuadNode *inNode) noexceptprivate

Splits the leaf into 4 children and moves the elements that fit a quadrant down.

Elements that straddle a split line, or whose child could not get a block, stay in the node. If the arena cannot hold 4 children the node is left as a leaf.

#template<typename NodePred, typename ElemPred, typename Visitor> bool traverse(QuadNode const *inNode, NodePred inNodePred, ElemPred inElemPred, Visitor &inVisitor) constprivate

Visits the elements of the subtree for which inElemPred holds, using an explicit fixed-size stack.

A child is entered only if inNodePred holds for its bounds, so the node predicate must be true whenever the element predicate is true for something inside the node. Nodes are visited in the same order as a recursive walk (parent, then children 0..3). Returns false once the visitor asked to stop.

#void nearest(QuadNode const *inNode, math::Float2 const &inPoint, float &ioBestDistance, std::optional< T > &ioBest) const noexceptprivate

Branch-and-bound step of Nearest: updates the best element found so far in the subtree.

#template<typename Visitor> bool visitPairs(QuadNode const *inNode, std::vector< Entry > &ioScratch, std::size_t inAncBegin, std::size_t inAncEnd, Visitor &inVisitor) constprivate

Reports the overlapping pairs found in the subtree of inNode.

ioScratch[inAncBegin, inAncEnd) holds the ancestor elements that overlap inNode and is the end of the buffer on entry; children append their own filtered list after it and truncate back once visited. Returns false once the visitor asked to stop.

Public Member Functions

#QuadTree(std::size_t inArenaBytes, std::size_t inMaxElements)

Reserves an arena of inArenaBytes for nodes and element blocks, and caps the tree at inMaxElements.

#BoolResult Reset(float inW, float inH) noexcept

Drops every element and node, then starts a new empty tree covering [0,inW]x[0,inH].

Errors with invalid_argument unless both sizes are finite and positive, and with no_buffer_space if the arena cannot even hold the root. After an error the tree is empty and uninitialised, so Insert fails until a Reset succeeds.

#BoolResult Reset(math::Rect const &inBounds) noexcept

Like Reset(inW, inH), but the new tree covers inBounds, which may start away from the origin.

#BoolResult Insert(T inElement, math::Rect const &inBounds) noexcept

Inserts an element with the given bounds.

Errors if Reset() was never called, the bounds are not fully inside the root, the element budget is exhausted, or the arena has no room left for the element. A failed insert leaves the tree valid and unchanged in content.

#std::size_t Size() const noexcept

Number of elements currently stored.

#template<typename Visitor> void Query(math::Rect const &inRect, Visitor &&inVisitor) const

Calls inVisitor(value) for every element overlapping inRect.

The visitor returns true to continue or false to stop the traversal early. Overlap is strict (AabbOverlap), so rects that only touch are not reported.

#template<typename Visitor> void Query(math::Float2 const &inPoint, Visitor &&inVisitor) const

Calls inVisitor(value) for every element whose rect strictly contains inPoint.

Same visitor protocol as the Rect overload; a point exactly on an element's edge is not inside it.

#template<typename Visitor> void Query(math::Circle const &inCircle, Visitor &&inVisitor) const

Calls inVisitor(value) for every element whose rect is closer to the circle's center than its radius.

Same visitor protocol as the Rect overload; an element that only touches the circle is not reported.

#std::optional< T > Nearest(math::Float2 const &inPoint) const noexcept

Returns the value of the element whose rect is closest to inPoint.

The distance is 0 for a point inside or on an element. When several elements are equally close, which one is returned is unspecified. Empty if the tree is empty or uninitialised.

#std::size_t NodeCount() const noexcept

Number of nodes created since the last Reset, root included.

#std::uint16_t MaxDepthReached() const noexcept

Depth of the deepest node created since the last Reset (the root is 0).

#bool ArenaExhausted() const noexcept

True if an arena allocation failed since the last Reset.

It means the tree degraded: some leaf could not split, or an Insert was refused for lack of memory. Compare ArenaUsed() with ArenaCapacity() to size the arena.

#std::size_t ArenaUsed() const noexcept

Arena bytes consumed so far by nodes and element blocks.

#std::size_t ArenaCapacity() const noexcept

Total arena size in bytes.

#template<typename Visitor> void ForEachPair(Visitor &&inVisitor) const

Calls inVisitor(a, b) once for every unordered pair of overlapping elements.

Never pairs an element with itself and never reports a pair twice; the order of the pairs and of the two values within a pair is unspecified. The visitor returns true to continue or false to stop early. Allocates one scratch buffer per call.