Octree-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_OCTREE_IMPL_HPP
12 #define CUBBYFLOW_OCTREE_IMPL_HPP
13 
14 #include <numeric>
15 #include <stack>
16 
17 namespace CubbyFlow
18 {
19 template <typename T>
20 bool Octree<T>::Node::IsLeaf() const
21 {
22  return firstChild == std::numeric_limits<size_t>::max();
23 }
24 
25 template <typename T>
26 void Octree<T>::Build(const std::vector<T>& items, const BoundingBox3D& bound,
27  const BoxIntersectionTestFunc3<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 =
38  std::max({ m_bbox.Width(), m_bbox.Height(), m_bbox.Depth() });
39  m_bbox.upperCorner =
40  m_bbox.lowerCorner + Vector3D{ maxEdgeLen, maxEdgeLen, maxEdgeLen };
41 
42  // Build
43  m_nodes.resize(1);
44  m_nodes[0].items.resize(m_items.size());
45  std::iota(m_nodes[0].items.begin(), m_nodes[0].items.end(), ZERO_SIZE);
46 
47  Build(0, 1, m_bbox, testFunc);
48 }
49 
50 template <typename T>
52 {
53  m_maxDepth = 1;
54  m_items.clear();
55  m_nodes.clear();
56  m_bbox = BoundingBox3D();
57 }
58 
59 template <typename T>
61  const Vector3D& pt,
62  const NearestNeighborDistanceFunc3<T>& distanceFunc) const
63 {
65  best.distance = std::numeric_limits<double>::max();
66  best.item = nullptr;
67 
68  // Prepare to traverse octree
69  std::stack<std::pair<const Node*, BoundingBox3D>> todo;
70 
71  // Traverse octree nodes
72  const Node* node = m_nodes.data();
73  BoundingBox3D bound = m_bbox;
74 
75  while (node != nullptr)
76  {
77  if (node->IsLeaf())
78  {
79  for (size_t itemIdx : node->items)
80  {
81  double d = distanceFunc(m_items[itemIdx], pt);
82  if (d < best.distance)
83  {
84  best.distance = d;
85  best.item = &m_items[itemIdx];
86  }
87  }
88 
89  // Grab next node to process from todo stack
90  if (todo.empty())
91  {
92  break;
93  }
94 
95  node = todo.top().first;
96  bound = todo.top().second;
97  todo.pop();
98  }
99  else
100  {
101  using NodeDistBox = std::tuple<const Node*, double, BoundingBox3D>;
102 
103  const double bestDistSqr = best.distance * best.distance;
104  std::array<NodeDistBox, 8> childDistSqrPairs;
105  const auto midPoint = bound.MidPoint();
106 
107  for (int i = 0; i < 8; ++i)
108  {
109  const Node* child = &m_nodes[node->firstChild + i];
110  const auto childBound =
111  BoundingBox3D(bound.Corner(i), midPoint);
112  Vector3D cp = childBound.Clamp(pt);
113  double distMinSqr = cp.DistanceSquaredTo(pt);
114 
115  childDistSqrPairs[i] =
116  std::make_tuple(child, distMinSqr, childBound);
117  }
118 
119  std::sort(childDistSqrPairs.begin(), childDistSqrPairs.end(),
120  [](const NodeDistBox& a, const NodeDistBox& b) {
121  return std::get<1>(a) > std::get<1>(b);
122  });
123 
124  for (int i = 0; i < 8; ++i)
125  {
126  const auto& childPair = childDistSqrPairs[i];
127 
128  if (std::get<1>(childPair) < bestDistSqr)
129  {
130  todo.emplace(std::get<0>(childPair),
131  std::get<2>(childPair));
132  }
133  }
134 
135  if (todo.empty())
136  {
137  break;
138  }
139 
140  node = todo.top().first;
141  bound = todo.top().second;
142  todo.pop();
143  }
144  }
145 
146  return best;
147 }
148 
149 template <typename T>
151  const BoxIntersectionTestFunc3<T>& testFunc) const
152 {
153  return Intersects(box, testFunc, 0, m_bbox);
154 }
155 
156 template <typename T>
157 bool Octree<T>::Intersects(const Ray3D& ray,
158  const RayIntersectionTestFunc3<T>& testFunc) const
159 {
160  return Intersects(ray, testFunc, 0, m_bbox);
161 }
162 
163 template <typename T>
165  const BoundingBox3D& box, const BoxIntersectionTestFunc3<T>& testFunc,
166  const IntersectionVisitorFunc<T>& visitorFunc) const
167 {
168  ForEachIntersectingItem(box, testFunc, visitorFunc, 0, m_bbox);
169 }
170 
171 template <typename T>
173  const Ray3D& ray, const RayIntersectionTestFunc3<T>& testFunc,
174  const IntersectionVisitorFunc<T>& visitorFunc) const
175 {
176  ForEachIntersectingItem(ray, testFunc, visitorFunc, 0, m_bbox);
177 }
178 
179 template <typename T>
181  const Ray3D& ray, const GetRayIntersectionFunc3<T>& testFunc) const
182 {
184  best.distance = std::numeric_limits<double>::max();
185  best.item = nullptr;
186 
187  return ClosestIntersection(ray, testFunc, 0, m_bbox, best);
188 }
189 
190 template <typename T>
192 {
193  return m_items.begin();
194 }
195 
196 template <typename T>
198 {
199  return m_items.end();
200 }
201 
202 template <typename T>
204 {
205  return m_items.begin();
206 }
207 
208 template <typename T>
210 {
211  return m_items.end();
212 }
213 
214 template <typename T>
216 {
217  return m_items.size();
218 }
219 
220 template <typename T>
221 const T& Octree<T>::GetItem(size_t i) const
222 {
223  return m_items[i];
224 }
225 
226 template <typename T>
228 {
229  return m_nodes.size();
230 }
231 
232 template <typename T>
233 const std::vector<size_t>& Octree<T>::GetItemsAtNode(size_t nodeIdx) const
234 {
235  return m_nodes[nodeIdx].items;
236 }
237 
238 template <typename T>
239 size_t Octree<T>::GetChildIndex(size_t nodeIdx, size_t childIdx) const
240 {
241  return m_nodes[nodeIdx].firstChild + childIdx;
242 }
243 
244 template <typename T>
246 {
247  return m_bbox;
248 }
249 
250 template <typename T>
252 {
253  return m_maxDepth;
254 }
255 
256 template <typename T>
257 void Octree<T>::Build(size_t nodeIdx, size_t depth, const BoundingBox3D& bound,
258  const BoxIntersectionTestFunc3<T>& testFunc)
259 {
260  if (depth < m_maxDepth && !m_nodes[nodeIdx].items.empty())
261  {
262  const size_t firstChild = m_nodes[nodeIdx].firstChild = m_nodes.size();
263  m_nodes.resize(m_nodes[nodeIdx].firstChild + 8);
264 
265  BoundingBox3D bboxPerNode[8];
266 
267  for (int i = 0; i < 8; ++i)
268  {
269  bboxPerNode[i] = BoundingBox3D{ bound.Corner(i), bound.MidPoint() };
270  }
271 
272  auto& currentItems = m_nodes[nodeIdx].items;
273  for (size_t i = 0; i < currentItems.size(); ++i)
274  {
275  size_t currentItem = currentItems[i];
276  for (int j = 0; j < 8; ++j)
277  {
278  if (testFunc(m_items[currentItem], bboxPerNode[j]))
279  {
280  m_nodes[firstChild + j].items.push_back(currentItem);
281  }
282  }
283  }
284 
285  // Remove non-leaf data
286  currentItems.clear();
287 
288  // Refine
289  for (int i = 0; i < 8; ++i)
290  {
291  Build(firstChild + i, depth + 1, bboxPerNode[i], testFunc);
292  }
293  }
294 }
295 
296 template <typename T>
297 bool Octree<T>::Intersects(const BoundingBox3D& box,
298  const BoxIntersectionTestFunc3<T>& testFunc,
299  size_t nodeIdx, const BoundingBox3D& bound) const
300 {
301  if (!box.Overlaps(bound))
302  {
303  return false;
304  }
305 
306  const Node& node = m_nodes[nodeIdx];
307 
308  if (!node.items.empty())
309  {
310  for (size_t itemIdx : node.items)
311  {
312  if (testFunc(m_items[itemIdx], box))
313  {
314  return true;
315  }
316  }
317  }
318 
319  if (node.firstChild != std::numeric_limits<size_t>::max())
320  {
321  for (int i = 0; i < 8; ++i)
322  {
323  if (Intersects(box, testFunc, node.firstChild + i,
324  BoundingBox3D{ bound.Corner(i), bound.MidPoint() }))
325  {
326  return true;
327  }
328  }
329  }
330 
331  return false;
332 }
333 
334 template <typename T>
335 bool Octree<T>::Intersects(const Ray3D& ray,
336  const RayIntersectionTestFunc3<T>& testFunc,
337  size_t nodeIdx, const BoundingBox3D& bound) const
338 {
339  if (!bound.Intersects(ray))
340  {
341  return false;
342  }
343 
344  const Node& node = m_nodes[nodeIdx];
345 
346  if (!node.items.empty())
347  {
348  for (size_t itemIdx : node.items)
349  {
350  if (testFunc(m_items[itemIdx], ray))
351  {
352  return true;
353  }
354  }
355  }
356 
357  if (node.firstChild != std::numeric_limits<size_t>::max())
358  {
359  for (int i = 0; i < 8; ++i)
360  {
361  if (Intersects(ray, testFunc, node.firstChild + i,
362  BoundingBox3D{ bound.Corner(i), bound.MidPoint() }))
363  {
364  return true;
365  }
366  }
367  }
368 
369  return false;
370 }
371 
372 template <typename T>
374  const BoundingBox3D& box, const BoxIntersectionTestFunc3<T>& testFunc,
375  const IntersectionVisitorFunc<T>& visitorFunc, size_t nodeIdx,
376  const BoundingBox3D& bound) const
377 {
378  if (!box.Overlaps(bound))
379  {
380  return;
381  }
382 
383  const Node& node = m_nodes[nodeIdx];
384 
385  if (!node.items.empty())
386  {
387  for (size_t itemIdx : node.items)
388  {
389  if (testFunc(m_items[itemIdx], box))
390  {
391  visitorFunc(m_items[itemIdx]);
392  }
393  }
394  }
395 
396  if (node.firstChild != std::numeric_limits<size_t>::max())
397  {
398  for (int i = 0; i < 8; ++i)
399  {
400  ForEachIntersectingItem(
401  box, testFunc, visitorFunc, node.firstChild + i,
402  BoundingBox3D{ bound.Corner(i), bound.MidPoint() });
403  }
404  }
405 }
406 
407 template <typename T>
409  const Ray3D& ray, const RayIntersectionTestFunc3<T>& testFunc,
410  const IntersectionVisitorFunc<T>& visitorFunc, size_t nodeIdx,
411  const BoundingBox3D& bound) const
412 {
413  if (!bound.Intersects(ray))
414  {
415  return;
416  }
417 
418  const Node& node = m_nodes[nodeIdx];
419 
420  if (!node.items.empty())
421  {
422  for (size_t itemIdx : node.items)
423  {
424  if (testFunc(m_items[itemIdx], ray))
425  {
426  visitorFunc(m_items[itemIdx]);
427  }
428  }
429  }
430 
431  if (node.firstChild != std::numeric_limits<size_t>::max())
432  {
433  for (int i = 0; i < 8; ++i)
434  {
435  ForEachIntersectingItem(
436  ray, testFunc, visitorFunc, node.firstChild + i,
437  BoundingBox3D{ bound.Corner(i), bound.MidPoint() });
438  }
439  }
440 }
441 
442 template <typename T>
444  const Ray3D& ray, const GetRayIntersectionFunc3<T>& testFunc,
445  size_t nodeIdx, const BoundingBox3D& bound,
447 {
448  if (!bound.Intersects(ray))
449  {
450  return best;
451  }
452 
453  const Node& node = m_nodes[nodeIdx];
454 
455  if (!node.items.empty())
456  {
457  for (size_t itemIdx : node.items)
458  {
459  double dist = testFunc(m_items[itemIdx], ray);
460  if (dist < best.distance)
461  {
462  best.distance = dist;
463  best.item = &m_items[itemIdx];
464  }
465  }
466  }
467 
468  if (node.firstChild != std::numeric_limits<size_t>::max())
469  {
470  for (int i = 0; i < 8; ++i)
471  {
472  best = ClosestIntersection(
473  ray, testFunc, node.firstChild + i,
474  BoundingBox3D{ bound.Corner(i), bound.MidPoint() }, best);
475  }
476  }
477 
478  return best;
479 }
480 } // namespace CubbyFlow
481 
482 #endif
ValueType DistanceSquaredTo(const MatrixExpression< T, R, C, E > &other) const
Returns the squared distance to the other vector.
void Clear()
Clears all the contents of this instance.
Definition: Octree-Impl.hpp:51
const BoundingBox3D & GetBoundingBox() const
Returns the bounding box of this octree.
Definition: Octree-Impl.hpp:245
NearestNeighborQueryResult3< T > Nearest(const Vector3D &pt, const NearestNeighborDistanceFunc3< T > &distanceFunc) const override
Definition: Octree-Impl.hpp:60
VectorType upperCorner
Upper corner of the bounding box.
Definition: BoundingBox.hpp:148
void ForEachIntersectingItem(const BoundingBox3D &box, const BoxIntersectionTestFunc3< T > &testFunc, const IntersectionVisitorFunc< T > &visitorFunc) const override
Invokes visitorFunc for every intersecting items.
Definition: Octree-Impl.hpp:164
size_t GetMaxDepth() const
Returns the maximum depth of the tree.
Definition: Octree-Impl.hpp:251
N-D nearest neighbor query result.
Definition: NearestNeighborQueryEngine.hpp:23
typename ContainerType::iterator Iterator
Definition: Octree.hpp:34
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
ClosestIntersectionQueryResult3< T > ClosestIntersection(const Ray3D &ray, const GetRayIntersectionFunc3< T > &testFunc) const override
Returns the closest intersection for given ray.
Definition: Octree-Impl.hpp:180
size_t GetNumberOfItems() const
Returns the number of items.
Definition: Octree-Impl.hpp:215
bool Intersects(const BoundingBox3D &box, const BoxIntersectionTestFunc3< T > &testFunc) const override
Returns true if given box intersects with any of the stored items.
Definition: Octree-Impl.hpp:150
N-D axis-aligned bounding box class.
Definition: BoundingBox.hpp:46
const std::vector< size_t > & GetItemsAtNode(size_t nodeIdx) const
Returns the list of the items for given node index.
Definition: Octree-Impl.hpp:233
const T & GetItem(size_t i) const
Returns the item at i.
Definition: Octree-Impl.hpp:221
typename ContainerType::const_iterator ConstIterator
Definition: Octree.hpp:35
bool Intersects(const RayType &ray) const
Returns true if the input ray is intersecting with this box.
Definition: BoundingBox-Impl.hpp:119
Definition: Matrix.hpp:27
BoundingBox3< double > BoundingBox3D
Definition: BoundingBox.hpp:163
N-D closest intersection query result.
Definition: IntersectionQueryEngine.hpp:25
Definition: pybind11Utils.hpp:20
RayIntersectionTestFunc< T, 3 > RayIntersectionTestFunc3
3-D ray-item intersection test function.
Definition: IntersectionQueryEngine.hpp:76
const T * item
Definition: NearestNeighborQueryEngine.hpp:25
double distance
Definition: IntersectionQueryEngine.hpp:28
GetRayIntersectionFunc< T, 3 > GetRayIntersectionFunc3
3-D ray-item closest intersection evaluation function.
Definition: IntersectionQueryEngine.hpp:89
size_t GetNumberOfNodes() const
Returns the number of octree nodes.
Definition: Octree-Impl.hpp:227
BoxIntersectionTestFunc< T, 3 > BoxIntersectionTestFunc3
3-D box-item intersection test function.
Definition: IntersectionQueryEngine.hpp:63
constexpr size_t ZERO_SIZE
Zero size_t.
Definition: Constants.hpp:20
void Build(const std::vector< T > &items, const BoundingBox3D &bound, const BoxIntersectionTestFunc3< T > &testFunc, size_t maxDepth)
Definition: Octree-Impl.hpp:26
NearestNeighborDistanceFunc< T, 3 > NearestNeighborDistanceFunc3
3-D nearest neighbor distance measure function.
Definition: NearestNeighborQueryEngine.hpp:48
size_t GetChildIndex(size_t nodeIdx, size_t childIdx) const
Returns a child&#39;s index for given node.
Definition: Octree-Impl.hpp:239
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
VectorType MidPoint() const
Returns the mid-point of this box.
Definition: BoundingBox-Impl.hpp:194
Iterator begin()
Returns the begin iterator of the item.
Definition: Octree-Impl.hpp:191
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
Iterator end()
Returns the end iterator of the item.
Definition: Octree-Impl.hpp:197