7QuadTree::QuadTree(
float minX,
float minY,
float maxX,
float maxY,
int maxDepth,
int maxPerNode)
8 : rootBounds_(
makeAABB2(minX, minY, maxX, maxY)),
9 maxDepth_(
std::
max(1, maxDepth)),
10 maxPerNode_(
std::
max(1, maxPerNode)) {
11 if (!rootBounds_.
valid() || rootBounds_.
width() <= 0.f || rootBounds_.
height() <= 0.f) {
12 throw Exception(
"QuadTree: bounds must have positive area");
17QuadTree::Node *QuadTree::ensureRoot() {
19 root_ = std::make_unique<Node>();
20 root_->bounds = rootBounds_;
37 if (!
b.valid())
return false;
42 if (!insertInto(*ensureRoot(),
id,
b)) {
44 ensureRoot()->itemIds.push_back(
id);
58 return insert(
id, minX, minY, maxX, maxY);
61void QuadTree::rebuild() {
65 for (
const auto &kv : items_) {
66 if (!insertInto(*root_, kv.first, kv.second)) {
67 root_->itemIds.push_back(kv.first);
72bool QuadTree::insertInto(Node &
node,
int id,
const AABB2 &
bounds) {
75 if (!
node.bounds.intersectsAABB(
bounds))
return false;
81 node.itemIds.push_back(
id);
82 if (
static_cast<int>(
node.itemIds.size()) > maxPerNode_ &&
node.depth < maxDepth_) {
88 for (
int i = 0; i < 4; ++i) {
89 if (
node.children[i] &&
node.children[i]->bounds.containsAABB(
bounds)) {
90 return insertInto(*
node.children[i],
id,
bounds);
93 node.itemIds.push_back(
id);
97void QuadTree::split(Node &
node) {
98 const float mx =
node.bounds.centerX();
99 const float my =
node.bounds.centerY();
100 const AABB2 quads[4] = {
106 for (
int i = 0; i < 4; ++i) {
107 node.children[i] = std::make_unique<Node>();
108 node.children[i]->bounds = quads[i];
109 node.children[i]->depth =
node.depth + 1;
112 std::vector<int> remain;
113 remain.reserve(
node.itemIds.size());
114 for (
int id :
node.itemIds) {
115 auto it = items_.find(
id);
116 if (it == items_.end())
continue;
118 for (
int i = 0; i < 4; ++i) {
119 if (
node.children[i]->bounds.containsAABB(it->second)) {
120 node.children[i]->itemIds.push_back(
id);
125 if (!
placed) remain.push_back(
id);
127 node.itemIds.swap(remain);
130void QuadTree::collect(Node &
node,
const AABB2 *rect,
float cx,
float cy,
float radius,
132 if (rect && !
node.bounds.intersectsAABB(*rect))
return;
133 if (useCircle && !
node.bounds.intersectsCircle(
cx,
cy,
radius))
return;
135 for (
int id :
node.itemIds) {
136 auto it = items_.find(
id);
137 if (it == items_.end())
continue;
138 const AABB2 &
b = it->second;
141 hit =
b.intersectsAABB(*rect);
142 }
else if (useCircle) {
150 if (!
node.isLeaf()) {
151 for (
int i = 0; i < 4; ++i) {
159 collect(*ensureRoot(),
nullptr,
x,
y, 0.f,
false);
166 collect(*ensureRoot(), &rect, 0.f, 0.f, 0.f,
false);
173 collect(*ensureRoot(),
nullptr,
cx,
cy,
radius,
true);
EVENGINE_API_FOUNDATION public API.
bool contains(int id) const
True if the id is currently stored.
bool insert(int id, float minX, float minY, float maxX, float maxY)
Inserts an item AABB; false if out of bounds or id already present.
bool update(int id, float minX, float minY, float maxX, float maxY)
Moves an existing item to a new AABB; false if unknown or out of bounds.
int queryPoint(float x, float y)
Finds items overlapping a point; fills the result buffer.
int queryRect(float minX, float minY, float maxX, float maxY)
Finds items overlapping an AABB; fills the result buffer.
QuadTree(float minX, float minY, float maxX, float maxY, int maxDepth=8, int maxPerNode=8)
Creates a quadtree covering the given 2D bounds.
void clear()
Removes all stored entries.
int queryCircle(float cx, float cy, float radius)
Finds items overlapping a circle; fills the result buffer.
bool remove(int id)
Removes an item by id; false if unknown.
void clear()
Removes all stored entries.
int getCount() const
Number of stored ids.
void addUnique(int id)
Appends id if not already present.
AABB2 makeAABB2(float minX, float minY, float maxX, float maxY)
Builds a normalized 2D AABB (swaps inverted mins/maxes).
Axis-aligned 2D bounding box (min/max inclusive).
bool valid() const
True when min <= max on every axis.
float width() const
Extent along X (max - min).
float height() const
Extent along Y (max - min).