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.cloear();
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 for (size_t itemIdx : node->items)
78 {
79 double d = distanceFunc(m_items[itemIdx], pt);
80 if (d < best.distance)
81 {
82 best.distance = d;
83 best.item = &m_items[itemIdx];
84 }
85 }
86
87 // Grab next node to process from todo stack
88 if (todo.empty())
89 {
90 break;
91 }
92
93 node = todo.top().first;
94 bound = todo.top().second;
95 todo.pop();
96 }
97 else
98 {
99 const double bestDistSqr = best.distance * best.distance;
100
101 typedef std::tuple<const Node*, double, BoundingBox2D> NodeDistBox;
102 std::array<NodeDistBox, 4> childDistSqrPairs;
103 const auto MidPoint = bound.MidPoint();
104 for (int i = 0; i < 4; ++i)
105 {
106 const Node* child = &m_nodes[node->firstChild + i];
107 const auto childBound =
108 BoundingBox2D{ bound.Corner(i), MidPoint };
109 Vector2D cp = childBound.Clamp(pt);
111
113 std::make_tuple(child, distMinSqr, childBound);
114 }
116 [](const NodeDistBox& a, const NodeDistBox& b) {
117 return std::get<1>(a) > std::get<1>(b);
118 });
119
120 for (int i = 0; i < 4; ++i)
121 {
122 const auto& childPair = childDistSqrPairs[i];
123 if (std::get<1>(childPair) < bestDistSqr)
124 {
125 todo.emplace(std::get<0>(childPair),
126 std::get<2>(childPair));
127 }
128 }
129
130 if (todo.empty())
131 {
132 break;
133 }
134
135 node = todo.top().first;
136 bound = todo.top().second;
137 todo.pop();
138 }
139 }
140
141 return best;
142}
143
144template <typename T>
147{
148 return Intersects(box, testFunc, 0, m_bbox);
149}
150
151template <typename T>
154{
155 return Intersects(ray, testFunc, 0, m_bbox);
156}
157
158template <typename T>
162{
163 ForEachIntersectingItem(box, testFunc, visitorFunc, 0, m_bbox);
164}
165
166template <typename T>
170{
171 ForEachIntersectingItem(ray, testFunc, visitorFunc, 0, m_bbox);
172}
173
174template <typename T>
176 const Ray2D& ray, const GetRayIntersectionFunc2<T>& testFunc) const
177{
179 best.distance = std::numeric_limits<double>::max();
180 best.item = nullptr;
181
182 return ClosestIntersection(ray, testFunc, 0, m_bbox, best);
183}
184
185template <typename T>
187{
188 return m_items.begin();
189}
190
191template <typename T>
193{
194 return m_items.end();
195}
196
197template <typename T>
199{
200 return m_items.begin();
201}
202
203template <typename T>
205{
206 return m_items.end();
207}
208
209template <typename T>
211{
212 return m_items.size();
213}
214
215template <typename T>
216const T& Quadtree<T>::GetItem(size_t i) const
217{
218 return m_items[i];
219}
220
221template <typename T>
223{
224 return m_nodes.size();
225}
226
227template <typename T>
228const std::vector<size_t>& Quadtree<T>::GetItemsAtNode(size_t nodeIdx) const
229{
230 return m_nodes[nodeIdx].items;
231}
232
233template <typename T>
234size_t Quadtree<T>::GetChildIndex(size_t nodeIdx, size_t childIdx) const
235{
236 return m_nodes[nodeIdx].firstChild + childIdx;
237}
238
239template <typename T>
241{
242 return m_bbox;
243}
244
245template <typename T>
247{
248 return m_maxDepth;
249}
250
251template <typename T>
252void Quadtree<T>::Build(size_t nodeIdx, size_t depth,
253 const BoundingBox2D& bound,
255{
256 if (depth < m_maxDepth && !m_nodes[nodeIdx].items.empty())
257 {
258 const size_t firstChild = m_nodes[nodeIdx].firstChild = m_nodes.size();
259 m_nodes.resize(m_nodes[nodeIdx].firstChild + 4);
260
262
263 for (int i = 0; i < 4; ++i)
264 {
265 bboxPerNode[i] = BoundingBox2D{ bound.Corner(i), bound.MidPoint() };
266 }
267
268 auto& currentItems = m_nodes[nodeIdx].items;
269 for (size_t i = 0; i < currentItems.size(); ++i)
270 {
271 size_t currentItem = currentItems[i];
272 for (int j = 0; j < 4; ++j)
273 {
274 if (testFunc(m_items[currentItem], bboxPerNode[j]))
275 {
276 m_nodes[firstChild + j].items.push_back(currentItem);
277 }
278 }
279 }
280
281 // Remove non-leaf data
282 currentItems.clear();
283
284 // Refine
285 for (int i = 0; i < 4; ++i)
286 {
287 Build(firstChild + i, depth + 1, bboxPerNode[i], testFunc);
288 }
289 }
290}
291
292template <typename T>
295 size_t nodeIdx, const BoundingBox2D& bound) const
296{
297 if (!box.Overlaps(bound))
298 {
299 return false;
300 }
301
302 const Node& node = m_nodes[nodeIdx];
303
304 if (node.items.size() > 0)
305 {
306 for (size_t itemIdx : node.items)
307 {
308 if (testFunc(m_items[itemIdx], box))
309 {
310 return true;
311 }
312 }
313 }
314
315 if (node.firstChild != std::numeric_limits<size_t>::max())
316 {
317 for (int i = 0; i < 4; ++i)
318 {
319 if (Intersects(box, testFunc, node.firstChild + i,
320 BoundingBox2D{ bound.Corner(i), bound.MidPoint() }))
321 {
322 return true;
323 }
324 }
325 }
326
327 return false;
328}
329
330template <typename T>
333 size_t nodeIdx, const BoundingBox2D& bound) const
334{
335 if (!bound.Intersects(ray))
336 {
337 return false;
338 }
339
340 const Node& node = m_nodes[nodeIdx];
341
342 if (node.items.size() > 0)
343 {
344 for (size_t itemIdx : node.items)
345 {
346 if (testFunc(m_items[itemIdx], ray))
347 {
348 return true;
349 }
350 }
351 }
352
353 if (node.firstChild != std::numeric_limits<size_t>::max())
354 {
355 for (int i = 0; i < 4; ++i)
356 {
357 if (Intersects(ray, testFunc, node.firstChild + i,
358 BoundingBox2D{ bound.Corner(i), bound.MidPoint() }))
359 {
360 return true;
361 }
362 }
363 }
364
365 return false;
366}
367
368template <typename T>
372 const BoundingBox2D& bound) const
373{
374 if (!box.Overlaps(bound))
375 {
376 return;
377 }
378
379 const Node& node = m_nodes[nodeIdx];
380
381 if (node.items.size() > 0)
382 {
383 for (size_t itemIdx : node.items)
384 {
385 if (testFunc(m_items[itemIdx], box))
386 {
387 visitorFunc(m_items[itemIdx]);
388 }
389 }
390 }
391
392 if (node.firstChild != std::numeric_limits<size_t>::max())
393 {
394 for (int i = 0; i < 4; ++i)
395 {
396 ForEachIntersectingItem(
397 box, testFunc, visitorFunc, node.firstChild + i,
398 BoundingBox2D{ bound.Corner(i), bound.MidPoint() });
399 }
400 }
401}
402
403template <typename T>
407 const BoundingBox2D& bound) const
408{
409 if (!bound.Intersects(ray))
410 {
411 return;
412 }
413
414 const Node& node = m_nodes[nodeIdx];
415
416 if (node.items.size() > 0)
417 {
418 for (size_t itemIdx : node.items)
419 {
420 if (testFunc(m_items[itemIdx], ray))
421 {
422 visitorFunc(m_items[itemIdx]);
423 }
424 }
425 }
426
427 if (node.firstChild != std::numeric_limits<size_t>::max())
428 {
429 for (int i = 0; i < 4; ++i)
430 {
431 ForEachIntersectingItem(
432 ray, testFunc, visitorFunc, node.firstChild + i,
433 BoundingBox2D{ bound.Corner(i), bound.MidPoint() });
434 }
435 }
436}
437
438template <typename T>
441 size_t nodeIdx, const BoundingBox2D& bound,
443{
444 if (!bound.Intersects(ray))
445 {
446 return best;
447 }
448
449 const Node& node = m_nodes[nodeIdx];
450
451 if (node.items.size() > 0)
452 {
453 for (size_t itemIdx : node.items)
454 {
455 double dist = testFunc(m_items[itemIdx], ray);
456 if (dist < best.distance)
457 {
458 best.distance = dist;
459 best.item = &m_items[itemIdx];
460 }
461 }
462 }
463
464 if (node.firstChild != std::numeric_limits<size_t>::max())
465 {
466 for (int i = 0; i < 4; ++i)
467 {
468 best = ClosestIntersection(
469 ray, testFunc, node.firstChild + i,
470 BoundingBox2D{ bound.Corner(i), bound.MidPoint() }, best);
471 }
472 }
473
474 return best;
475}
476} // namespace CubbyFlow
477
478#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:194
VectorType Corner(size_t idx) const
Returns corner position. Index starts from x-first order.
Definition BoundingBox-Impl.hpp:240
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
size_t GetMaxDepth() const
Returns the maximum depth of the tree.
Definition Quadtree-Impl.hpp:246
void Build(const std::vector< T > &items, const BoundingBox2D &bound, const BoxIntersectionTestFunc2< T > &testFunc, size_t maxDepth)
Definition Quadtree-Impl.hpp:26
size_t GetNumberOfItems() const
Returns the number of items.
Definition Quadtree-Impl.hpp:210
Iterator begin()
Returns the begin iterator of the item.
Definition Quadtree-Impl.hpp:186
NearestNeighborQueryResult2< T > Nearest(const Vector2D &pt, const NearestNeighborDistanceFunc2< T > &distanceFunc) const override
Definition Quadtree-Impl.hpp:59
bool Intersects(const BoundingBox2D &box, const BoxIntersectionTestFunc2< T > &testFunc) const override
Returns true if given box intersects with any of the stored items.
Definition Quadtree-Impl.hpp:145
size_t GetNumberOfNodes() const
Returns the number of quadtree nodes.
Definition Quadtree-Impl.hpp:222
size_t GetChildIndex(size_t nodeIdx, size_t childIdx) const
Returns a child's index for given node.
Definition Quadtree-Impl.hpp:234
ClosestIntersectionQueryResult2< T > ClosestIntersection(const Ray2D &ray, const GetRayIntersectionFunc2< T > &testFunc) const override
Returns the closest intersection for given ray.
Definition Quadtree-Impl.hpp:175
const std::vector< size_t > & GetItemsAtNode(size_t nodeIdx) const
Returns the list of the items for given node index.
Definition Quadtree-Impl.hpp:228
const BoundingBox2D & GetBoundingBox() const
Returns the bounding box of this quadtree.
Definition Quadtree-Impl.hpp:240
void ForEachIntersectingItem(const BoundingBox2D &box, const BoxIntersectionTestFunc2< T > &testFunc, const IntersectionVisitorFunc< T > &visitorFunc) const override
Invokes visitorFunc for every intersecting items.
Definition Quadtree-Impl.hpp:159
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:192
const T & GetItem(size_t i) const
Returns the item at i.
Definition Quadtree-Impl.hpp:216
Class for N-D ray.
Definition Ray.hpp:26
Definition pybind11Utils.hpp:21
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:738
Ray2< double > Ray2D
Definition Ray.hpp:73