PhysicsBasedAnimationToolkit 0.0.10
Cross-platform C++20 library of algorithms and data structures commonly used in computer graphics research on physically-based simulation.
Loading...
Searching...
No Matches
AabbRadixTreeHierarchy.h
1#ifndef PBAT_GEOMETRY_AABBRADIXTREEHIERARCHY_H
2#define PBAT_GEOMETRY_AABBRADIXTREEHIERARCHY_H
3
5#include "SpatialSearch.h"
6#include "pbat/Aliases.h"
10#include "pbat/common/Permute.h"
13#include "pbat/math/linalg/mini/Eigen.h"
15
16#include <cpp-sort/sorters/ska_sorter.h>
17#include <numeric>
18
19namespace pbat::geometry {
33template <auto Dims>
34class AabbRadixTreeHierarchy
35{
36public:
37 using IndexType = Index;
38 static auto constexpr kDims = Dims;
39 using SelfType = AabbRadixTreeHierarchy<kDims>;
40
41 AabbRadixTreeHierarchy() = default;
51 template <class TDerivedL, class TDerivedU>
53 Eigen::DenseBase<TDerivedL> const& L,
54 Eigen::DenseBase<TDerivedU> const& U);
66 template <class TDerivedL, class TDerivedU>
67 void Construct(Eigen::DenseBase<TDerivedL> const& L, Eigen::DenseBase<TDerivedU> const& U);
80 template <class TDerivedL, class TDerivedU>
81 void Update(Eigen::DenseBase<TDerivedL> const& L, Eigen::DenseBase<TDerivedU> const& U);
93 template <class FNodeOverlaps, class FObjectOverlaps, class FOnOverlap>
94 void
96 FNodeOverlaps fNodeOverlaps,
97 FObjectOverlaps fObjectOverlaps,
98 FOnOverlap fOnOverlap)
99 const;
114 template <class FDistanceToNode, class FDistanceToObject, class FOnNearestNeighbour>
116 FDistanceToNode fDistanceToNode,
117 FDistanceToObject fDistanceToObject,
118 FOnNearestNeighbour fOnNearestNeighbour,
119 Scalar radius = std::numeric_limits<Scalar>::max(),
120 Scalar eps = Scalar(0)) const;
134 template <class FDistanceToNode, class FDistanceToObject, class FOnNearestNeighbour>
136 FDistanceToNode fDistanceToNode,
137 FDistanceToObject fDistanceToObject,
138 FOnNearestNeighbour fOnNearestNeighbour,
139 Index K,
140 Scalar radius = std::numeric_limits<Scalar>::max()) const;
149 template <class FObjectsOverlap, class FOnSelfOverlap>
150 void SelfOverlaps(FObjectsOverlap fObjectsOverlap, FOnSelfOverlap fOnSelfOverlap) const;
162 template <class FObjectsOverlap, class FOnOverlap>
163 void
164 Overlaps(SelfType const& rhs, FObjectsOverlap fObjectsOverlap, FOnOverlap fOnOverlap) const;
165
172 auto InternalNodeBoundingBoxes() const { return IB; }
177 auto Tree() const -> common::BinaryRadixTree<IndexType> const& { return tree; }
178
179protected:
189 template <class TDerivedL, class TDerivedU>
190 void
191 ComputeMortonCodes(Eigen::DenseBase<TDerivedL> const& L, Eigen::DenseBase<TDerivedU> const& U);
196
197private:
198 Eigen::Vector<MortonCodeType, Eigen::Dynamic> codes;
199 Eigen::Vector<IndexType, Eigen::Dynamic> inds;
201 IB;
206};
207
208template <auto Dims>
209template <class TDerivedL, class TDerivedU>
210inline AabbRadixTreeHierarchy<Dims>::AabbRadixTreeHierarchy(
211 Eigen::DenseBase<TDerivedL> const& L,
212 Eigen::DenseBase<TDerivedU> const& U)
213{
214 Construct(L.derived(), U.derived());
215}
216
217template <auto Dims>
218template <class TDerivedL, class TDerivedU>
220 Eigen::DenseBase<TDerivedL> const& L,
221 Eigen::DenseBase<TDerivedU> const& U)
222{
223 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.AabbRadixTreeHierarchy.Construct");
224 auto const n = L.cols();
225 codes.resize(n);
226 inds.resize(n);
227 ComputeMortonCodes(L.derived(), U.derived());
229 tree.Construct(codes);
230 IB.resize(2 * kDims, tree.InternalNodeCount());
231}
232
233template <auto Dims>
234template <class TDerivedL, class TDerivedU>
236 Eigen::DenseBase<TDerivedL> const& L,
237 Eigen::DenseBase<TDerivedU> const& U)
238{
239 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.AabbRadixTreeHierarchy.Update");
240 IB.template topRows<kDims>().setConstant(std::numeric_limits<Scalar>::max());
241 IB.template bottomRows<kDims>().setConstant(std::numeric_limits<Scalar>::lowest());
243 [&](Index n) {
244 // n is guaranteed to be internal node, due to our fChild functor
245 auto const lc = tree.Left(n);
246 auto const rc = tree.Right(n);
247 Vector<kDims> LL, LU, RL, RU;
248 if (tree.IsLeaf(lc))
249 {
250 auto i = inds(tree.CodeIndex(lc));
251 LL = L.col(i).template head<kDims>();
252 LU = U.col(i).template head<kDims>();
253 }
254 else
255 {
256 LL = IB.col(lc).template head<kDims>();
257 LU = IB.col(lc).template tail<kDims>();
258 }
259 if (tree.IsLeaf(rc))
260 {
261 auto i = inds(tree.CodeIndex(rc));
262 RL = L.col(i).template head<kDims>();
263 RU = U.col(i).template head<kDims>();
264 }
265 else
266 {
267 RL = IB.col(rc).template head<kDims>();
268 RU = IB.col(rc).template tail<kDims>();
269 }
270 IB.col(n).template head<kDims>() = LL.cwiseMin(RL);
271 IB.col(n).template tail<kDims>() = LU.cwiseMax(RU);
272 },
273 // fChild functor that only returns non-leaf children
274 [&]<auto c>(Index n) -> Index {
275 if constexpr (c == 0)
276 return tree.IsLeaf(tree.Left(n)) ? -1 : tree.Left(n);
277 else
278 return tree.IsLeaf(tree.Right(n)) ? -1 : tree.Right(n);
279 });
280}
281
282template <auto Dims>
283template <class FNodeOverlaps, class FObjectOverlaps, class FOnOverlap>
285 FNodeOverlaps fNodeOverlaps,
286 FObjectOverlaps fObjectOverlaps,
287 FOnOverlap fOnOverlap) const
288{
289 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.AabbRadixTreeHierarchy.Overlaps");
291 [&]<auto c>(Index n) -> Index {
292 if constexpr (c == 0)
293 return tree.IsLeaf(n) ? -1 : tree.Left(n);
294 else
295 return tree.IsLeaf(n) ? -1 : tree.Right(n);
296 },
297 [&](Index n) { return tree.IsLeaf(n); },
298 []([[maybe_unused]] Index n) { return 1; },
299 [&](Index n, [[maybe_unused]] Index i) { return inds(tree.CodeIndex(n)); },
300 [&](Index n) {
301 if (tree.IsLeaf(n))
302 return true; // Radix tree leaf nodes correspond to individual objects
303 auto L = IB.col(n).template head<kDims>();
304 auto U = IB.col(n).template tail<kDims>();
305 using TDerivedL = decltype(L);
306 using TDerivedU = decltype(U);
307 return fNodeOverlaps.template operator()<TDerivedL, TDerivedU>(L, U);
308 },
309 fObjectOverlaps,
310 fOnOverlap);
311}
312
313template <auto Dims>
314template <class FDistanceToNode, class FDistanceToObject, class FOnNearestNeighbour>
316 FDistanceToNode fDistanceToNode,
317 FDistanceToObject fDistanceToObject,
318 FOnNearestNeighbour fOnNearestNeighbour,
319 Scalar radius,
320 Scalar eps) const
321{
322 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.AabbRadixTreeHierarchy.NearestNeighbours");
324 [&]<auto c>(Index n) -> Index {
325 if constexpr (c == 0)
326 return tree.IsLeaf(n) ? -1 : tree.Left(n);
327 else
328 return tree.IsLeaf(n) ? -1 : tree.Right(n);
329 },
330 [&](Index n) { return tree.IsLeaf(n); },
331 []([[maybe_unused]] Index n) { return 1; },
332 [&](Index n, [[maybe_unused]] Index i) { return inds(tree.CodeIndex(n)); },
333 [&](Index n) {
334 using TDerivedL = decltype(IB.col(n).template head<kDims>());
335 using TDerivedU = decltype(IB.col(n).template tail<kDims>());
336 using ScalarType = std::invoke_result_t<FDistanceToNode, TDerivedL, TDerivedU>;
337 if (tree.IsLeaf(n))
338 return ScalarType(0); // Radix tree leaf nodes correspond to individual objects
339 auto L = IB.col(n).template head<kDims>();
340 auto U = IB.col(n).template tail<kDims>();
341 return fDistanceToNode.template operator()<TDerivedL, TDerivedU>(L, U);
342 },
343 fDistanceToObject,
344 fOnNearestNeighbour,
345 false /*bUseBestFirstSearch*/,
346 radius,
347 eps);
348}
349
350template <auto Dims>
351template <class FDistanceToNode, class FDistanceToObject, class FOnNearestNeighbour>
353 FDistanceToNode fDistanceToNode,
354 FDistanceToObject fDistanceToObject,
355 FOnNearestNeighbour fOnNearestNeighbour,
356 Index K,
357 Scalar radius) const
358{
359 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.AabbRadixTreeHierarchy.KNearestNeighbours");
361 [&]<auto c>(Index n) -> Index {
362 if constexpr (c == 0)
363 return tree.IsLeaf(n) ? -1 : tree.Left(n);
364 else
365 return tree.IsLeaf(n) ? -1 : tree.Right(n);
366 },
367 [&](Index n) { return tree.IsLeaf(n); },
368 []([[maybe_unused]] Index n) { return 1; },
369 [&](Index n, [[maybe_unused]] Index i) { return inds(tree.CodeIndex(n)); },
370 [&](Index n) {
371 using TDerivedL = decltype(IB.col(n).template head<kDims>());
372 using TDerivedU = decltype(IB.col(n).template tail<kDims>());
373 using ScalarType = std::invoke_result_t<FDistanceToNode, TDerivedL, TDerivedU>;
374 if (tree.IsLeaf(n))
375 return ScalarType(0); // Radix tree leaf nodes correspond to individual objects
376 auto L = IB.col(n).template head<kDims>();
377 auto U = IB.col(n).template tail<kDims>();
378 return fDistanceToNode.template operator()<TDerivedL, TDerivedU>(L, U);
379 },
380 fDistanceToObject,
381 fOnNearestNeighbour,
382 K,
383 radius);
384}
385
386template <auto Dims>
387template <class FObjectsOverlap, class FOnSelfOverlap>
389 FObjectsOverlap fObjectsOverlap,
390 FOnSelfOverlap fOnSelfOverlap) const
391{
392 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.AabbRadixTreeHierarchy.SelfOverlaps");
394 [&]<auto c>(Index n) -> Index {
395 if constexpr (c == 0)
396 return tree.IsLeaf(n) ? -1 : tree.Left(n);
397 else
398 return tree.IsLeaf(n) ? -1 : tree.Right(n);
399 },
400 [&](Index n) { return tree.IsLeaf(n); },
401 []([[maybe_unused]] Index n) { return 1; },
402 [&](Index n, [[maybe_unused]] Index i) { return inds(tree.CodeIndex(n)); },
403 [&](Index n1, Index n2) {
404 if (tree.IsLeaf(n1) or tree.IsLeaf(n2))
405 return true; // Radix tree leaf nodes correspond to individual objects
406 using math::linalg::mini::FromEigen;
407 auto L1 = IB.col(n1).template head<kDims>();
408 auto U1 = IB.col(n1).template tail<kDims>();
409 auto L2 = IB.col(n2).template head<kDims>();
410 auto U2 = IB.col(n2).template tail<kDims>();
412 FromEigen(L1),
413 FromEigen(U1),
414 FromEigen(L2),
415 FromEigen(U2));
416 },
417 fObjectsOverlap,
418 fOnSelfOverlap);
419}
420
421template <auto Dims>
422template <class FObjectsOverlap, class FOnOverlap>
424 SelfType const& rhs,
425 FObjectsOverlap fObjectsOverlap,
426 FOnOverlap fOnOverlap) const
427{
428 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.AabbRadixTreeHierarchy.Overlaps");
429 // This tree will be the left-hand side tree
430 auto const fChildLhs = [&]<auto c>(Index n) -> Index {
431 if constexpr (c == 0)
432 return tree.IsLeaf(n) ? -1 : tree.Left(n);
433 else
434 return tree.IsLeaf(n) ? -1 : tree.Right(n);
435 };
436 auto const fIsLeafLhs = [&](Index n) {
437 return tree.IsLeaf(n);
438 };
439 auto const fLeafSizeLhs = []([[maybe_unused]] Index n) {
440 return 1;
441 };
442 auto const fLeafObjectLhs = [&](Index n, [[maybe_unused]] Index i) {
443 return inds(tree.CodeIndex(n));
444 };
445 // This tree will be the right-hand side tree
446 auto const fChildRhs = [&]<auto c>(Index n) -> Index {
447 if constexpr (c == 0)
448 return rhs.tree.IsLeaf(n) ? -1 : rhs.tree.Left(n);
449 else
450 return rhs.tree.IsLeaf(n) ? -1 : rhs.tree.Right(n);
451 };
452 auto const fIsLeafRhs = [&](Index n) {
453 return rhs.tree.IsLeaf(n);
454 };
455 auto const fLeafSizeRhs = []([[maybe_unused]] Index n) {
456 return 1;
457 };
458 auto const fLeafObjectRhs = [&](Index n, [[maybe_unused]] Index i) {
459 return rhs.inds(rhs.tree.CodeIndex(n));
460 };
461 // Register overlaps
463 fChildLhs,
464 fIsLeafLhs,
465 fLeafSizeLhs,
466 fLeafObjectLhs,
467 fChildRhs,
468 fIsLeafRhs,
469 fLeafSizeRhs,
470 fLeafObjectRhs,
471 [&](Index n1, Index n2) {
472 if (tree.IsLeaf(n1) or rhs.tree.IsLeaf(n2))
473 return true; // Radix tree leaf nodes correspond to individual objects
474 using math::linalg::mini::FromEigen;
475 auto L1 = IB.col(n1).template head<kDims>();
476 auto U1 = IB.col(n1).template tail<kDims>();
477 auto L2 = rhs.IB.col(n2).template head<kDims>();
478 auto U2 = rhs.IB.col(n2).template tail<kDims>();
480 FromEigen(L1),
481 FromEigen(U1),
482 FromEigen(L2),
483 FromEigen(U2));
484 },
485 fObjectsOverlap,
486 fOnOverlap);
487}
488
489template <auto Dims>
490template <class TDerivedL, class TDerivedU>
492 Eigen::DenseBase<TDerivedL> const& L,
493 Eigen::DenseBase<TDerivedU> const& U)
494{
495 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.AabbRadixTreeHierarchy.ComputeMortonCodes");
496 Vector<kDims> min = L.rowwise().minCoeff();
497 Vector<kDims> max = U.rowwise().maxCoeff();
498 Vector<kDims> extents = max - min;
499 auto C = 0.5 * (L.derived() + U.derived());
500 for (Index i = 0; i < L.cols(); ++i)
501 {
502 Vector<kDims> center;
503 common::ForRange<0, kDims>([&]<auto d>() { center(d) = (C(d, i) - min(d)) / extents(d); });
504 codes(i) = Morton3D(center);
505 }
506}
507
508template <auto Dims>
510{
511 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.AabbRadixTreeHierarchy.SortMortonCodes");
512 // NOTE:
513 // It would potentially be more efficient not to re-sort from scratch every time, due to
514 // this std::iota call. If we kept the inds ordering between calls, we would benefit from
515 // significant performance speedups of certain sorting algorithms on nearly sorted input.
516 // However, this would mean that in the call to ComputeMortonCodes, we would have to
517 // recompute the Morton codes in the same order as the previous call, which will not
518 // exploit cache locality of iterating through B's columns. This is a trade-off that
519 // should be considered if performance is critical.
520 std::iota(inds.begin(), inds.end(), 0);
521 cppsort::ska_sort(inds.begin(), inds.end(), [&](IndexType i) { return codes(i); });
522 common::Permute(codes.begin(), codes.end(), inds.begin());
523}
524} // namespace pbat::geometry
525
526#endif // PBAT_GEOMETRY_AABBRADIXTREEHIERARCHY_H
Axis-aligned bounding box class.
Radix Tree implementation.
Compile-time for loops.
This file contains functions to compute Morton codes.
Generic n-ary tree traversals.
This file contains functions to answer overlap queries.
Permutation of values in-place.
Generic efficient spatial search query implementations.
Binary radix tree implementation.
Definition BinaryRadixTree.h:28
TIndex CodeIndex(TIndex leaf) const
Get the index of the code associated with a leaf node.
Definition BinaryRadixTree.h:150
TIndex Right(TIndex i) const
Get the right child of internal node i
Definition BinaryRadixTree.h:111
TIndex Left(TIndex i) const
Get the left child of internal node i
Definition BinaryRadixTree.h:103
bool IsLeaf(TIndex i) const
Check if a node is a leaf.
Definition BinaryRadixTree.h:137
void SelfOverlaps(FObjectsOverlap fObjectsOverlap, FOnSelfOverlap fOnSelfOverlap) const
Find all object pairs that overlap.
Definition AabbRadixTreeHierarchy.h:388
static auto constexpr kDims
Definition AabbRadixTreeHierarchy.h:38
void NearestNeighbours(FDistanceToNode fDistanceToNode, FDistanceToObject fDistanceToObject, FOnNearestNeighbour fOnNearestNeighbour, Scalar radius=std::numeric_limits< Scalar >::max(), Scalar eps=Scalar(0)) const
Find the nearest neighbour to some user-defined query. If there are multiple nearest neighbours,...
Definition AabbRadixTreeHierarchy.h:315
void ComputeMortonCodes(Eigen::DenseBase< TDerivedL > const &L, Eigen::DenseBase< TDerivedU > const &U)
Compute Morton codes for the AABBs.
Definition AabbRadixTreeHierarchy.h:491
void Overlaps(SelfType const &rhs, FObjectsOverlap fObjectsOverlap, FOnOverlap fOnOverlap) const
Find all object pairs that overlap with another hierarchy.
Definition AabbRadixTreeHierarchy.h:423
void SortMortonCodes()
Sort the Morton codes.
Definition AabbRadixTreeHierarchy.h:509
AabbRadixTreeHierarchy(Eigen::DenseBase< TDerivedL > const &L, Eigen::DenseBase< TDerivedU > const &U)
Construct an AabbRadixTreeHierarchy from an input AABB matrix B.
Definition AabbRadixTreeHierarchy.h:210
auto InternalNodeBoundingBoxes() const
Get the internal node bounding boxes.
Definition AabbRadixTreeHierarchy.h:172
void Construct(Eigen::DenseBase< TDerivedL > const &L, Eigen::DenseBase< TDerivedU > const &U)
Construct an AabbRadixTreeHierarchy from an input AABB matrix B.
Definition AabbRadixTreeHierarchy.h:219
void KNearestNeighbours(FDistanceToNode fDistanceToNode, FDistanceToObject fDistanceToObject, FOnNearestNeighbour fOnNearestNeighbour, Index K, Scalar radius=std::numeric_limits< Scalar >::max()) const
Find the K nearest neighbours to some user-defined query.
Definition AabbRadixTreeHierarchy.h:352
auto Tree() const -> common::BinaryRadixTree< IndexType > const &
Get the underlying tree hierarchy.
Definition AabbRadixTreeHierarchy.h:177
Index IndexType
Type of the indices.
Definition AabbRadixTreeHierarchy.h:37
void Overlaps(FNodeOverlaps fNodeOverlaps, FObjectOverlaps fObjectOverlaps, FOnOverlap fOnOverlap) const
Find all objects that overlap with some user-defined query.
Definition AabbRadixTreeHierarchy.h:284
AabbRadixTreeHierarchy< kDims > SelfType
Type of this template instantiation.
Definition AabbRadixTreeHierarchy.h:39
void Update(Eigen::DenseBase< TDerivedL > const &L, Eigen::DenseBase< TDerivedU > const &U)
Recomputes k-D tree node AABBs given the object AABBs.
Definition AabbRadixTreeHierarchy.h:235
Common functionality.
Definition ArgSort.h:20
void Permute(TValuesBegin vb, TValuesEnd ve, TPermutationBegin pb)
Permute the values in-place according to the permutation.
Definition Permute.h:39
PBAT_HOST_DEVICE void TraverseNAryTreePseudoPostOrder(FVisit fVisit, FChild fChild, TIndex root=0)
Post-order traversal over an n-ary tree starting from root.
Definition NAryTreeTraversal.h:84
constexpr void ForRange(F &&f)
Compile-time for loop over a range of values.
Definition ConstexprFor.h:55
PBAT_HOST_DEVICE bool AxisAlignedBoundingBoxes(TMatrixL1 const &L1, TMatrixU1 const &U1, TMatrixL2 const &L2, TMatrixU2 const &U2)
Tests for overlap between axis-aligned bounding box (L1,U1) and axis-aligned bounding box (L2,...
Definition OverlapQueries.h:608
Geometric queries, quantities and data structures.
Definition AabbKdTreeHierarchy.h:23
PBAT_HOST_DEVICE bool KNearestNeighbours(FChild fChild, FIsLeaf fIsLeaf, FLeafSize fLeafSize, FLeafObject fLeafObject, FDistanceLowerBound fLower, FDistance fDistance, FOnFound fOnFound, TIndex K, TScalar fUpper=std::numeric_limits< TScalar >::max(), TIndex root=0)
Find the (sorted) K objects with the smallest distances in a branch and bound tree rooted in root.
Definition SpatialSearch.h:1050
PBAT_HOST_DEVICE bool DfsAllNearestNeighbours(FChild fChild, FIsLeaf fIsLeaf, FLeafSize fLeafSize, FLeafObject fLeafObject, FDistanceLowerBound fLower, FDistance fDistance, FOnFound fOnFound, bool bUseBestFirstSearch=false, TScalar fUpper=std::numeric_limits< TScalar >::max(), TScalar eps=TScalar(0), TIndex root=0)
Find distance minimizing objects in branch and bound tree rooted in root.
Definition SpatialSearch.h:763
PBAT_HOST_DEVICE MortonCodeType Morton3D(Point x)
Calculates a 30-bit Morton code for the given 3D point located within the unit cube [0,...
Definition Morton.h:69
PBAT_HOST_DEVICE bool SelfOverlaps(FChild fChild, FIsLeaf fIsLeaf, FLeafSize fLeafSize, FLeafObject fLeafObject, FNodesOverlap fNodesOverlap, FObjectsOverlap fObjectsOverlap, FOnFound fOnFound, TIndex root=0)
Compute overlapping nodes from a branch and bound tree rooted in root.
Definition SpatialSearch.h:612
PBAT_HOST_DEVICE bool Overlaps(FChild fChild, FIsLeaf fIsLeaf, FLeafSize fLeafSize, FLeafObject fLeafObject, FNodeOverlap fNodeOverlaps, FObjectOverlap fObjectOverlaps, FOnFound fOnFound, TIndex root=0)
Find all nodes in a branch and bound tree that overlap with a given object.
Definition SpatialSearch.h:438
Eigen::Vector< Scalar, N > Vector
Fixed-size vector type.
Definition Aliases.h:24
std::ptrdiff_t Index
Index type.
Definition Aliases.h:17
double Scalar
Scalar type.
Definition Aliases.h:18
Eigen::Matrix< Scalar, Rows, Cols > Matrix
Fixed-size matrix type.
Definition Aliases.h:31
Profiling utilities for the Physics-Based Animation Toolkit (PBAT)