Ocean
Octree.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_GEOMETRY_OCTREE_H
9 #define META_OCEAN_GEOMETRY_OCTREE_H
10 
12 
13 #include "ocean/math/BoundingBox.h"
14 #include "ocean/math/Line3.h"
15 #include "ocean/math/Vector3.h"
16 
17 namespace Ocean
18 {
19 
20 namespace Geometry
21 {
22 
23 /// Forward declaration.
24 template <typename T>
25 class OctreeT;
26 
27 /**
28  * Definition of an Octree using Scalar as data type.
29  * @see OctreeT
30  * @ingroup geometry
31  */
33 
34 /**
35  * Definition of an Octree using double as data type.
36  * @see OctreeT
37  * @ingroup geometry
38  */
40 
41 /**
42  * Definition of an Octree using float as data type.
43  * @see OctreeT
44  * @ingroup geometry
45  */
47 
48 /**
49  * This class implements an Octree allowing to manage 3D points.
50  * Each node in the Octree has exactly eight child nodes (unless it is a leaf node).
51  * @ingroup geometry
52  */
53 template <typename T>
54 class OctreeT
55 {
56  public:
57 
58  /// The data type of this octree.
59  typedef T Type;
60 
61  /**
62  * This class stores construction parameters for an octree.
63  */
64  class Parameters
65  {
66  public:
67 
68  /**
69  * Default constructor.
70  */
71  Parameters() = default;
72 
73  /**
74  * Creates a new parameter object.
75  * @param maximalPointsPerLeaf The maximal number of points each leaf node can have, with range [1, infinity)
76  * @param useTightBoundingBoxes True, to use tight bounding boxes for each individual node (only covering the actual points); False, to use bisected bounding boxes of the bounding box of the previous node
77  */
78  inline Parameters(const unsigned int maximalPointsPerLeaf, const bool useTightBoundingBoxes);
79 
80  /**
81  * Returns whether this object holds valid parameters.
82  * @return True, if so
83  */
84  inline bool isValid() const;
85 
86  public:
87 
88  /// The maximal number of points each leaf node can have.
89  unsigned int maximalPointsPerLeaf_ = 40u;
90 
91  /// True, to use tight bounding boxes for each individual node (only covering the actual points); False, to use bisected bounding boxes of the bounding box of the previous node
93  };
94 
95  /**
96  * Definition of a class which holds reusable data for internal use.
97  * This object can avoid reallocating memory when calling a matching function several times in a row.<br>
98  * Simply define this object outside of the loop and provide the object as parameter, e.g.,
99  * @code
100  * std::vector<const Indices32*> leafs;
101  * ReusableData reusableData;
102  * for (const Vector3& point : points)
103  * {
104  * leafs.clear();
105  * octree.closestLeafs(point, maximalDistance, leafs, reusableData);
106  * ...
107  * }
108  * @endcode
109  */
111  {
112  friend class OctreeT<T>;
113 
114  public:
115 
116  /**
117  * Creates a new object.
118  */
119  ReusableData() = default;
120 
121  protected:
122 
123  /// The internal reusable data.
124  mutable std::vector<const OctreeT<T>*> internalData_;
125  };
126 
127  public:
128 
129  /**
130  * Default constructor creating an empty tree.
131  */
132  OctreeT() = default;
133 
134  /**
135  * Disabled copy constructor.
136  * @param octree The octree which would be copied
137  */
138  OctreeT(const OctreeT& octree) = delete;
139 
140  /**
141  * Move constructor.
142  * @param octree The octree to be moved
143  */
144  OctreeT(OctreeT&& octree);
145 
146  /**
147  * Creates a new Octree for a given set of 3D points.
148  * The given points must not change afterwards, the points must exist as long as the tree exists.
149  * @param treePoints The points for which the tree will be created, can be 'nullptr' if 'numberTreePoints == 0'
150  * @param numberTreePoints The number given tree points, with range [0, infinity)
151  * @param parameters The parameters to used to construct the tree, must be valid
152  */
153  OctreeT(const VectorT3<T>* treePoints, const size_t numberTreePoints, const Parameters& parameters = Parameters());
154 
155  /**
156  * Destructs this tree node.
157  */
158  ~OctreeT();
159 
160  /**
161  * Returns the bounding box containing all points of this node (of all points in all child leaf nodes)
162  * @return The tree node's bounding box
163  */
164  inline const BoundingBox& boundingBox() const;
165 
166  /**
167  * Returns the indices of the tree points which belong to this leaf node.
168  * @return The leaf node's point indices, empty if this node is not a leaf node
169  */
170  inline const Indices32& pointIndices() const;
171 
172  /**
173  * Returns the eight child nodes of this tree node
174  * @return The node's eight child node, nullptr if this node is a leaf node
175  */
176  inline const OctreeT<T>* const * childNodes() const;
177 
178  /**
179  * Returns the closest leaf nodes for a given query point.
180  * @param queryPoint The query point for which the closest leaf nodes will be returned
181  * @param maximalDistance The maximal distance between the query point and any potential point in a leaf node, with range [0, infinity)
182  * @param leafs The resulting leaf nodes, mainly the point indices of the tree points which stored in the closest leaf nodes
183  * @param reusableData An reusable object to speedup the search, should be located outside of the function call if several function calls are done after each other
184  */
185  void closestLeafs(const VectorT3<T>& queryPoint, const T maximalDistance, std::vector<const Indices32*>& leafs, const ReusableData& reusableData = ReusableData()) const;
186 
187  /**
188  * Returns the intersecting leaf nodes for a given query ray.
189  * @param queryRay The query ray for which the intersecting leaf nodes will be returned, the search treats the ray as an infinite ray in space, must be valid
190  * @param leafs The resulting leaf nodes, mainly the point indices of the tree points which stored in the intersecting leaf nodes
191  * @param reusableData An reusable object to speedup the search, should be located outside of the function call if several function calls are done after each other
192  */
193  void intersectingLeafs(const LineT3<T>& queryRay, std::vector<const Indices32*>& leafs, const ReusableData& reusableData = ReusableData()) const;
194 
195  /**
196  * Returns the intersecting leaf nodes for a given approximated query cone expressed as a ray with a cone apex angle.
197  * This function applies an approximation to determine the distance between the cone's apex and leaf nodes.
198  * @param queryRay The query ray defining the apex and axis of the cone for which the intersecting leaf nodes will be returned, the search treats the ray as an infinite ray in space, must be valid
199  * @param tanHalfAngle The tangent of the cone's half apex angle, e.g., Numeric::tan(Numeric::deg2rad(1)), with range [0, 1)
200  * @param leafs The resulting leaf nodes, mainly the point indices of the tree points which stored in the intersecting leaf nodes
201  * @param reusableData An reusable object to speedup the search, should be located outside of the function call if several function calls are done after each other
202  */
203  void intersectingLeafs(const LineT3<T>& queryRay, const Scalar tanHalfAngle, std::vector<const Indices32*>& leafs, const ReusableData& reusableData = ReusableData()) const;
204 
205  /**
206  * Returns the closest tree points for a given query point.
207  * @param treePoints The tree points from which the closest points will be determined (must be the same points for which the tree has been created), must be valid
208  * @param queryPoint The query point for which the closest points will be returned
209  * @param maximalDistance The maximal distance between the query point and any potential tree point in a leaf node, with range [0, infinity)
210  * @param pointIndices The resulting indices of the tree points which have a maximal distance of 'maximalDistance' to the query point
211  * @param points Optional resulting tree points, one for each resulting index, will be {treePoints[pointIndices[0]], treePoints[pointIndices[1]], ..., treePoints[pointIndices[pointIndices.size() - 1]]}; nullptr if not of interest
212  * @param reusableData An reusable object to speedup the search, should be located outside of the function call if several function calls are done after each other
213  */
214  void closestPoints(const VectorT3<T>* treePoints, const VectorT3<T>& queryPoint, const T maximalDistance, Indices32& pointIndices, VectorsT3<T>* points = nullptr, const ReusableData& reusableData = ReusableData()) const;
215 
216  /**
217  * Returns whether this node is valid (if this node has a valid bounding box)
218  * @return True, if so
219  */
220  inline bool isValid() const;
221 
222  /**
223  * Move operator.
224  * @param octree The octree to be moved
225  * @return Reference to this octree
226  */
227  OctreeT& operator=(OctreeT&& octree);
228 
229  /**
230  * Disabled copy constructor.
231  * @param octree The octree which would be copied
232  * @return Reference to this octree
233  */
234  OctreeT& operator=(const OctreeT& octree) = delete;
235 
236  protected:
237 
238  /**
239  * Creates a new octree node.
240  * @param parameters The parameters to be used, must be valid
241  * @param treePoints The points for which the tree will be created, must be valid
242  * @param reusablePointIndicesInput The indices of the points for which the new node will be created, must be valid
243  * @param reusablePointIndicesOutput Memory block of indices with same size as 'reusableIndicesInput' which can be re-used internally, must be valid
244  * @param numberPointIndices The number of given indices in 'reusablePointIndicesInput', with range [0, infinity)
245  * @param boundingBox The bounding box of the new child node; will be ignored if `parameters.useTightBoundingBoxes_ == true`
246  */
247  OctreeT(const Parameters& parameters, const VectorT3<T>* treePoints, Index32* reusablePointIndicesInput, Index32* reusablePointIndicesOutput, const size_t numberPointIndices, const BoundingBox& boundingBox);
248 
249  protected:
250 
251  /// The bounding box of this tree node.
253 
254  /// The indicies of the tree points which belong to this leaf node, empty if this node is not a leaf node.
256 
257  /// The eight child nodes.
258  OctreeT<T>* childNodes_[8] = {nullptr};
259 };
260 
261 template <typename T>
262 inline OctreeT<T>::Parameters::Parameters(const unsigned int maximalPointsPerLeaf, const bool useTightBoundingBoxes) :
263  maximalPointsPerLeaf_(maximalPointsPerLeaf),
264  useTightBoundingBoxes_(useTightBoundingBoxes)
265 {
266  ocean_assert(maximalPointsPerLeaf_ >= 1u);
267 }
268 
269 template <typename T>
271 {
272  return maximalPointsPerLeaf_ >= 1u;
273 }
274 
275 template <typename T>
277 {
278  *this = std::move(octree);
279 }
280 
281 template <typename T>
282 OctreeT<T>::OctreeT(const VectorT3<T>* treePoints, const size_t numberTreePoints, const Parameters& parameters)
283 {
284  ocean_assert(parameters.isValid());
285 
286  if (numberTreePoints == 0)
287  {
288  return;
289  }
290 
291  ocean_assert(treePoints != nullptr);
292 
293  Indices32 reusablePointIndicesInput(createIndices<Index32>(numberTreePoints, 0u));
294  Indices32 reusablePointIndicesOutput(reusablePointIndicesInput.size());
295 
297 
298  if (parameters.useTightBoundingBoxes_ == false)
299  {
300  for (size_t n = 0; n < numberTreePoints; ++n)
301  {
302  boundingBox += Vector3(treePoints[n]);
303  }
304  }
305 
306  *this = Octree(parameters, treePoints, reusablePointIndicesInput.data(), reusablePointIndicesOutput.data(), numberTreePoints, boundingBox);
307 }
308 
309 template <typename T>
310 OctreeT<T>::OctreeT(const Parameters& parameters, const VectorT3<T>* treePoints, Index32* reusablePointIndicesInput, Index32* reusablePointIndicesOutput, const size_t numberPointIndices, const BoundingBox& boundingBox)
311 {
312  ocean_assert(parameters.isValid());
313  ocean_assert(treePoints != nullptr);
314 
315  if (numberPointIndices == 0)
316  {
317  return;
318  }
319 
320  ocean_assert(reusablePointIndicesInput != nullptr && reusablePointIndicesOutput != nullptr);
321 
322  bool isLeafNode = numberPointIndices <= parameters.maximalPointsPerLeaf_;
323 
324  if (!isLeafNode)
325  {
326  // let's ensure that not all points are identical, in this case we have a leaf node as well
327 
328  const Vector3& firstPoint = treePoints[reusablePointIndicesInput[0]];
329 
330  bool allPointsIdentical = true;
331 
332  for (size_t n = 1; n < numberPointIndices; ++n)
333  {
334  const Index32& index = reusablePointIndicesInput[n];
335  const VectorT3<T>& point = treePoints[index];
336 
337  if (firstPoint != point)
338  {
339  allPointsIdentical = false;
340  break;
341  }
342  }
343 
344  isLeafNode = allPointsIdentical;
345  }
346 
347  if (parameters.useTightBoundingBoxes_ == false)
348  {
349  ocean_assert(boundingBox.isValid());
351 
352 #ifdef OCEAN_DEBUG
353  for (size_t n = 0; n < numberPointIndices; ++n)
354  {
355  const Index32& index = reusablePointIndicesInput[n];
356  ocean_assert(boundingBox_.isInside(Vector3(treePoints[index])));
357  }
358 #endif
359 
360  if (isLeafNode)
361  {
362  // we have a leaf node
363 
364  pointIndices_.reserve(numberPointIndices);
365 
366  for (size_t n = 0; n < numberPointIndices; ++n)
367  {
368  const Index32& index = reusablePointIndicesInput[n];
369  pointIndices_.emplace_back(index);
370  }
371 
372  return;
373  }
374  }
375  else
376  {
377  if (isLeafNode)
378  {
379  // we have a leaf node
380 
381  pointIndices_.reserve(numberPointIndices);
382 
383  for (size_t n = 0; n < numberPointIndices; ++n)
384  {
385  const Index32& index = reusablePointIndicesInput[n];
386 
387  boundingBox_ += Vector3(treePoints[index]);
388  pointIndices_.emplace_back(index);
389  }
390 
391  ocean_assert(boundingBox_.isValid());
392 
393  return;
394  }
395 
396  // we need to separate the points
397 
398  for (size_t n = 0; n < numberPointIndices; ++n)
399  {
400  const Index32& index = reusablePointIndicesInput[n];
401  boundingBox_ += Vector3(treePoints[index]);
402  }
403  }
404 
405  ocean_assert(boundingBox_.isValid());
406 
407  const VectorT3<T> center(boundingBox_.center());
408 
409  // first, we count how may points fall in which node
410 
411  size_t lowLowLow = 0;
412  size_t lowLowHigh = 0;
413  size_t lowHighLow = 0;
414  size_t lowHighHigh = 0;
415 
416  size_t highLowLow = 0;
417  size_t highLowHigh = 0;
418  size_t highHighLow = 0;
419  size_t highHighHigh = 0;
420 
421  for (size_t n = 0; n < numberPointIndices; ++n)
422  {
423  const Index32& index = reusablePointIndicesInput[n];
424  const VectorT3<T>& point = treePoints[index];
425 
426  if (point.x() < center.x())
427  {
428  if (point.y() < center.y())
429  {
430  if (point.z() < center.z())
431  {
432  ++lowLowLow;
433  }
434  else
435  {
436  ++lowLowHigh;
437  }
438  }
439  else
440  {
441  if (point.z() < center.z())
442  {
443  ++lowHighLow;
444  }
445  else
446  {
447  ++lowHighHigh;
448  }
449  }
450  }
451  else
452  {
453  if (point.y() < center.y())
454  {
455  if (point.z() < center.z())
456  {
457  ++highLowLow;
458  }
459  else
460  {
461  ++highLowHigh;
462  }
463  }
464  else
465  {
466  if (point.z() < center.z())
467  {
468  ++highHighLow;
469  }
470  else
471  {
472  ++highHighHigh;
473  }
474  }
475  }
476  }
477 
478  // now, we shuffle the indices of the individual points
479 
480  Index32* lowLowLowPointer = reusablePointIndicesOutput;
481  Index32* lowLowHighPointer = lowLowLowPointer + lowLowLow;
482  Index32* lowHighLowPointer = lowLowHighPointer + lowLowHigh;
483  Index32* lowHighHighPointer = lowHighLowPointer + lowHighLow;
484 
485  Index32* highLowLowPointer = lowHighHighPointer + lowHighHigh;
486  Index32* highLowHighPointer = highLowLowPointer + highLowLow;
487  Index32* highHighLowPointer = highLowHighPointer + highLowHigh;
488  Index32* highHighHighPointer = highHighLowPointer + highHighLow;
489 
490  ocean_assert(highHighHighPointer + highHighHigh == reusablePointIndicesOutput + numberPointIndices);
491 
492  for (size_t n = 0; n < numberPointIndices; ++n)
493  {
494  const Index32& index = reusablePointIndicesInput[n];
495  const VectorT3<T>& point = treePoints[index];
496 
497  if (point.x() < center.x())
498  {
499  if (point.y() < center.y())
500  {
501  if (point.z() < center.z())
502  {
503  *lowLowLowPointer++ = index;
504  }
505  else
506  {
507  *lowLowHighPointer++ = index;
508  }
509  }
510  else
511  {
512  if (point.z() < center.z())
513  {
514  *lowHighLowPointer++ = index;
515  }
516  else
517  {
518  *lowHighHighPointer++ = index;
519  }
520  }
521  }
522  else
523  {
524  if (point.y() < center.y())
525  {
526  if (point.z() < center.z())
527  {
528  *highLowLowPointer++ = index;
529  }
530  else
531  {
532  *highLowHighPointer++ = index;
533  }
534  }
535  else
536  {
537  if (point.z() < center.z())
538  {
539  *highHighLowPointer++ = index;
540  }
541  else
542  {
543  *highHighHighPointer++ = index;
544  }
545  }
546  }
547  }
548 
549  ocean_assert(lowLowLowPointer == reusablePointIndicesOutput + lowLowLow);
550  ocean_assert(lowLowHighPointer == lowLowLowPointer + lowLowHigh);
551  ocean_assert(lowHighLowPointer == lowLowHighPointer + lowHighLow);
552  ocean_assert(lowHighHighPointer == lowHighLowPointer + lowHighHigh);
553 
554  ocean_assert(highLowLowPointer == lowHighHighPointer + highLowLow);
555  ocean_assert(highLowHighPointer == highLowLowPointer + highLowHigh);
556  ocean_assert(highHighLowPointer == highLowHighPointer + highHighLow);
557  ocean_assert(highHighHighPointer == highHighLowPointer + highHighHigh);
558 
559  if (parameters.useTightBoundingBoxes_)
560  {
561  childNodes_[0] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput, reusablePointIndicesInput, lowLowLow, BoundingBox()); // with swapped reusableIndicesOutput and reusableIndicesInput
562  childNodes_[1] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += lowLowLow, reusablePointIndicesInput += lowLowLow, lowLowHigh, BoundingBox());
563  childNodes_[2] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += lowLowHigh, reusablePointIndicesInput += lowLowHigh, lowHighLow, BoundingBox());
564  childNodes_[3] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += lowHighLow, reusablePointIndicesInput += lowHighLow, lowHighHigh, BoundingBox());
565 
566  childNodes_[4] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += lowHighHigh, reusablePointIndicesInput += lowHighHigh, highLowLow, BoundingBox());
567  childNodes_[5] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += highLowLow, reusablePointIndicesInput += highLowLow, highLowHigh, BoundingBox());
568  childNodes_[6] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += highLowHigh, reusablePointIndicesInput += highLowHigh, highHighLow, BoundingBox());
569  childNodes_[7] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += highHighLow, reusablePointIndicesInput += highHighLow, highHighHigh, BoundingBox());
570  }
571  else
572  {
573  const BoundingBox boxLowLowLow(Vector3(boundingBox_.lower().x(), boundingBox_.lower().y(), boundingBox_.lower().z()), Vector3(center.x(), center.y(), center.z()));
574  const BoundingBox boxLowLowHigh(Vector3(boundingBox_.lower().x(), boundingBox_.lower().y(), center.z()), Vector3(center.x(), center.y(), boundingBox_.higher().z()));
575  const BoundingBox boxLowHighLow(Vector3(boundingBox_.lower().x(), center.y(), boundingBox_.lower().z()), Vector3(center.x(), boundingBox_.higher().y(), center.z()));
576  const BoundingBox boxLowHighHigh(Vector3(boundingBox_.lower().x(), center.y(), center.z()), Vector3(center.x(), boundingBox_.higher().y(), boundingBox_.higher().z()));
577 
578  const BoundingBox boxHighLowLow(Vector3(center.x(), boundingBox_.lower().y(), boundingBox_.lower().z()), Vector3(boundingBox_.higher().x(), center.y(), center.z()));
579  const BoundingBox boxHighLowHigh(Vector3(center.x(), boundingBox_.lower().y(), center.z()), Vector3(boundingBox_.higher().x(), center.y(), boundingBox_.higher().z()));
580  const BoundingBox boxHighHighLow(Vector3(center.x(), center.y(), boundingBox_.lower().z()), Vector3(boundingBox_.higher().x(), boundingBox_.higher().y(), center.z()));
581  const BoundingBox boxHighHighHigh(Vector3(center.x(), center.y(), center.z()), Vector3(boundingBox_.higher().x(), boundingBox_.higher().y(), boundingBox_.higher().z()));
582 
583  childNodes_[0] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput, reusablePointIndicesInput, lowLowLow, boxLowLowLow); // with swapped reusableIndicesOutput and reusableIndicesInput
584  childNodes_[1] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += lowLowLow, reusablePointIndicesInput += lowLowLow, lowLowHigh, boxLowLowHigh);
585  childNodes_[2] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += lowLowHigh, reusablePointIndicesInput += lowLowHigh, lowHighLow, boxLowHighLow);
586  childNodes_[3] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += lowHighLow, reusablePointIndicesInput += lowHighLow, lowHighHigh, boxLowHighHigh);
587 
588  childNodes_[4] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += lowHighHigh, reusablePointIndicesInput += lowHighHigh, highLowLow, boxHighLowLow);
589  childNodes_[5] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += highLowLow, reusablePointIndicesInput += highLowLow, highLowHigh, boxHighLowHigh);
590  childNodes_[6] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += highLowHigh, reusablePointIndicesInput += highLowHigh, highHighLow, boxHighHighLow);
591  childNodes_[7] = new OctreeT<T>(parameters, treePoints, reusablePointIndicesOutput += highHighLow, reusablePointIndicesInput += highHighLow, highHighHigh, boxHighHighHigh);
592  }
593 }
594 
595 template <typename T>
597 {
598  for (unsigned int n = 0u; n < 8u; ++n)
599  {
600  delete childNodes_[n];
601  }
602 }
603 
604 template <typename T>
606 {
607  return boundingBox_;
608 }
609 
610 template <typename T>
611 inline const Indices32& OctreeT<T>::pointIndices() const
612 {
613  return pointIndices_;
614 }
615 
616 template <typename T>
617 inline const OctreeT<T>* const * OctreeT<T>::childNodes() const
618 {
619  if (childNodes_[0] == nullptr)
620  {
621  return nullptr;
622  }
623 
624  return childNodes_;
625 }
626 
627 template <typename T>
628 void OctreeT<T>::closestLeafs(const VectorT3<T>& queryPoint, const T maximalDistance, std::vector<const Indices32*>& leafs, const ReusableData& reusableData) const
629 {
630  if (!isValid())
631  {
632  return;
633  }
634 
635  const Vector3 scalarPoint(queryPoint);
636 
637  const T maximalSqrDistance = Scalar(maximalDistance * maximalDistance);
638 
639  if (!boundingBox_.isInside(scalarPoint, Scalar(maximalDistance)))
640  {
641  return;
642  }
643 
644  std::vector<const OctreeT<T>*>& nodes = reusableData.internalData_;
645  nodes.emplace_back(this);
646 
647  while (!nodes.empty())
648  {
649  const OctreeT<T>* node = nodes.back();
650  nodes.pop_back();
651 
652  ocean_assert(node != nullptr);
653  ocean_assert(node->boundingBox_.isInside(scalarPoint, Scalar(maximalDistance)));
654 
655  if (node->childNodes_[0] != nullptr)
656  {
657  for (unsigned int n = 0u; n < 8u; ++n)
658  {
659  ocean_assert(node->childNodes_[n] != nullptr);
660  const OctreeT<T>& childNode = *node->childNodes_[n];
661 
662  if (childNode.isValid() && childNode.boundingBox_.isInside(scalarPoint, Scalar(maximalDistance)))
663  {
664  nodes.emplace_back(&childNode);
665  }
666  }
667  }
668  else if (!node->pointIndices_.empty())
669  {
670  leafs.emplace_back(&node->pointIndices_);
671  }
672  }
673 }
674 
675 template <typename T>
676 void OctreeT<T>::intersectingLeafs(const LineT3<T>& queryRay, std::vector<const Indices32*>& leafs, const ReusableData& reusableData) const
677 {
678  ocean_assert(queryRay.isValid());
679  ocean_assert(leafs.empty());
680 
681  if (!isValid())
682  {
683  return;
684  }
685 
686  const Line3 scalarRay(queryRay);
687 
688  if (!boundingBox_.hasIntersection(scalarRay))
689  {
690  return;
691  }
692 
693  std::vector<const OctreeT<T>*>& nodes = reusableData.internalData_;
694  nodes.emplace_back(this);
695 
696  while (!nodes.empty())
697  {
698  const OctreeT<T>* node = nodes.back();
699  nodes.pop_back();
700 
701  ocean_assert(node != nullptr);
702  ocean_assert(node->boundingBox_.hasIntersection(scalarRay));
703 
704  if (node->childNodes_[0] != nullptr)
705  {
706  for (unsigned int n = 0u; n < 8u; ++n)
707  {
708  ocean_assert(node->childNodes_[n] != nullptr);
709  const OctreeT<T>& childNode = *node->childNodes_[n];
710 
711  if (childNode.isValid() && childNode.boundingBox_.hasIntersection(scalarRay))
712  {
713  nodes.emplace_back(&childNode);
714  }
715  }
716  }
717  else if (!node->pointIndices_.empty())
718  {
719  leafs.emplace_back(&node->pointIndices_);
720  }
721  }
722 }
723 
724 template <typename T>
725 void OctreeT<T>::intersectingLeafs(const LineT3<T>& queryRay, const Scalar tanHalfAngle, std::vector<const Indices32*>& leafs, const ReusableData& reusableData) const
726 {
727  ocean_assert(queryRay.isValid());
728  ocean_assert(tanHalfAngle >= 0 && tanHalfAngle < 1);
729  ocean_assert(leafs.empty());
730 
731  if (!isValid())
732  {
733  return;
734  }
735 
736  const Line3 scalarRay(queryRay);
737 
738  const Scalar& epsPerDistance = tanHalfAngle;
739 
740  if (!boundingBox_.hasIntersection(scalarRay, epsPerDistance))
741  {
742  return;
743  }
744 
745  std::vector<const OctreeT<T>*>& nodes = reusableData.internalData_;
746  nodes.emplace_back(this);
747 
748  while (!nodes.empty())
749  {
750  const OctreeT<T>* node = nodes.back();
751  nodes.pop_back();
752 
753  ocean_assert(node != nullptr);
754  ocean_assert(node->boundingBox_.hasIntersection(scalarRay, epsPerDistance));
755 
756  if (node->childNodes_[0] != nullptr)
757  {
758  for (unsigned int n = 0u; n < 8u; ++n)
759  {
760  ocean_assert(node->childNodes_[n] != nullptr);
761  const OctreeT<T>& childNode = *node->childNodes_[n];
762 
763  if (childNode.isValid() && childNode.boundingBox_.hasIntersection(scalarRay, epsPerDistance))
764  {
765  nodes.emplace_back(&childNode);
766  }
767  }
768  }
769  else if (!node->pointIndices_.empty())
770  {
771  leafs.emplace_back(&node->pointIndices_);
772  }
773  }
774 }
775 
776 template <typename T>
777 void OctreeT<T>::closestPoints(const VectorT3<T>* treePoints, const VectorT3<T>& queryPoint, const T maximalDistance, Indices32& pointIndices, VectorsT3<T>* points, const ReusableData& reusableData) const
778 {
779  ocean_assert(treePoints != nullptr);
780 
781  ocean_assert(pointIndices.empty());
782  ocean_assert(points == nullptr || points->empty());
783 
784  if (!isValid())
785  {
786  return;
787  }
788 
789  const Vector3 scalarPoint(queryPoint);
790 
791  const T maximalSqrDistance = Scalar(maximalDistance * maximalDistance);
792 
793  if (!boundingBox_.isInside(scalarPoint, Scalar(maximalDistance)))
794  {
795  return;
796  }
797 
798  std::vector<const OctreeT<T>*>& nodes = reusableData.internalData_;
799  nodes.emplace_back(this);
800 
801  while (!nodes.empty())
802  {
803  const OctreeT<T>* node = nodes.back();
804  nodes.pop_back();
805 
806  ocean_assert(node != nullptr);
807  ocean_assert(node->boundingBox_.isInside(scalarPoint, Scalar(maximalDistance)));
808 
809  if (node->childNodes_[0] != nullptr)
810  {
811  for (unsigned int n = 0u; n < 8u; ++n)
812  {
813  const OctreeT<T>& childNode = *node->childNodes_[n];
814 
815  if (childNode.isValid() && childNode.boundingBox_.isInside(scalarPoint, Scalar(maximalDistance)))
816  {
817  nodes.emplace_back(&childNode);
818  }
819  }
820  }
821  else
822  {
823  for (const Index32& pointIndex : node->pointIndices_)
824  {
825  const VectorT3<T>& treePoint = treePoints[pointIndex];
826 
827  if (points != nullptr)
828  {
829  if (treePoint.sqrDistance(queryPoint) <= maximalSqrDistance)
830  {
831  pointIndices.emplace_back(pointIndex);
832  points->emplace_back(treePoints[pointIndex]);
833  }
834  }
835  else
836  {
837  if (treePoint.sqrDistance(queryPoint) <= maximalSqrDistance)
838  {
839  pointIndices.emplace_back(pointIndex);
840  }
841  }
842  }
843  }
844  }
845 }
846 
847 template <typename T>
848 inline bool OctreeT<T>::isValid() const
849 {
850  return boundingBox_.isValid();
851 }
852 
853 template <typename T>
855 {
856  if (this != &octree)
857  {
858  boundingBox_ = octree.boundingBox_;
859  octree.boundingBox_ = BoundingBox();
860 
861  pointIndices_ = std::move(octree.pointIndices_);
862 
863  for (unsigned int n = 0u; n < 8u; ++n)
864  {
865  childNodes_[n] = octree.childNodes_[n];
866  octree.childNodes_[n] = nullptr;
867  }
868  }
869 
870  return *this;
871 }
872 
873 }
874 
875 }
876 
877 #endif // META_OCEAN_GEOMETRY_GRID_H
This class implements a 3D bounding box.
Definition: BoundingBox.h:23
bool isInside(const VectorT3< T > &point, const T eps=T(0)) const
Returns whether a given point is inside this bounding box.
bool isValid() const
Returns whether the bounding box is valid.
const VectorT3< T > & lower() const
Returns the lower corner of the box.
VectorT3< T > center() const
Returns the center of the box.
bool hasIntersection(const LineT3< T > &ray) const
Returns whether a given ray has an intersection with this box.
const VectorT3< T > & higher() const
Returns the higher corner of the box.
This class stores construction parameters for an octree.
Definition: Octree.h:65
bool useTightBoundingBoxes_
True, to use tight bounding boxes for each individual node (only covering the actual points); False,...
Definition: Octree.h:92
bool isValid() const
Returns whether this object holds valid parameters.
Definition: Octree.h:270
Parameters()=default
Default constructor.
unsigned int maximalPointsPerLeaf_
The maximal number of points each leaf node can have.
Definition: Octree.h:89
Definition of a class which holds reusable data for internal use.
Definition: Octree.h:111
std::vector< const OctreeT< T > * > internalData_
The internal reusable data.
Definition: Octree.h:124
ReusableData()=default
Creates a new object.
Forward declaration.
Definition: Octree.h:55
void closestLeafs(const VectorT3< T > &queryPoint, const T maximalDistance, std::vector< const Indices32 * > &leafs, const ReusableData &reusableData=ReusableData()) const
Returns the closest leaf nodes for a given query point.
Definition: Octree.h:628
Indices32 pointIndices_
The indicies of the tree points which belong to this leaf node, empty if this node is not a leaf node...
Definition: Octree.h:255
void intersectingLeafs(const LineT3< T > &queryRay, std::vector< const Indices32 * > &leafs, const ReusableData &reusableData=ReusableData()) const
Returns the intersecting leaf nodes for a given query ray.
Definition: Octree.h:676
OctreeT< T > * childNodes_[8]
The eight child nodes.
Definition: Octree.h:258
OctreeT(const OctreeT &octree)=delete
Disabled copy constructor.
OctreeT()=default
Default constructor creating an empty tree.
T Type
The data type of this octree.
Definition: Octree.h:59
OctreeT & operator=(OctreeT &&octree)
Move operator.
Definition: Octree.h:854
const BoundingBox & boundingBox() const
Returns the bounding box containing all points of this node (of all points in all child leaf nodes)
Definition: Octree.h:605
~OctreeT()
Destructs this tree node.
Definition: Octree.h:596
void closestPoints(const VectorT3< T > *treePoints, const VectorT3< T > &queryPoint, const T maximalDistance, Indices32 &pointIndices, VectorsT3< T > *points=nullptr, const ReusableData &reusableData=ReusableData()) const
Returns the closest tree points for a given query point.
Definition: Octree.h:777
BoundingBox boundingBox_
The bounding box of this tree node.
Definition: Octree.h:252
OctreeT & operator=(const OctreeT &octree)=delete
Disabled copy constructor.
const Indices32 & pointIndices() const
Returns the indices of the tree points which belong to this leaf node.
Definition: Octree.h:611
const OctreeT< T > *const * childNodes() const
Returns the eight child nodes of this tree node.
Definition: Octree.h:617
bool isValid() const
Returns whether this node is valid (if this node has a valid bounding box)
Definition: Octree.h:848
This class implements an infinite line in 3D space.
Definition: Line3.h:70
bool isValid() const
Returns whether this line has valid parameters.
Definition: Line3.h:303
This class implements a vector with three elements.
Definition: Vector3.h:97
const T & y() const noexcept
Returns the y value.
Definition: Vector3.h:812
const T & x() const noexcept
Returns the x value.
Definition: Vector3.h:800
const T & z() const noexcept
Returns the z value.
Definition: Vector3.h:824
const T * data() const noexcept
Returns an pointer to the vector elements.
Definition: Vector3.h:842
T sqrDistance(const VectorT3< T > &right) const
Returns the square distance between this 3D position and a second 3D position.
Definition: Vector3.h:682
std::vector< Index32 > Indices32
Definition of a vector holding 32 bit index values.
Definition: Base.h:96
uint32_t Index32
Definition of a 32 bit index value.
Definition: Base.h:84
OctreeT< double > OctreeD
Definition of an Octree using double as data type.
Definition: Octree.h:39
OctreeT< float > OctreeF
Definition of an Octree using float as data type.
Definition: Octree.h:46
OctreeT< Scalar > Octree
Definition of an Octree using Scalar as data type.
Definition: Octree.h:25
float Scalar
Definition of a scalar type.
Definition: Math.h:128
VectorT3< Scalar > Vector3
Definition of a 3D vector.
Definition: Vector3.h:22
std::vector< VectorT3< T > > VectorsT3
Definition of a typename alias for vectors with VectorT3 objects.
Definition: Vector3.h:58
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15