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