Loading...
Searching...
No Matches
Quadtree-Impl.hpp
Go to the documentation of this file.
1// This code is based on Jet framework.
2// Copyright (c) 2018 Doyub Kim
3// CubbyFlow is voxel-based fluid simulation engine for computer games.
4// Copyright (c) 2020 CubbyFlow Team
5// Core Part: Chris Ohk, Junwoo Hwang, Jihong Sin, Seungwoo Yoo
6// AI Part: Dongheon Cho, Minseo Kim
7// We are making my contributions/submissions to this project solely in our
8// personal capacity and are not conveying any rights to any intellectual
9// property of any third parties.
10
11#ifndef CUBBYFLOW_QUADTREE_IMPL_HPP
12#define CUBBYFLOW_QUADTREE_IMPL_HPP
13
14#include <numeric>
15#include <stack>
16
17namespace CubbyFlow
18{
19template <typename T>
20bool Quadtree<T>::Node::IsLeaf() const
21{
22 return firstChild == std::numeric_limits<size_t>::max();
23}
24
25template <typename T>
26void Quadtree<T>::Build(const std::vector<T>& items, const BoundingBox2D& bound,
28 size_t maxDepth)
29{
30 // Reset items
31 m_maxDepth = maxDepth;
32 m_items = items;
33 m_nodes.clear();
34
35 // Normalize bounding box
36 m_bbox = bound;
37 const double maxEdgeLen = std::max(m_bbox.Width(), m_bbox.Height());
38 m_bbox.upperCorner =
39 m_bbox.lowerCorner + Vector2D{ maxEdgeLen, maxEdgeLen };
40
41 // Build
42 m_nodes.resize(1);
43 m_nodes[0].items.resize(m_items.size());
44 std::iota(m_nodes[0].items.begin(), m_nodes[0].items.end(), ZERO_SIZE);
45
46 Build(0, 1, m_bbox, testFunc);
47}
48
49template <typename T>
51{
52 m_maxDepth = 1;
53 m_items.clear();
54 m_nodes.clear();
55 m_bbox = BoundingBox2D();
56}
57
58template <typename T>
60 const Vector2D& pt,
62{
64 best.distance = std::numeric_limits<double>::max();
65 best.item = nullptr;
66
67 // Prepare to traverse quadtree
68 std::stack<std::pair<const Node*, BoundingBox2D>> todo;
69
70 // Traverse quadtree nodes
71 const Node* node = m_nodes.data();
72 BoundingBox2D bound = m_bbox;
73 while (node != nullptr)
74 {
75 if (node->IsLeaf())
76 {
77 std::ranges::for_each(
78 node->items, [this, &distanceFunc, &pt, &best](size_t itemIdx) {
79 const double distance = distanceFunc(m_items[itemIdx], pt);
80
81 if (distance < best.distance)
82 {
83 best.distance = distance;
84 best.item = &m_items[itemIdx];
85 }
86 });
87 }
88 else
89 {
90 const double bestDistSqr = best.distance * best.distance;
91
92 typedef std::tuple<const Node*, double, BoundingBox2D> NodeDistBox;
93 std::array<NodeDistBox, 4> childDistSqrPairs;
94 const auto MidPoint = bound.MidPoint();
95 for (int i = 0; i < 4; ++i)
96 {
97 const Node* child = &m_nodes[node->firstChild + i];
98 const auto childBound =
99 BoundingBox2D{ bound.Corner(i), MidPoint };
100 Vector2D cp = childBound.Clamp(pt);
102
104 std::make_tuple(child, distMinSqr, childBound);
105 }
107 [](const NodeDistBox& a, const NodeDistBox& b) {
108 return std::get<1>(a) > std::get<1>(b);
109 });
110
111 std::ranges::for_each(
114 if (std::get<1>(childPair) < bestDistSqr)
115 {
116 todo.emplace(std::get<0>(childPair),
117 std::get<2>(childPair));
118 }
119 });
120 }
121
122 // Grab next node to process from todo stack
123 if (todo.empty())
124 {
125 break;
126 }
127
128 node = todo.top().first;
129 bound = todo.top().second;
130 todo.pop();
131 }
132
133 return best;
134}
135
136template <typename T>
139{
140 return Intersects(box, testFunc, 0, m_bbox);
141}
142
143template <typename T>
146{
147 return Intersects(ray, testFunc, 0, m_bbox);
148}
149
150template <typename T>
154{
155 ForEachIntersectingItem(box, testFunc, visitorFunc, 0, m_bbox);
156}
157
158template <typename T>
162{
163 ForEachIntersectingItem(ray, testFunc, visitorFunc, 0, m_bbox);
164}
165
166template <typename T>
168 const Ray2D& ray, const GetRayIntersectionFunc2<T>& testFunc) const
169{
171 best.distance = std::numeric_limits<double>::max();
172 best.item = nullptr;
173
174 return ClosestIntersection(ray, testFunc, 0, m_bbox, best);
175}
176
177template <typename T>
179{
180 return m_items.begin();
181}
182
183template <typename T>
185{
186 return m_items.end();
187}
188
189template <typename T>
191{
192 return m_items.begin();
193}
194
195template <typename T>
197{
198 return m_items.end();
199}
200
201template <typename T>
203{
204 return m_items.size();
205}
206
207template <typename T>
208const T& Quadtree<T>::GetItem(size_t i) const
209{
210 return m_items[i];
211}
212
213template <typename T>
215{
216 return m_nodes.size();
217}
218
219template <typename T>
220const std::vector<size_t>& Quadtree<T>::GetItemsAtNode(size_t nodeIdx) const
221{
222 return m_nodes[nodeIdx].items;
223}
224
225template <typename T>
226size_t Quadtree<T>::GetChildIndex(size_t nodeIdx, size_t childIdx) const
227{
228 return m_nodes[nodeIdx].firstChild + childIdx;
229}
230
231template <typename T>
233{
234 return m_bbox;
235}
236
237template <typename T>
239{
240 return m_maxDepth;
241}
242
243template <typename T>
244void Quadtree<T>::Build(size_t nodeIdx, size_t depth,
245 const BoundingBox2D& bound,
247{
248 if (depth >= m_maxDepth || m_nodes[nodeIdx].items.empty())
249 {
250 return;
251 }
252
253 const size_t firstChild = m_nodes[nodeIdx].firstChild = m_nodes.size();
254 m_nodes.resize(m_nodes[nodeIdx].firstChild + 4);
255
256 BoundingBox2D bboxPerNode[4];
257
258 for (int i = 0; i < 4; ++i)
259 {
260 bboxPerNode[i] = BoundingBox2D{ bound.Corner(i), bound.MidPoint() };
261 }
262
263 auto& currentItems = m_nodes[nodeIdx].items;
264 for (size_t i = 0; i < currentItems.size(); ++i)
265 {
266 size_t currentItem = currentItems[i];
267 for (int j = 0; j < 4; ++j)
268 {
269 if (testFunc(m_items[currentItem], bboxPerNode[j]))
270 {
271 m_nodes[firstChild + j].items.push_back(currentItem);
272 }
273 }
274 }
275
276 // Remove non-leaf data
277 currentItems.clear();
278
279 // Refine
280 for (int i = 0; i < 4; ++i)
281 {
282 Build(firstChild + i, depth + 1, bboxPerNode[i], testFunc);
283 }
284}
285
286template <typename T>
287bool Quadtree<T>::Intersects(const BoundingBox2D& box,
288 const BoxIntersectionTestFunc2<T>& testFunc,
289 size_t nodeIdx, const BoundingBox2D& bound) const
290{
291 if (!box.Overlaps(bound))
292 {
293 return false;
294 }
295
296 const Node& node = m_nodes[nodeIdx];
297
298 if (node.items.size() > 0)
299 {
300 for (size_t itemIdx : node.items)
301 {
302 if (testFunc(m_items[itemIdx], box))
303 {
304 return true;
305 }
306 }
307 }
308
309 if (node.firstChild != std::numeric_limits<size_t>::max())
310 {
311 for (int i = 0; i < 4; ++i)
312 {
313 if (Intersects(box, testFunc, node.firstChild + i,
314 BoundingBox2D{ bound.Corner(i), bound.MidPoint() }))
315 {
316 return true;
317 }
318 }
319 }
320
321 return false;
322}
323
324template <typename T>
325bool Quadtree<T>::Intersects(const Ray2D& ray,
326 const RayIntersectionTestFunc2<T>& testFunc,
327 size_t nodeIdx, const BoundingBox2D& bound) const
328{
329 if (!bound.Intersects(ray))
330 {
331 return false;
332 }
333
334 const Node& node = m_nodes[nodeIdx];
335
336 if (node.items.size() > 0)
337 {
338 for (size_t itemIdx : node.items)
339 {
340 if (testFunc(m_items[itemIdx], ray))
341 {
342 return true;
343 }
344 }
345 }
346
347 if (node.firstChild != std::numeric_limits<size_t>::max())
348 {
349 for (int i = 0; i < 4; ++i)
350 {
351 if (Intersects(ray, testFunc, node.firstChild + i,
352 BoundingBox2D{ bound.Corner(i), bound.MidPoint() }))
353 {
354 return true;
355 }
356 }
357 }
358
359 return false;
360}
361
362template <typename T>
363void Quadtree<T>::ForEachIntersectingItem(
364 const BoundingBox2D& box, const BoxIntersectionTestFunc2<T>& testFunc,
365 const IntersectionVisitorFunc<T>& visitorFunc, size_t nodeIdx,
366 const BoundingBox2D& bound) const
367{
368 if (!box.Overlaps(bound))
369 {
370 return;
371 }
372
373 const Node& node = m_nodes[nodeIdx];
374
375 if (node.items.size() > 0)
376 {
377 for (size_t itemIdx : node.items)
378 {
379 if (testFunc(m_items[itemIdx], box))
380 {
381 visitorFunc(m_items[itemIdx]);
382 }
383 }
384 }
385
386 if (node.firstChild != std::numeric_limits<size_t>::max())
387 {
388 for (int i = 0; i < 4; ++i)
389 {
390 ForEachIntersectingItem(
391 box, testFunc, visitorFunc, node.firstChild + i,
392 BoundingBox2D{ bound.Corner(i), bound.MidPoint() });
393 }
394 }
395}
396
397template <typename T>
398void Quadtree<T>::ForEachIntersectingItem(
399 const Ray2D& ray, const RayIntersectionTestFunc2<T>& testFunc,
400 const IntersectionVisitorFunc<T>& visitorFunc, size_t nodeIdx,
401 const BoundingBox2D& bound) const
402{
403 if (!bound.Intersects(ray))
404 {
405 return;
406 }
407
408 const Node& node = m_nodes[nodeIdx];
409
410 if (node.items.size() > 0)
411 {
412 for (size_t itemIdx : node.items)
413 {
414 if (testFunc(m_items[itemIdx], ray))
415 {
416 visitorFunc(m_items[itemIdx]);
417 }
418 }
419 }
420
421 if (node.firstChild != std::numeric_limits<size_t>::max())
422 {
423 for (int i = 0; i < 4; ++i)
424 {
425 ForEachIntersectingItem(
426 ray, testFunc, visitorFunc, node.firstChild + i,
427 BoundingBox2D{ bound.Corner(i), bound.MidPoint() });
428 }
429 }
430}
431
432template <typename T>
433ClosestIntersectionQueryResult2<T> Quadtree<T>::ClosestIntersection(
434 const Ray2D& ray, const GetRayIntersectionFunc2<T>& testFunc,
435 size_t nodeIdx, const BoundingBox2D& bound,
436 ClosestIntersectionQueryResult2<T> best) const
437{
438 if (!bound.Intersects(ray))
439 {
440 return best;
441 }
442
443 const Node& node = m_nodes[nodeIdx];
444
445 if (node.items.size() > 0)
446 {
447 for (size_t itemIdx : node.items)
448 {
449 double dist = testFunc(m_items[itemIdx], ray);
450 if (dist < best.distance)
451 {
452 best.distance = dist;
453 best.item = &m_items[itemIdx];
454 }
455 }
456 }
457
458 if (node.firstChild != std::numeric_limits<size_t>::max())
459 {
460 for (int i = 0; i < 4; ++i)
461 {
462 best = ClosestIntersection(
463 ray, testFunc, node.firstChild + i,
464 BoundingBox2D{ bound.Corner(i), bound.MidPoint() }, best);
465 }
466 }
467
468 return best;
469}
470} // namespace CubbyFlow
471
472#endif
N-D axis-aligned bounding box class.
Definition BoundingBox.hpp:47
VectorType MidPoint() const
Returns the mid-point of this box.
Definition BoundingBox-Impl.hpp:189
VectorType Corner(size_t idx) const
Returns corner position. Index starts from x-first order.
Definition BoundingBox-Impl.hpp:235
ValueType DistanceSquaredTo(const MatrixExpression< T, R, C, E > &other) const
Returns the squared distance to the other vector.
Definition Matrix.hpp:30
Iterator begin()
Definition Matrix-Impl.hpp:272
Pointer data()
Definition Matrix-Impl.hpp:298
Iterator end()
Definition Matrix-Impl.hpp:285
Generic quadtree data structure.
Definition Quadtree.hpp:31
void Build(const std::vector< T > &items, const BoundingBox2D &bound, const BoxIntersectionTestFunc2< T > &testFunc, size_t maxDepth)
Definition Quadtree-Impl.hpp:26
Iterator begin()
Returns the begin iterator of the item.
Definition Quadtree-Impl.hpp:178
NearestNeighborQueryResult2< T > Nearest(const Vector2D &pt, const NearestNeighborDistanceFunc2< T > &distanceFunc) const override
Definition Quadtree-Impl.hpp:59
typename ContainerType::iterator Iterator
Definition Quadtree.hpp:34
void Clear()
Clears all the contents of this instance.
Definition Quadtree-Impl.hpp:50
typename ContainerType::const_iterator ConstIterator
Definition Quadtree.hpp:35
Iterator end()
Returns the end iterator of the item.
Definition Quadtree-Impl.hpp:184
Class for N-D ray.
Definition Ray.hpp:26
Definition pybind11Utils.hpp:22
constexpr size_t ZERO_SIZE
Zero size_t.
Definition Constants.hpp:20
BoundingBox2< double > BoundingBox2D
Definition BoundingBox.hpp:159
Matrix< T, Rows, 1 > Vector
Definition Matrix.hpp:719