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
HashGrid.h
Go to the documentation of this file.
1
10
11#ifndef PBAT_GEOMETRY_HASHGRID_H
12#define PBAT_GEOMETRY_HASHGRID_H
13
15#include "pbat/common/Modulo.h"
17
18#include <Eigen/Core>
19#include <algorithm>
20#include <cassert>
21
22namespace pbat::geometry {
23
30template <common::CIndex THashed>
31[[maybe_unused]] inline auto HashByXorOfPrimeMultiples()
32{
33 return []<class TDerivedX>(Eigen::DenseBase<TDerivedX> const& X) {
34 static_assert(
35 TDerivedX::SizeAtCompileTime == 2 or TDerivedX::SizeAtCompileTime == 3,
36 "Only eigen vectors of compile-time size 2 or 3 are supported.");
37 if constexpr (TDerivedX::SizeAtCompileTime == 2)
38 {
39 return static_cast<THashed>(X(0) * 73856093) ^ static_cast<THashed>(X(1) * 19349663);
40 }
41 if constexpr (TDerivedX::SizeAtCompileTime == 3)
42 {
43 return static_cast<THashed>(X(0) * 73856093) ^ static_cast<THashed>(X(1) * 19349663) ^
44 static_cast<THashed>(X(2) * 83492791);
45 }
46 };
47}
48
58template <int Dims, common::CFloatingPoint TScalar = Scalar, common::CIndex TIndex = Index>
60{
61 public:
62 using ScalarType = TScalar;
63 using IndexType = TIndex;
64 static constexpr int kDims = Dims;
65 static_assert(Dims >= 2 and Dims <= 3, "HashGrid only supports 2D and 3D");
66
70 HashGrid() = default;
76 HashGrid(ScalarType cellSize, IndexType nBuckets);
82 void Configure(ScalarType cellSize, IndexType nBuckets);
87 void Reserve(IndexType nPrimitives);
100 template <class FHash, class TDerivedL, class TDerivedU>
101 void Construct(
102 Eigen::DenseBase<TDerivedL> const& L,
103 Eigen::DenseBase<TDerivedU> const& U,
104 FHash fHash);
113 template <class FHash, class TDerivedX>
114 void Construct(Eigen::DenseBase<TDerivedX> const& X, FHash fHash);
127 template <class FOnPair, class FHash, class TDerivedX>
128 void BroadPhase(Eigen::DenseBase<TDerivedX> const& X, FOnPair fOnPair, FHash fHash) const;
133 IndexType NumberOfBuckets() const { return static_cast<IndexType>(mPrefix.size()) - 1; }
139 template <class TDerivedX>
140 auto Cell(Eigen::DenseBase<TDerivedX> const& X) const -> Eigen::Matrix<ScalarType, kDims, 2>;
147 template <class TDerivedX>
148 auto ToIntegerCoordinates(Eigen::DenseBase<TDerivedX> const& X) const
149 -> Eigen::Vector<IndexType, kDims>;
150
151 private:
152 ScalarType mCellSize;
153 Eigen::Vector<IndexType, Eigen::Dynamic>
154 mBucketIds;
155 Eigen::Vector<IndexType, Eigen::Dynamic>
156 mPrefix;
157 Eigen::Vector<IndexType, Eigen::Dynamic>
158 mPrimitiveIds;
160};
161
162template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
164 : HashGrid<Dims, TScalar, TIndex>()
165{
166 Configure(cellSize, nBuckets);
167}
168
169template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
171{
172 mCellSize = cellSize;
173 mPrefix.resize(nBuckets + 1);
174}
175
176template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
178{
179 mBucketIds.resize(nPrimitives);
180 mPrimitiveIds.resize(nPrimitives);
181}
182
183template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
184template <class FHash, class TDerivedL, class TDerivedU>
186 Eigen::DenseBase<TDerivedL> const& L,
187 Eigen::DenseBase<TDerivedU> const& U,
188 FHash fHash)
189{
190 assert(L.rows() == U.rows());
191 assert(L.cols() == U.cols());
192 Construct(ScalarType(0.5) * (L.derived() + U.derived()), fHash);
193}
194
195template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
196template <class FHash, class TDerivedX>
197inline void
198HashGrid<Dims, TScalar, TIndex>::Construct(Eigen::DenseBase<TDerivedX> const& X, FHash fHash)
199{
200 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.HashGrid.Construct");
201 auto const nPrimitives = static_cast<IndexType>(X.cols());
202 auto const nBuckets = NumberOfBuckets();
203 assert(nBuckets >= nPrimitives);
204 assert(nPrimitives == static_cast<IndexType>(X.cols()));
205 assert(X.rows() == kDims);
206 Reserve(nPrimitives);
207 // Map primitives to hash IDs
208 for (IndexType i = 0; i < nPrimitives; ++i)
209 {
210 auto ixyz = ToIntegerCoordinates(X.col(i));
211 mBucketIds[i] = common::Modulo(fHash(ixyz), nBuckets);
212 }
213 // Compute id counts in the prefix sum's memory
214 mPrefix.setZero();
215 mPrefix(mBucketIds).array() += 1;
216 // Compute the shifted prefix sum
217 std::inclusive_scan(mPrefix.begin(), mPrefix.end(), mPrefix.begin());
218 // Construct primitive IDs while unshifting prefix sum
219 for (IndexType i = 0; i < nPrimitives; ++i)
220 {
221 auto bucketId = mBucketIds[i];
222 mPrimitiveIds[--mPrefix[bucketId]] = i;
223 }
224}
225
226template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
227template <class FOnPair, class FHash, class TDerivedX>
229 Eigen::DenseBase<TDerivedX> const& X,
230 FOnPair fOnPair,
231 FHash fHash) const
232{
233 PBAT_PROFILE_NAMED_SCOPE("pbat.geometry.HashGrid.BroadPhase");
234 assert(X.rows() == kDims);
235 auto const nQueries = static_cast<IndexType>(X.cols());
236 auto const nBuckets = NumberOfBuckets();
237 auto const fVisitCell = [&](IndexType q, Eigen::Vector<IndexType, kDims> const& ixyz) {
238 auto bucketId = common::Modulo(fHash(ixyz), nBuckets);
239 auto begin = mPrefix[bucketId];
240 auto end = mPrefix[bucketId + 1];
241 for (auto j = begin; j < end; ++j)
242 fOnPair(q, mPrimitiveIds[j]);
243 };
244 for (IndexType q = 0; q < nQueries; ++q)
245 {
246 Eigen::Vector<IndexType, kDims> ixyz = ToIntegerCoordinates(X.col(q));
247 if constexpr (kDims == 2)
248 {
249 for (auto cx = -1; cx <= 1; ++cx)
250 {
251 ixyz.x() += cx;
252 for (auto cy = -1; cy <= 1; ++cy)
253 {
254 ixyz.y() += cy;
255 fVisitCell(q, ixyz);
256 ixyz.y() -= cy;
257 }
258 ixyz.x() -= cx;
259 }
260 }
261 if constexpr (kDims == 3)
262 {
263 for (auto cx = -1; cx <= 1; ++cx)
264 {
265 ixyz.x() += cx;
266 for (auto cy = -1; cy <= 1; ++cy)
267 {
268 ixyz.y() += cy;
269 for (auto cz = -1; cz <= 1; ++cz)
270 {
271 ixyz.z() += cz;
272 fVisitCell(q, ixyz);
273 ixyz.z() -= cz;
274 }
275 ixyz.y() -= cy;
276 }
277 ixyz.x() -= cx;
278 }
279 }
280 }
281}
282
283template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
284template <class TDerivedX>
285inline auto HashGrid<Dims, TScalar, TIndex>::Cell(Eigen::DenseBase<TDerivedX> const& X) const
286 -> Eigen::Matrix<ScalarType, kDims, 2>
287{
288 Eigen::Matrix<ScalarType, kDims, 2> cell{};
289 auto const ixyz = ToIntegerCoordinates(X);
290 cell.col(0) = ixyz.template cast<ScalarType>() * mCellSize;
291 cell.col(1) = cell.col(0).array() + static_cast<ScalarType>(mCellSize);
292 return cell;
293}
294
295template <int Dims, common::CFloatingPoint TScalar, common::CIndex TIndex>
296template <class TDerivedX>
297inline auto
298HashGrid<Dims, TScalar, TIndex>::ToIntegerCoordinates(Eigen::DenseBase<TDerivedX> const& X) const
299 -> Eigen::Vector<IndexType, kDims>
300{
301 return Eigen::Vector<ScalarType, kDims>(
302 X.derived().array() / static_cast<ScalarType>(mCellSize))
303 .array()
304 .floor()
305 .template cast<IndexType>();
306}
307
308} // namespace pbat::geometry
309
310#endif // PBAT_GEOMETRY_HASHGRID_H
Modulo function.
void Construct(Eigen::DenseBase< TDerivedL > const &L, Eigen::DenseBase< TDerivedU > const &U, FHash fHash)
Construct a HashGrid from lower and upper bounds of input axis-aligned bounding boxes (aabbs).
Definition HashGrid.h:185
auto ToIntegerCoordinates(Eigen::DenseBase< TDerivedX > const &X) const -> Eigen::Vector< IndexType, kDims >
Convert a point X to integer coordinates in the grid.
Definition HashGrid.h:298
void Configure(ScalarType cellSize, IndexType nBuckets)
Configure the hash grid with a specific cell size and number of buckets.
Definition HashGrid.h:170
TIndex IndexType
Type alias for index values (e.g., int or long).
Definition HashGrid.h:63
void BroadPhase(Eigen::DenseBase< TDerivedX > const &X, FOnPair fOnPair, FHash fHash) const
Find all primitives whose cell overlaps with points X.
Definition HashGrid.h:228
static constexpr int kDims
Number of spatial dimensions.
Definition HashGrid.h:64
IndexType NumberOfBuckets() const
Get the number of buckets in the hash table.
Definition HashGrid.h:133
HashGrid()=default
Default constructor for HashGrid.
void Reserve(IndexType nPrimitives)
Reserve space for a specific number of primitives in the hash grid.
Definition HashGrid.h:177
TScalar ScalarType
Type alias for scalar values (e.g., float or double).
Definition HashGrid.h:62
auto Cell(Eigen::DenseBase< TDerivedX > const &X) const -> Eigen::Matrix< ScalarType, kDims, 2 >
Get the hash grid's cell (i.e. aabb) corresponding to a point X.
Definition HashGrid.h:285
Concepts for common types.
Geometric queries, quantities and data structures.
Definition AabbKdTreeHierarchy.h:23
auto HashByXorOfPrimeMultiples()
Returns a hash function used in teschner2003 for 2 or 3-vectors suitable as input to HashGrid's API.
Definition HashGrid.h:31
Profiling utilities for the Physics-Based Animation Toolkit (PBAT)