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
HierarchicalHashGrid.h
1#ifndef PBAT_GEOMETRY_HIERARCHICALHASHGRID_H
2#define PBAT_GEOMETRY_HIERARCHICALHASHGRID_H
3
4#include "pbat/Aliases.h"
7#include "pbat/common/Eigen.h"
10#include "pbat/math/linalg/mini/Eigen.h"
12
13#include <Eigen/Core>
14#include <algorithm>
15#include <cassert>
16#include <limits>
17#include <span>
18
19namespace pbat::geometry {
28template <int Dims, common::CFloatingPoint TScalar = Scalar, common::CIndex TIndex = Index>
30{
31public:
32 using ScalarType = TScalar;
33 using IndexType = TIndex;
34 static constexpr int kDims = Dims;
35 static_assert(Dims >= 2 and Dims <= 3, "HierarchicalHashGrid only supports 2D and 3D");
36 static constexpr int kMaxCellsPerPrimitive =
37 Dims == 2 ? 4 : 8;
38
49 HierarchicalHashGrid(IndexType nPrimitives, IndexType nBuckets = 0);
57 void Configure(IndexType nPrimitives, IndexType nBuckets = 0);
68 template <class TDerivedL, class TDerivedU>
69 void Construct(Eigen::DenseBase<TDerivedL> const& L, Eigen::DenseBase<TDerivedU> const& U);
80 template <class FOnPair, class TDerivedX>
81 void BroadPhase(Eigen::DenseBase<TDerivedX> const& X, FOnPair fOnPair) const;
94 template <class FOnPair, class TDerivedL, class TDerivedU>
95 void BroadPhase(
96 Eigen::DenseBase<TDerivedL> const& L,
97 Eigen::DenseBase<TDerivedU> const& U,
98 FOnPair fOnPair) const;
112 template <class FOnPair, class TDerivedLP, class TDerivedUP, class TDerivedX>
113 void BroadPhase(
114 Eigen::DenseBase<TDerivedLP> const& LP,
115 Eigen::DenseBase<TDerivedUP> const& UP,
116 Eigen::DenseBase<TDerivedX> const& XQ,
117 FOnPair fOnPair) const;
133 template <class FOnPair, class TDerivedLP, class TDerivedUP, class TDerivedLQ, class TDerivedUQ>
134 void BroadPhase(
135 Eigen::DenseBase<TDerivedLP> const& LP,
136 Eigen::DenseBase<TDerivedUP> const& UP,
137 Eigen::DenseBase<TDerivedLQ> const& LQ,
138 Eigen::DenseBase<TDerivedUQ> const& UQ,
139 FOnPair fOnPair) const;
144 IndexType NumberOfBuckets() const { return static_cast<IndexType>(mPrefix.size()) - 1; }
149 IndexType NumberOfLevels() const { return static_cast<IndexType>(mSetOfLevels.Size()); }
154 auto Levels() const -> std::span<std::int16_t const>
155 {
156 return std::span<std::int16_t const>(mSetOfLevels.begin(), mSetOfLevels.Size());
157 }
158
166 template <class TDerivedX>
168 Eigen::DenseBase<TDerivedX> const& X,
169 ScalarType const cellSize) const
170 -> Eigen::Vector<IndexType, kDims>;
178 template <class TDerivedX>
179 auto Hash(Eigen::DenseBase<TDerivedX> const& X, std::int16_t l) const;
180
181protected:
185 void ClearHashTable();
186
187private:
188 Eigen::Vector<std::uint8_t, Eigen::Dynamic>
189 mCellCounts;
190 Eigen::Vector<IndexType, Eigen::Dynamic>
191 mBucketIds;
194 Eigen::Vector<IndexType, Eigen::Dynamic>
195 mPrefix;
198 Eigen::Vector<IndexType, Eigen::Dynamic>
199 mPrimitiveIds;
202 common::BruteSet<std::int16_t> mSetOfLevels;
203};
204
205template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
207 IndexType nPrimitives,
208 IndexType nBuckets)
209 : mCellCounts(),
210 mBucketIds(),
211 mPrefix(),
212 mPrimitiveIds(),
213 mSetOfLevels()
214{
215 Configure(nPrimitives, nBuckets);
216}
217
218template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
219inline void
221{
222 nBuckets = std::max(nPrimitives * kMaxCellsPerPrimitive, nBuckets);
223 mCellCounts.resize(nPrimitives);
224 mBucketIds.resize(nBuckets);
225 mPrefix.resize(nBuckets + 1);
226 mPrimitiveIds.resize(nBuckets);
227}
228
229template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
230template <class TDerivedL, class TDerivedU>
232 Eigen::DenseBase<TDerivedL> const& L,
233 Eigen::DenseBase<TDerivedU> const& U)
234{
235 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.HierarchicalHashGrid.Construct");
236 assert(L.rows() == kDims and U.rows() == kDims); // L and U must have |# dims| rows
237 assert(L.cols() == U.cols()); // L and U must have the same number of columns
238 auto const nPrimitives = static_cast<IndexType>(L.cols());
239 auto const nBuckets = NumberOfBuckets();
240 assert(nPrimitives <= nBuckets);
241 assert(mPrefix.size() == nBuckets + 1); // Prefix size must be nBuckets + 1
242 // Build hash table from scratch
244 // Map primitives to cells
245 IndexType k = 0; // Index for mBucketIds and mPrimitiveIds
246 for (IndexType i = 0; i < nPrimitives; ++i)
247 {
248 ScalarType const maxExtent = (U.col(i) - L.col(i)).maxCoeff();
249 ScalarType const l = std::ceil(std::log2(maxExtent));
250 ScalarType const cellSize = std::pow(ScalarType(2), l);
251 Eigen::Vector<IndexType, kDims> const ib =
252 ToIntegerCoordinates(L.col(i).template segment<kDims>(0), cellSize);
253 Eigen::Vector<IndexType, kDims> const ie =
254 ToIntegerCoordinates(U.col(i).template segment<kDims>(0), cellSize);
255 auto const level = static_cast<std::int16_t>(l);
256 Eigen::Vector<IndexType, kDims> ix;
257 // Add primitive i to every cell at level l overlapping with i's bounding box
258 for (ix(0) = ib(0); ix(0) <= ie(0); ++ix(0))
259 {
260 for (ix(1) = ib(1); ix(1) <= ie(1); ++ix(1))
261 {
262 if constexpr (kDims == 2)
263 {
264 IndexType const hash = Hash(ix, level);
265 mBucketIds(k++) = common::Modulo(hash, nBuckets);
266 ++mCellCounts(i);
267 }
268 else
269 {
270 for (ix(2) = ib(2); ix(2) <= ie(2); ++ix(2))
271 {
272 IndexType const hash = Hash(ix, level);
273 mBucketIds(k++) = common::Modulo(hash, nBuckets);
274 ++mCellCounts(i);
275 }
276 }
277 }
278 }
279 mSetOfLevels.Insert(level);
280 }
281 // Compute id counts in the prefix sum's memory
282 mPrefix(mBucketIds.segment(0, k)).array() += 1;
283 // Compute the shifted prefix sum
284 std::inclusive_scan(mPrefix.begin(), mPrefix.end(), mPrefix.begin());
285 // Construct primitive IDs while unshifting prefix sum
286 k = 0;
287 for (IndexType i = 0; i < nPrimitives; ++i)
288 {
289 for (std::uint8_t j = 0; j < mCellCounts(i); ++j, ++k)
290 {
291 auto bucketId = mBucketIds(k);
292 mPrimitiveIds(--mPrefix(bucketId)) = i;
293 }
294 }
295}
296
297template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
298template <class FOnPair, class TDerivedX>
300 Eigen::DenseBase<TDerivedX> const& X,
301 FOnPair fOnPair) const
302{
303 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.HierarchicalHashGrid.BroadPhase");
304 assert(X.rows() == kDims); // X must have |# dims| rows
305 auto const nQueries = static_cast<IndexType>(X.cols());
306 auto const nBuckets = NumberOfBuckets();
307 for (IndexType q = 0; q < nQueries; ++q)
308 {
309 for (std::int16_t l : mSetOfLevels)
310 {
311 auto cellSize = std::pow(ScalarType(2), static_cast<ScalarType>(l));
312 Eigen::Vector<IndexType, kDims> iq = ToIntegerCoordinates(X.col(q), cellSize);
313 auto bucketId = (kDims == 2) ?
314 common::Modulo(Hash(iq, l), nBuckets) :
315 common::Modulo(Hash(iq, l), nBuckets);
316 auto beginBucket = mPrefix(bucketId);
317 auto endBucket = mPrefix(bucketId + 1);
318 for (auto k = beginBucket; k < endBucket; ++k)
319 {
320 auto const i = mPrimitiveIds(k);
321 fOnPair(q, i);
322 }
323 }
324 }
325}
326
327template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
328template <class FOnPair, class TDerivedL, class TDerivedU>
330 Eigen::DenseBase<TDerivedL> const& L,
331 Eigen::DenseBase<TDerivedU> const& U,
332 FOnPair fOnPair) const
333{
334 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.HierarchicalHashGrid.BroadPhase");
335 assert(L.rows() == kDims); // L must have |# dims| rows
336 assert(U.rows() == kDims); // U must have |# dims| rows
337 assert(L.cols() == U.cols()); // L and U must have the same number of columns
338 auto const nQueries = static_cast<IndexType>(L.cols());
339 auto const nBuckets = NumberOfBuckets();
340 for (IndexType q = 0; q < nQueries; ++q)
341 {
342 for (std::int16_t l : mSetOfLevels)
343 {
344 auto cellSize = std::pow(ScalarType(2), static_cast<ScalarType>(l));
345 Eigen::Vector<IndexType, kDims> iqb = ToIntegerCoordinates(L.col(q), cellSize);
346 Eigen::Vector<IndexType, kDims> iqe = ToIntegerCoordinates(U.col(q), cellSize);
347 Eigen::Vector<IndexType, kDims> iq;
348 for (iq(0) = iqb(0); iq(0) <= iqe(0); ++iq(0))
349 {
350 for (iq(1) = iqb(1); iq(1) <= iqe(1); ++iq(1))
351 {
352 if constexpr (kDims == 2)
353 {
354 auto bucketId = common::Modulo(Hash(iq, l), nBuckets);
355 auto beginBucket = mPrefix(bucketId);
356 auto endBucket = mPrefix(bucketId + 1);
357 for (auto k = beginBucket; k < endBucket; ++k)
358 {
359 auto const i = mPrimitiveIds(k);
360 fOnPair(q, i);
361 }
362 }
363 else
364 {
365 for (iq(2) = iqb(2); iq(2) <= iqe(2); ++iq(2))
366 {
367 auto bucketId = common::Modulo(Hash(iq, l), nBuckets);
368 auto beginBucket = mPrefix(bucketId);
369 auto endBucket = mPrefix(bucketId + 1);
370 for (auto k = beginBucket; k < endBucket; ++k)
371 {
372 auto const i = mPrimitiveIds(k);
373 fOnPair(q, i);
374 }
375 }
376 }
377 }
378 }
379 }
380 }
381}
382
383template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
384template <class FOnPair, class TDerivedLP, class TDerivedUP, class TDerivedX>
386 Eigen::DenseBase<TDerivedLP> const& LP,
387 Eigen::DenseBase<TDerivedUP> const& UP,
388 Eigen::DenseBase<TDerivedX> const& XQ,
389 FOnPair fOnPair) const
390{
392 XQ,
393 [&](Index q, Index p) {
394 using pbat::math::linalg::mini::FromEigen;
396 FromEigen(XQ.col(q).template head<kDims>()),
397 FromEigen(LP.col(p).template head<kDims>()),
398 FromEigen(UP.col(p).template head<kDims>())))
399 {
400 fOnPair(q, p);
401 }
402 });
403}
404
405template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
406template <class FOnPair, class TDerivedLP, class TDerivedUP, class TDerivedLQ, class TDerivedUQ>
408 Eigen::DenseBase<TDerivedLP> const& LP,
409 Eigen::DenseBase<TDerivedUP> const& UP,
410 Eigen::DenseBase<TDerivedLQ> const& LQ,
411 Eigen::DenseBase<TDerivedUQ> const& UQ,
412 FOnPair fOnPair) const
413{
414 using pbat::math::linalg::mini::FromEigen;
416 LQ,
417 UQ,
418 [&](Index q, Index p) {
420 FromEigen(LP.col(p).template head<kDims>()),
421 FromEigen(UP.col(p).template head<kDims>()),
422 FromEigen(LQ.col(q).template head<kDims>()),
423 FromEigen(UQ.col(q).template head<kDims>())))
424 {
425 fOnPair(q, p);
426 }
427 });
428}
429
430template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
431template <class TDerivedX>
433 Eigen::DenseBase<TDerivedX> const& X,
434 ScalarType const cellSize) const -> Eigen::Vector<IndexType, kDims>
435{
436 return Eigen::Vector<ScalarType, kDims>(X.derived().array() / static_cast<ScalarType>(cellSize))
437 .array()
438 .floor()
439 .template cast<IndexType>();
440}
441
442template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
443template <class TDerivedX>
445 Eigen::DenseBase<TDerivedX> const& X,
446 std::int16_t l) const
447{
448 if constexpr (kDims == 2)
449 return (X(0) * 73856093) ^ (X(1) * 19349663) ^ (l * 67867979);
450 else
451 return (X(0) * 73856093) ^ (X(1) * 19349663) ^ (X(2) * 834927911) ^ (l * 67867979);
452}
453
454template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
456{
457 mCellCounts.setZero();
458 mPrimitiveIds.setConstant(IndexType(-1));
459 mPrefix.setZero();
460 mSetOfLevels.Clear();
461}
462} // namespace pbat::geometry
463
464#endif // PBAT_GEOMETRY_HIERARCHICALHASHGRID_H
Fixed-size brute-force set implementation usable in both host and device code, suitable for small set...
Modulo function.
This file contains functions to answer overlap queries.
Fixed-size brute-force set implementation.
Definition BruteSet.h:29
auto Levels() const -> std::span< std::int16_t const >
Get the set of levels in the hierarchical grid.
Definition HierarchicalHashGrid.h:154
void Construct(Eigen::DenseBase< TDerivedL > const &L, Eigen::DenseBase< TDerivedU > const &U)
Construct a HashGrid from lower and upper bounds of input axis-aligned bounding boxes (aabbs).
Definition HierarchicalHashGrid.h:231
IndexType NumberOfBuckets() const
Get the number of buckets in the hash table.
Definition HierarchicalHashGrid.h:144
static constexpr int kMaxCellsPerPrimitive
Maximum number of cells per primitive in the grid.
Definition HierarchicalHashGrid.h:36
HierarchicalHashGrid()=default
Default constructor for HierarchicalHashGrid.
void Configure(IndexType nPrimitives, IndexType nBuckets=0)
Configure the hash grid with a specific number of buckets.
Definition HierarchicalHashGrid.h:220
auto ToIntegerCoordinates(Eigen::DenseBase< TDerivedX > const &X, ScalarType const cellSize) const -> Eigen::Vector< IndexType, kDims >
Convert a point X to integer coordinates in the grid.
Definition HierarchicalHashGrid.h:432
TIndex IndexType
Type alias for index values (e.g., int or long).
Definition HierarchicalHashGrid.h:33
IndexType NumberOfLevels() const
Get the number of levels in the hierarchical grid.
Definition HierarchicalHashGrid.h:149
void ClearHashTable()
Clear the hash table, resetting all internal data structures.
Definition HierarchicalHashGrid.h:455
TScalar ScalarType
Type alias for scalar values (e.g., float or double).
Definition HierarchicalHashGrid.h:32
auto Hash(Eigen::DenseBase< TDerivedX > const &X, std::int16_t l) const
Hash a point X at level l in the grid. See eitz2007hierarchical for details on the hashing scheme.
Definition HierarchicalHashGrid.h:444
void BroadPhase(Eigen::DenseBase< TDerivedX > const &X, FOnPair fOnPair) const
Find all primitives whose cell overlaps with points X.
Definition HierarchicalHashGrid.h:299
static constexpr int kDims
Number of spatial dimensions.
Definition HierarchicalHashGrid.h:34
Concepts for common types.
Eigen adaptors for ranges.
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
PBAT_HOST_DEVICE bool PointAxisAlignedBoundingBox(TMatrixP const &P, TMatrixL const &L, TMatrixU const &U)
Tests for overlap between point P and axis-aligned bounding box (L,U)
Definition OverlapQueries.h:536
Geometric queries, quantities and data structures.
Definition AabbKdTreeHierarchy.h:23
std::ptrdiff_t Index
Index type.
Definition Aliases.h:17
Profiling utilities for the Physics-Based Animation Toolkit (PBAT)