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 
17 namespace CubbyFlow
18 {
19 template <typename T>
20 bool Quadtree<T>::Node::IsLeaf() const
21 {
22  return firstChild == std::numeric_limits<size_t>::max();
23 }
24 
25 template <typename T>
26 void Quadtree<T>::Build(const std::vector<T>& items, const BoundingBox2D& bound,
27  const BoxIntersectionTestFunc2<T>& testFunc,
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 
49 template <typename T>
51 {
52  m_maxDepth = 1;
53  m_items.clear();
54  m_nodes.cloear();
55  m_bbox = BoundingBox2D();
56 }
57 
58 template <typename T>
60  const Vector2D& pt,
61  const NearestNeighborDistanceFunc2<T>& distanceFunc) const
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);
110  double distMinSqr = cp.DistanceSquaredTo(pt);
111 
112  childDistSqrPairs[i] =
113  std::make_tuple(child, distMinSqr, childBound);
114  }
115  std::sort(childDistSqrPairs.begin(), childDistSqrPairs.end(),
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 
144 template <typename T>
146  const BoxIntersectionTestFunc2<T>& testFunc) const
147 {
148  return Intersects(box, testFunc, 0, m_bbox);
149 }
150 
151 template <typename T>
153  const RayIntersectionTestFunc2<T>& testFunc) const
154 {
155  return Intersects(ray, testFunc, 0, m_bbox);
156 }
157 
158 template <typename T>
160  const BoundingBox2D& box, const BoxIntersectionTestFunc2<T>& testFunc,
161  const IntersectionVisitorFunc<T>& visitorFunc) const
162 {
163  ForEachIntersectingItem(box, testFunc, visitorFunc, 0, m_bbox);
164 }
165 
166 template <typename T>
168  const Ray2D& ray, const RayIntersectionTestFunc2<T>& testFunc,
169  const IntersectionVisitorFunc<T>& visitorFunc) const
170 {
171  ForEachIntersectingItem(ray, testFunc, visitorFunc, 0, m_bbox);
172 }
173 
174 template <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 
185 template <typename T>
187 {
188  return m_items.begin();
189 }
190 
191 template <typename T>
193 {
194  return m_items.end();
195 }
196 
197 template <typename T>
199 {
200  return m_items.begin();
201 }
202 
203 template <typename T>
205 {
206  return m_items.end();
207 }
208 
209 template <typename T>
211 {
212  return m_items.size();
213 }
214 
215 template <typename T>
216 const T& Quadtree<T>::GetItem(size_t i) const
217 {
218  return m_items[i];
219 }
220 
221 template <typename T>
223 {
224  return m_nodes.size();
225 }
226 
227 template <typename T>
228 const std::vector<size_t>& Quadtree<T>::GetItemsAtNode(size_t nodeIdx) const
229 {
230  return m_nodes[nodeIdx].items;
231 }
232 
233 template <typename T>
234 size_t Quadtree<T>::GetChildIndex(size_t nodeIdx, size_t childIdx) const
235 {
236  return m_nodes[nodeIdx].firstChild + childIdx;
237 }
238 
239 template <typename T>
241 {
242  return m_bbox;
243 }
244 
245 template <typename T>
247 {
248  return m_maxDepth;
249 }
250 
251 template <typename T>
252 void Quadtree<T>::Build(size_t nodeIdx, size_t depth,
253  const BoundingBox2D& bound,
254  const BoxIntersectionTestFunc2<T>& testFunc)
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 
261  BoundingBox2D bboxPerNode[4];
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 
292 template <typename T>
293 bool Quadtree<T>::Intersects(const BoundingBox2D& box,
294  const BoxIntersectionTestFunc2<T>& testFunc,
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 
330 template <typename T>
331 bool Quadtree<T>::Intersects(const Ray2D& ray,
332  const RayIntersectionTestFunc2<T>& testFunc,
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 
368 template <typename T>
370  const BoundingBox2D& box, const BoxIntersectionTestFunc2<T>& testFunc,
371  const IntersectionVisitorFunc<T>& visitorFunc, size_t nodeIdx,
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 
403 template <typename T>
405  const Ray2D& ray, const RayIntersectionTestFunc2<T>& testFunc,
406  const IntersectionVisitorFunc<T>& visitorFunc, size_t nodeIdx,
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 
438 template <typename T>
440  const Ray2D& ray, const GetRayIntersectionFunc2<T>& testFunc,
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
ValueType DistanceSquaredTo(const MatrixExpression< T, R, C, E > &other) const
Returns the squared distance to the other vector.
VectorType upperCorner
Upper corner of the bounding box.
Definition: BoundingBox.hpp:148
N-D nearest neighbor query result.
Definition: NearestNeighborQueryEngine.hpp:23
VectorType Corner(size_t idx) const
Returns corner position. Index starts from x-first order.
Definition: BoundingBox-Impl.hpp:240
Class for N-D ray.
Definition: Ray.hpp:25
N-D axis-aligned bounding box class.
Definition: BoundingBox.hpp:46
size_t GetNumberOfNodes() const
Returns the number of quadtree nodes.
Definition: Quadtree-Impl.hpp:222
size_t GetMaxDepth() const
Returns the maximum depth of the tree.
Definition: Quadtree-Impl.hpp:246
BoundingBox2< double > BoundingBox2D
Definition: BoundingBox.hpp:159
size_t GetChildIndex(size_t nodeIdx, size_t childIdx) const
Returns a child&#39;s index for given node.
Definition: Quadtree-Impl.hpp:234
NearestNeighborQueryResult2< T > Nearest(const Vector2D &pt, const NearestNeighborDistanceFunc2< T > &distanceFunc) const override
Definition: Quadtree-Impl.hpp:59
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
void Build(const std::vector< T > &items, const BoundingBox2D &bound, const BoxIntersectionTestFunc2< T > &testFunc, size_t maxDepth)
Definition: Quadtree-Impl.hpp:26
bool Intersects(const RayType &ray) const
Returns true if the input ray is intersecting with this box.
Definition: BoundingBox-Impl.hpp:119
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
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
Definition: Matrix.hpp:27
void Clear()
Clears all the contents of this instance.
Definition: Quadtree-Impl.hpp:50
N-D closest intersection query result.
Definition: IntersectionQueryEngine.hpp:25
Definition: pybind11Utils.hpp:20
GetRayIntersectionFunc< T, 2 > GetRayIntersectionFunc2
2-D ray-item closest intersection evaluation function.
Definition: IntersectionQueryEngine.hpp:85
const T * item
Definition: NearestNeighborQueryEngine.hpp:25
double distance
Definition: IntersectionQueryEngine.hpp:28
Iterator begin()
Returns the begin iterator of the item.
Definition: Quadtree-Impl.hpp:186
size_t GetNumberOfItems() const
Returns the number of items.
Definition: Quadtree-Impl.hpp:210
BoxIntersectionTestFunc< T, 2 > BoxIntersectionTestFunc2
2-D box-item intersection test function.
Definition: IntersectionQueryEngine.hpp:59
Iterator end()
Returns the end iterator of the item.
Definition: Quadtree-Impl.hpp:192
constexpr size_t ZERO_SIZE
Zero size_t.
Definition: Constants.hpp:20
const BoundingBox2D & GetBoundingBox() const
Returns the bounding box of this quadtree.
Definition: Quadtree-Impl.hpp:240
RayIntersectionTestFunc< T, 2 > RayIntersectionTestFunc2
2-D ray-item intersection test function.
Definition: IntersectionQueryEngine.hpp:72
bool Overlaps(const BoundingBox &other) const
Returns true of this box and other box overlaps.
Definition: BoundingBox-Impl.hpp:90
const T * item
Definition: IntersectionQueryEngine.hpp:27
ClosestIntersectionQueryResult2< T > ClosestIntersection(const Ray2D &ray, const GetRayIntersectionFunc2< T > &testFunc) const override
Returns the closest intersection for given ray.
Definition: Quadtree-Impl.hpp:175
VectorType MidPoint() const
Returns the mid-point of this box.
Definition: BoundingBox-Impl.hpp:194
const T & GetItem(size_t i) const
Returns the item at i.
Definition: Quadtree-Impl.hpp:216
typename ContainerType::const_iterator ConstIterator
Definition: Quadtree.hpp:35
double distance
Definition: NearestNeighborQueryEngine.hpp:26
std::function< void(const T &)> IntersectionVisitorFunc
Visitor function which is invoked for each intersecting item.
Definition: IntersectionQueryEngine.hpp:93
typename ContainerType::iterator Iterator
Definition: Quadtree.hpp:34
NearestNeighborDistanceFunc< T, 2 > NearestNeighborDistanceFunc2
2-D nearest neighbor distance measure function.
Definition: NearestNeighborQueryEngine.hpp:44