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
KdTree.h
Go to the documentation of this file.
1
10
11#ifndef PBAT_GEOMETRY_KDTREE_H
12#define PBAT_GEOMETRY_KDTREE_H
13
16#include "pbat/common/Stack.h"
17
18#include <algorithm>
19#include <pbat/Aliases.h>
21#include <ranges>
22#include <vector>
23
24namespace pbat {
25namespace geometry {
26
31{
32 enum { kLeafNodeLeftChild = -2 };
36 Index c{kLeafNodeLeftChild};
38
42 [[maybe_unused]] bool IsLeaf() const { return c == -2; }
47 [[maybe_unused]] bool IsInternal() const { return not IsLeaf(); }
52 [[maybe_unused]] auto Left() const { return c; }
57 [[maybe_unused]] auto Right() const { return c + 1; }
58};
59
64template <int Dims>
65class KdTree
66{
67 public:
68 KdTree() = default;
69
76 template <class TDerivedP>
77 KdTree(Eigen::DenseBase<TDerivedP> const& P, Index maxPointsInLeaf = 8);
103 template <class TDerivedP>
104 void Construct(Eigen::DenseBase<TDerivedP> const& P, Index maxPointsInLeaf);
113 template <class FVisit>
114 void DepthFirstSearch(FVisit visit, Index root = 0) const;
115
120 std::vector<KdTreeNode> const& Nodes() const { return mNodes; }
129 IndexVectorX const& Permutation() const { return mPermutation; }
135 auto PointsInNode(Index const nodeIdx) const;
141 auto PointsInNode(KdTreeNode const& node) const;
146 Index constexpr Root() const { return 0; }
147
148 private:
149 IndexVectorX mPermutation;
152 std::vector<KdTreeNode> mNodes;
153};
154
155template <int Dims>
156template <class TDerivedP>
157inline KdTree<Dims>::KdTree(Eigen::DenseBase<TDerivedP> const& P, Index maxPointsInLeaf)
158{
159 Construct(P, maxPointsInLeaf);
160}
161
162template <int Dims>
163template <class TDerivedP>
164inline void KdTree<Dims>::Construct(Eigen::DenseBase<TDerivedP> const& P, Index maxPointsInLeaf)
165{
166 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.KdTree.Construct");
167 // Our k-D tree is a full binary tree (i.e. # nodes = 2*leaves - 1). We try to estimate
168 // the number of leaf nodes to reserve memory up-front and prevent any reallocation, but without
169 // excessively using up memory. We estimate the number of nodes per leaf to be 80% of the
170 // maximum number of points in a leaf node.
171 Index const nPoints = P.cols();
172 Scalar constexpr kEstimatedLeafOccupancy = 0.8;
173 auto const nEstimatedNodesPerLeaf =
174 (maxPointsInLeaf > 1) ?
175 static_cast<Index>(
176 std::ceil(kEstimatedLeafOccupancy * static_cast<Scalar>(maxPointsInLeaf))) :
177 1;
178 auto const nEstimatedLeafNodes =
179 (nPoints / nEstimatedNodesPerLeaf) + (nPoints % nEstimatedNodesPerLeaf != 0);
180 mNodes.clear();
181#include "pbat/warning/Push.h"
182#include "pbat/warning/SignConversion.h"
183 mNodes.reserve(2 * nEstimatedLeafNodes - 1);
184 auto iota = std::views::iota(Index{0}, nPoints);
185 mPermutation.resize(nPoints);
186 std::copy(iota.begin(), iota.end(), mPermutation.data());
187
188 // Top-down construction of the k-D tree
189 struct StackFrame
190 {
192 Index nodeIdx;
193 Index begin;
194 Index n;
195 };
197 Index constexpr root = 0;
198 stack.Push({geometry::AxisAlignedBoundingBox<Dims>{P}, root, 0, nPoints});
199 mNodes.emplace_back(0, nPoints, KdTreeNode::kLeafNodeLeftChild);
200 while (not stack.IsEmpty())
201 {
202 auto const [aabb, nodeIdx, begin, n] = stack.Pop();
203 if (n <= maxPointsInLeaf)
204 continue;
205
206 // Find the dimension with the largest extent
207 Eigen::Index dimension{};
208 (aabb.max() - aabb.min()).maxCoeff(&dimension);
209 // Partition the points along the dimension on left/right sides of median
210 Index const halfn = n / 2;
211 std::nth_element(
212 mPermutation.data() + begin,
213 mPermutation.data() + begin + halfn,
214 mPermutation.data() + begin + n,
215 [&](Index const lhs, Index const rhs) {
216 return P(dimension, lhs) < P(dimension, rhs);
217 });
218 // Set the left (and implicitly right) child index of the current node
219 mNodes[nodeIdx].c = static_cast<Index>(mNodes.size());
220 // Split bounding box into child boxes
221 Scalar const split = P(dimension, mPermutation[begin + halfn]);
222 AxisAlignedBoundingBox laabb = aabb;
223 laabb.max()(dimension) = split;
224 AxisAlignedBoundingBox raabb = aabb;
225 raabb.min()(dimension) = split;
226 // Store left/right node contiguously in memory
227 mNodes.emplace_back(begin, halfn, KdTreeNode::kLeafNodeLeftChild);
228 mNodes.emplace_back(begin + halfn, n - halfn, KdTreeNode::kLeafNodeLeftChild);
229 // Schedule left and right sub-tree constructions
230 stack.Push({laabb, mNodes[nodeIdx].Left(), begin, halfn});
231 stack.Push({raabb, mNodes[nodeIdx].Right(), begin + halfn, n - halfn});
232 }
233#include "pbat/warning/Pop.h"
234}
235
236template <int Dims>
237template <class FVisit>
238inline void KdTree<Dims>::DepthFirstSearch(FVisit fVisit, Index root) const
239{
240 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.KdTree.DepthFirstSearch");
241#include "pbat/warning/Push.h"
242#include "pbat/warning/SignConversion.h"
244 [&](Index node) { return fVisit(node, mNodes[node]); },
245 [&]<auto c>(Index node) {
246 if constexpr (c == 0)
247 return mNodes[node].Left();
248 else
249 return mNodes[node].Right();
250 },
251 root);
252#include "pbat/warning/Pop.h"
253}
254
255template <int Dims>
256inline auto KdTree<Dims>::PointsInNode(Index const nodeIdx) const
257{
258 auto const& node = mNodes[static_cast<std::size_t>(nodeIdx)];
259 return PointsInNode(node);
260}
261
262template <int Dims>
263inline auto KdTree<Dims>::PointsInNode(KdTreeNode const& node) const
264{
265 namespace vi = std::views;
266 auto indrng = mPermutation | vi::drop(node.begin) | vi::take(node.n);
267 return indrng;
268}
269
270} // namespace geometry
271} // namespace pbat
272
273#endif // PBAT_GEOMETRY_KDTREE_H
Axis-aligned bounding box class.
Generic n-ary tree traversals.
Fixed-size stack implementation.
Definition Stack.h:26
Axis-aligned bounding box class.
Definition AxisAlignedBoundingBox.h:29
std::vector< KdTreeNode > const & Nodes() const
Returns the nodes of the k-D tree.
Definition KdTree.h:120
void DepthFirstSearch(FVisit visit, Index root=0) const
Depth-first search over the k-D tree.
Definition KdTree.h:238
auto PointsInNode(KdTreeNode const &node) const
Returns the points in a node.
Definition KdTree.h:263
IndexVectorX const & Permutation() const
Returns the permutation of the points in the k-D tree.
Definition KdTree.h:129
auto PointsInNode(Index const nodeIdx) const
Returns the points in a node.
Definition KdTree.h:256
void Construct(Eigen::DenseBase< TDerivedP > const &P, Index maxPointsInLeaf)
Construct a k-D tree from a set of points.
Definition KdTree.h:164
KdTree(Eigen::DenseBase< TDerivedP > const &P, Index maxPointsInLeaf=8)
Construct a k-D tree from a set of points.
Definition KdTree.h:157
Index constexpr Root() const
Returns the root node index.
Definition KdTree.h:146
Fixed-size stack implementation usable in both host and device code.
PBAT_HOST_DEVICE void TraverseNAryTreePseudoPreOrder(FVisit fVisit, FChild fChild, TIndex root=0)
Pre-order traversal over an n-ary tree starting from root.
Definition NAryTreeTraversal.h:65
Geometric queries, quantities and data structures.
Definition AabbKdTreeHierarchy.h:23
The main namespace of the library.
Definition Aliases.h:15
Eigen::Vector< Index, Eigen::Dynamic > IndexVectorX
Dynamic-size index vector type.
Definition Aliases.h:49
std::ptrdiff_t Index
Index type.
Definition Aliases.h:17
double Scalar
Scalar type.
Definition Aliases.h:18
Profiling utilities for the Physics-Based Animation Toolkit (PBAT)
Node of a KDTree.
Definition KdTree.h:31
bool IsInternal() const
Returns true if this node is an internal node, false otherwise.
Definition KdTree.h:47
Index begin
Index to first point encapsulated in this node's AABB in the permutation list.
Definition KdTree.h:33
bool IsLeaf() const
Returns true if this node is a leaf node, false otherwise.
Definition KdTree.h:42
Index n
Definition KdTree.h:34
auto Right() const
Returns right child node.
Definition KdTree.h:57
Index c
Definition KdTree.h:36
auto Left() const
Returns left child node.
Definition KdTree.h:52