载入中...
搜索中...
未找到
BSPTree2D.cpp
浏览该文件的文档.
1#include "spatial/BSPTree2D.h"
2
3#include "common/Exception.h"
4
5namespace eve::spatial {
6
7BSPTree2D::BSPTree2D(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("BSPTree2D: bounds must have positive area");
13 }
14 ensureRoot();
15}
16
17BSPTree2D::Node *BSPTree2D::ensureRoot() {
18 if (!root_) {
19 root_ = std::make_unique<Node>();
20 root_->bounds = rootBounds_;
21 root_->depth = 0;
22 root_->axis = 0;
23 }
24 return root_.get();
25}
26
28 items_.clear();
29 results_.clear();
30 root_.reset();
31 ensureRoot();
32}
33
34bool BSPTree2D::contains(int id) const { return items_.find(id) != items_.end(); }
35
36bool BSPTree2D::insert(int id, float minX, float minY, float maxX, float maxY) {
37 AABB2 b = makeAABB2(minX, minY, maxX, maxY);
38 if (!b.valid()) return false;
39 if (contains(id)) remove(id);
40 items_[id] = b;
41 if (!insertInto(*ensureRoot(), id, b)) {
42 ensureRoot()->itemIds.push_back(id);
43 }
44 return true;
45}
46
47bool BSPTree2D::remove(int id) {
48 if (!contains(id)) return false;
49 items_.erase(id);
50 rebuild();
51 return true;
52}
53
54bool BSPTree2D::update(int id, float minX, float minY, float maxX, float maxY) {
55 if (!contains(id)) return false;
56 return insert(id, minX, minY, maxX, maxY);
57}
58
59void BSPTree2D::rebuild() {
60 results_.clear();
61 root_.reset();
62 ensureRoot();
63 for (const auto &kv : items_) {
64 if (!insertInto(*root_, kv.first, kv.second)) {
65 root_->itemIds.push_back(kv.first);
66 }
67 }
68}
69
70bool BSPTree2D::insertInto(Node &node, int id, const AABB2 &bounds) {
71 if (!node.bounds.containsAABB(bounds) && node.depth == 0) {
72 if (!node.bounds.intersectsAABB(bounds)) return false;
73 } else if (node.depth > 0 && !node.bounds.containsAABB(bounds)) {
74 return false;
75 }
76
77 if (node.isLeaf()) {
78 node.itemIds.push_back(id);
79 if (static_cast<int>(node.itemIds.size()) > maxPerNode_ && node.depth < maxDepth_) {
80 split(node);
81 }
82 return true;
83 }
84
85 // Fully on one side of the split plane?
86 if (node.axis == 0) {
87 if (bounds.maxX <= node.split && node.left) {
88 return insertInto(*node.left, id, bounds);
89 }
90 if (bounds.minX >= node.split && node.right) {
91 return insertInto(*node.right, id, bounds);
92 }
93 } else {
94 if (bounds.maxY <= node.split && node.left) {
95 return insertInto(*node.left, id, bounds);
96 }
97 if (bounds.minY >= node.split && node.right) {
98 return insertInto(*node.right, id, bounds);
99 }
100 }
101 node.itemIds.push_back(id);
102 return true;
103}
104
105void BSPTree2D::split(Node &node) {
106 node.axis = node.depth % 2;
107 node.split = (node.axis == 0) ? node.bounds.centerX() : node.bounds.centerY();
108
109 node.left = std::make_unique<Node>();
110 node.right = std::make_unique<Node>();
111 node.left->depth = node.depth + 1;
112 node.right->depth = node.depth + 1;
113 node.left->axis = node.left->depth % 2;
114 node.right->axis = node.right->depth % 2;
115
116 if (node.axis == 0) {
117 node.left->bounds =
118 makeAABB2(node.bounds.minX, node.bounds.minY, node.split, node.bounds.maxY);
119 node.right->bounds =
120 makeAABB2(node.split, node.bounds.minY, node.bounds.maxX, node.bounds.maxY);
121 } else {
122 node.left->bounds =
123 makeAABB2(node.bounds.minX, node.bounds.minY, node.bounds.maxX, node.split);
124 node.right->bounds =
125 makeAABB2(node.bounds.minX, node.split, node.bounds.maxX, node.bounds.maxY);
126 }
127
128 std::vector<int> remain;
129 remain.reserve(node.itemIds.size());
130 for (int id : node.itemIds) {
131 auto it = items_.find(id);
132 if (it == items_.end()) continue;
133 const AABB2 &b = it->second;
134 if (node.axis == 0) {
135 if (b.maxX <= node.split) {
136 node.left->itemIds.push_back(id);
137 } else if (b.minX >= node.split) {
138 node.right->itemIds.push_back(id);
139 } else {
140 remain.push_back(id);
141 }
142 } else {
143 if (b.maxY <= node.split) {
144 node.left->itemIds.push_back(id);
145 } else if (b.minY >= node.split) {
146 node.right->itemIds.push_back(id);
147 } else {
148 remain.push_back(id);
149 }
150 }
151 }
152 node.itemIds.swap(remain);
153}
154
155void BSPTree2D::collect(Node &node, const AABB2 *rect, float cx, float cy, float radius,
156 bool useCircle) {
157 if (rect && !node.bounds.intersectsAABB(*rect)) return;
158 if (useCircle && !node.bounds.intersectsCircle(cx, cy, radius)) return;
159
160 for (int id : node.itemIds) {
161 auto it = items_.find(id);
162 if (it == items_.end()) continue;
163 const AABB2 &b = it->second;
164 bool hit = false;
165 if (rect) {
166 hit = b.intersectsAABB(*rect);
167 } else if (useCircle) {
168 hit = b.intersectsCircle(cx, cy, radius);
169 } else {
170 hit = b.containsPoint(cx, cy);
171 }
172 if (hit) results_.addUnique(id);
173 }
174
175 if (!node.isLeaf()) {
176 if (node.left) collect(*node.left, rect, cx, cy, radius, useCircle);
177 if (node.right) collect(*node.right, rect, cx, cy, radius, useCircle);
178 }
179}
180
181int BSPTree2D::queryPoint(float x, float y) {
182 results_.clear();
183 collect(*ensureRoot(), nullptr, x, y, 0.f, false);
184 return results_.getCount();
185}
186
187int BSPTree2D::queryRect(float minX, float minY, float maxX, float maxY) {
188 results_.clear();
189 AABB2 rect = makeAABB2(minX, minY, maxX, maxY);
190 collect(*ensureRoot(), &rect, 0.f, 0.f, 0.f, false);
191 return results_.getCount();
192}
193
194int BSPTree2D::queryCircle(float cx, float cy, float radius) {
195 results_.clear();
196 if (radius < 0.f) radius = 0.f;
197 collect(*ensureRoot(), nullptr, cx, cy, radius, true);
198 return results_.getCount();
199}
200
201} // namespace eve::spatial
float cx
Definition CardTypes.cpp:31
float cy
Definition CardTypes.cpp:32
std::string id
int y
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
uint32_t b
bool update(int id, float minX, float minY, float maxX, float maxY)
Definition BSPTree2D.cpp:54
BSPTree2D(float minX, float minY, float maxX, float maxY, int maxDepth=12, int maxPerNode=8)
Definition BSPTree2D.cpp:7
int queryPoint(float x, float y)
bool contains(int id) const
Definition BSPTree2D.cpp:34
int queryRect(float minX, float minY, float maxX, float maxY)
int queryCircle(float cx, float cy, float radius)
bool insert(int id, float minX, float minY, float maxX, float maxY)
Definition BSPTree2D.cpp:36
int getCount() const
Definition QueryIds.h:24
void addUnique(int id)
Definition QueryIds.h:15
NodeDesc node(std::string id, std::vector< NodeDesc > children, std::string name)
Definition NodeDesc.cpp:214
AABB2 makeAABB2(float minX, float minY, float maxX, float maxY)
Definition Bounds.h:85
bool valid() const
Definition Bounds.h:20
float width() const
Definition Bounds.h:15
float height() const
Definition Bounds.h:16