Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | /// @file ParticleAtlas.h | ||
5 | /// | ||
6 | /// @brief Space-partitioning acceleration structure for particles, points with | ||
7 | /// radius. Partitions particle indices into voxels to accelerate range | ||
8 | /// and nearest neighbor searches. | ||
9 | /// | ||
10 | /// @note This acceleration structure only stores integer offsets into an external particle | ||
11 | /// data structure that conforms to the ParticleArray interface. | ||
12 | /// | ||
13 | /// @details Constructs and maintains a sequence of @c PointIndexGrids each of progressively | ||
14 | /// lower resolution. Particles are uniquely assigned to a particular resolution | ||
15 | /// level based on their radius. This strategy has proven efficient for accelerating | ||
16 | /// spatial queries on particle data sets with varying radii. | ||
17 | /// | ||
18 | /// @details The data structure automatically detects and adapts to particle data sets with | ||
19 | /// uniform radii. The construction is simplified and spatial queries pre-cache the | ||
20 | /// uniform particle radius to avoid redundant access calls to the | ||
21 | /// ParticleArray::getRadius method. | ||
22 | /// | ||
23 | /// @author Mihai Alden | ||
24 | |||
25 | #ifndef OPENVDB_TOOLS_PARTICLE_ATLAS_HAS_BEEN_INCLUDED | ||
26 | #define OPENVDB_TOOLS_PARTICLE_ATLAS_HAS_BEEN_INCLUDED | ||
27 | |||
28 | #include "PointIndexGrid.h" | ||
29 | |||
30 | #include <openvdb/Grid.h> | ||
31 | #include <openvdb/Types.h> | ||
32 | #include <openvdb/math/Transform.h> | ||
33 | #include <openvdb/tree/Tree.h> | ||
34 | #include <openvdb/tree/LeafNode.h> | ||
35 | |||
36 | #include <tbb/blocked_range.h> | ||
37 | #include <tbb/parallel_for.h> | ||
38 | #include <tbb/parallel_reduce.h> | ||
39 | #include <algorithm> // for std::min(), std::max() | ||
40 | #include <cmath> // for std::sqrt() | ||
41 | #include <deque> | ||
42 | #include <limits> | ||
43 | #include <memory> | ||
44 | #include <utility> // for std::pair | ||
45 | #include <vector> | ||
46 | |||
47 | |||
48 | namespace openvdb { | ||
49 | OPENVDB_USE_VERSION_NAMESPACE | ||
50 | namespace OPENVDB_VERSION_NAME { | ||
51 | namespace tools { | ||
52 | |||
53 | |||
54 | //////////////////////////////////////// | ||
55 | |||
56 | |||
57 | /// @brief Partition particles and performs range and nearest-neighbor searches. | ||
58 | /// | ||
59 | /// @interface ParticleArray | ||
60 | /// Expected interface for the ParticleArray container: | ||
61 | /// @code | ||
62 | /// template<typename VectorType> | ||
63 | /// struct ParticleArray | ||
64 | /// { | ||
65 | /// // The type used to represent world-space positions | ||
66 | /// using PosType = VectorType; | ||
67 | /// using ScalarType = typename PosType::value_type; | ||
68 | /// | ||
69 | /// // Return the number of particles in the array | ||
70 | /// size_t size() const; | ||
71 | /// | ||
72 | /// // Return the world-space position for the nth particle. | ||
73 | /// void getPos(size_t n, PosType& xyz) const; | ||
74 | /// | ||
75 | /// // Return the world-space radius for the nth particle. | ||
76 | /// void getRadius(size_t n, ScalarType& radius) const; | ||
77 | /// }; | ||
78 | /// @endcode | ||
79 | /// | ||
80 | /// @details Constructs a collection of @c PointIndexGrids of different resolutions | ||
81 | /// to accelerate spatial searches for particles with varying radius. | ||
82 | template<typename PointIndexGridType = PointIndexGrid> | ||
83 | struct ParticleAtlas | ||
84 | { | ||
85 | using Ptr = SharedPtr<ParticleAtlas>; | ||
86 | using ConstPtr = SharedPtr<const ParticleAtlas>; | ||
87 | |||
88 | using PointIndexGridPtr = typename PointIndexGridType::Ptr; | ||
89 | using IndexType = typename PointIndexGridType::ValueType; | ||
90 | |||
91 | struct Iterator; | ||
92 | |||
93 | ////////// | ||
94 | |||
95 | ParticleAtlas() : mIndexGridArray(), mMinRadiusArray(), mMaxRadiusArray() {} | ||
96 | |||
97 | /// @brief Partitions particle indices | ||
98 | /// | ||
99 | /// @param particles container conforming to the ParticleArray interface | ||
100 | /// @param minVoxelSize minimum voxel size limit | ||
101 | /// @param maxLevels maximum number of resolution levels | ||
102 | template<typename ParticleArrayType> | ||
103 | void construct(const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels = 50); | ||
104 | |||
105 | /// @brief Create a new @c ParticleAtlas from the given @a particles. | ||
106 | /// | ||
107 | /// @param particles container conforming to the ParticleArray interface | ||
108 | /// @param minVoxelSize minimum voxel size limit | ||
109 | /// @param maxLevels maximum number of resolution levels | ||
110 | template<typename ParticleArrayType> | ||
111 | static Ptr create(const ParticleArrayType& particles, | ||
112 | double minVoxelSize, size_t maxLevels = 50); | ||
113 | |||
114 | /// @brief Returns the number of resolution levels. | ||
115 | size_t levels() const { return mIndexGridArray.size(); } | ||
116 | /// @brief true if the container size is 0, false otherwise. | ||
117 | bool empty() const { return mIndexGridArray.empty(); } | ||
118 | |||
119 | /// @brief Returns minimum particle radius for level @a n. | ||
120 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
2 | double minRadius(size_t n) const { return mMinRadiusArray[n]; } |
121 | /// @brief Returns maximum particle radius for level @a n. | ||
122 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
4 | double maxRadius(size_t n) const { return mMaxRadiusArray[n]; } |
123 | |||
124 | /// @brief Returns the @c PointIndexGrid that represents the given level @a n. | ||
125 | PointIndexGridType& pointIndexGrid(size_t n) { return *mIndexGridArray[n]; } | ||
126 | /// @brief Returns the @c PointIndexGrid that represents the given level @a n. | ||
127 | const PointIndexGridType& pointIndexGrid(size_t n) const { return *mIndexGridArray[n]; } | ||
128 | |||
129 | private: | ||
130 | // Disallow copying | ||
131 | ParticleAtlas(const ParticleAtlas&); | ||
132 | ParticleAtlas& operator=(const ParticleAtlas&); | ||
133 | |||
134 | std::vector<PointIndexGridPtr> mIndexGridArray; | ||
135 | std::vector<double> mMinRadiusArray, mMaxRadiusArray; | ||
136 | }; // struct ParticleAtlas | ||
137 | |||
138 | |||
139 | using ParticleIndexAtlas = ParticleAtlas<PointIndexGrid>; | ||
140 | |||
141 | |||
142 | //////////////////////////////////////// | ||
143 | |||
144 | |||
145 | /// @brief Provides accelerated range and nearest-neighbor searches for | ||
146 | /// particles that are partitioned using the ParticleAtlas. | ||
147 | /// | ||
148 | /// @note Prefer to construct the iterator object once and reuse it | ||
149 | /// for subsequent queries. | ||
150 | template<typename PointIndexGridType> | ||
151 | struct ParticleAtlas<PointIndexGridType>::Iterator | ||
152 | { | ||
153 | using TreeType = typename PointIndexGridType::TreeType; | ||
154 | using ConstAccessor = tree::ValueAccessor<const TreeType>; | ||
155 | using ConstAccessorPtr = std::unique_ptr<ConstAccessor>; | ||
156 | |||
157 | ///// | ||
158 | |||
159 | /// @brief Construct an iterator from the given @a atlas. | ||
160 | explicit Iterator(const ParticleAtlas& atlas); | ||
161 | |||
162 | /// @brief Clear the iterator and update it with the result of the given | ||
163 | /// world-space radial query. | ||
164 | /// @param center world-space center | ||
165 | /// @param radius world-space search radius | ||
166 | /// @param particles container conforming to the ParticleArray interface | ||
167 | template<typename ParticleArrayType> | ||
168 | void worldSpaceSearchAndUpdate(const Vec3d& center, double radius, | ||
169 | const ParticleArrayType& particles); | ||
170 | |||
171 | /// @brief Clear the iterator and update it with the result of the given | ||
172 | /// world-space radial query. | ||
173 | /// @param bbox world-space bounding box | ||
174 | /// @param particles container conforming to the ParticleArray interface | ||
175 | template<typename ParticleArrayType> | ||
176 | void worldSpaceSearchAndUpdate(const BBoxd& bbox, const ParticleArrayType& particles); | ||
177 | |||
178 | /// @brief Returns the total number of resolution levels. | ||
179 | size_t levels() const { return mAtlas->levels(); } | ||
180 | |||
181 | /// @brief Clear the iterator and update it with all particles that reside | ||
182 | /// at the given resolution @a level. | ||
183 | void updateFromLevel(size_t level); | ||
184 | |||
185 | /// Reset the iterator to point to the first item. | ||
186 | void reset(); | ||
187 | |||
188 | /// Return a const reference to the item to which this iterator is pointing. | ||
189 | const IndexType& operator*() const { return *mRange.first; } | ||
190 | |||
191 | /// @{ | ||
192 | /// @brief Return @c true if this iterator is not yet exhausted. | ||
193 |
16/32✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 20000 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 20000 times.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✓ Branch 20 taken 1 times.
✓ Branch 21 taken 1408 times.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 1 times.
✓ Branch 29 taken 20559 times.
✓ Branch 30 taken 1 times.
✗ Branch 31 not taken.
|
61975 | bool test() const { return mRange.first < mRange.second || mIter != mRangeList.end(); } |
194 | operator bool() const { return this->test(); } | ||
195 | /// @} | ||
196 | |||
197 | /// Advance iterator to next item. | ||
198 | void increment(); | ||
199 | |||
200 | /// Advance iterator to next item. | ||
201 | 61967 | void operator++() { this->increment(); } | |
202 | |||
203 | /// @brief Advance iterator to next item. | ||
204 | /// @return @c true if this iterator is not yet exhausted. | ||
205 | bool next(); | ||
206 | |||
207 | /// Return the number of point indices in the iterator range. | ||
208 | size_t size() const; | ||
209 | |||
210 | /// Return @c true if both iterators point to the same element. | ||
211 | bool operator==(const Iterator& p) const { return mRange.first == p.mRange.first; } | ||
212 | bool operator!=(const Iterator& p) const { return !this->operator==(p); } | ||
213 | |||
214 | private: | ||
215 | Iterator(const Iterator& rhs); | ||
216 | Iterator& operator=(const Iterator& rhs); | ||
217 | |||
218 | void clear(); | ||
219 | |||
220 | using Range = std::pair<const IndexType*, const IndexType*>; | ||
221 | using RangeDeque = std::deque<Range>; | ||
222 | using RangeDequeCIter = typename RangeDeque::const_iterator; | ||
223 | using IndexArray = std::unique_ptr<IndexType[]>; | ||
224 | |||
225 | ParticleAtlas const * const mAtlas; | ||
226 | std::unique_ptr<ConstAccessorPtr[]> mAccessorList; | ||
227 | |||
228 | // Primary index collection | ||
229 | Range mRange; | ||
230 | RangeDeque mRangeList; | ||
231 | RangeDequeCIter mIter; | ||
232 | // Secondary index collection | ||
233 | IndexArray mIndexArray; | ||
234 | size_t mIndexArraySize, mAccessorListSize; | ||
235 | }; // struct ParticleAtlas::Iterator | ||
236 | |||
237 | |||
238 | //////////////////////////////////////// | ||
239 | |||
240 | // Internal operators and implementation details | ||
241 | |||
242 | /// @cond OPENVDB_DOCS_INTERNAL | ||
243 | |||
244 | namespace particle_atlas_internal { | ||
245 | |||
246 | |||
247 | template<typename ParticleArrayT> | ||
248 | struct ComputeExtremas | ||
249 | { | ||
250 | using PosType = typename ParticleArrayT::PosType; | ||
251 | using ScalarType = typename PosType::value_type; | ||
252 | |||
253 | 3 | ComputeExtremas(const ParticleArrayT& particles) | |
254 | : particleArray(&particles) | ||
255 | , minRadius(std::numeric_limits<ScalarType>::max()) | ||
256 | 3 | , maxRadius(-std::numeric_limits<ScalarType>::max()) | |
257 | { | ||
258 | } | ||
259 | |||
260 | 11 | ComputeExtremas(ComputeExtremas& rhs, tbb::split) | |
261 | 11 | : particleArray(rhs.particleArray) | |
262 | , minRadius(std::numeric_limits<ScalarType>::max()) | ||
263 | 11 | , maxRadius(-std::numeric_limits<ScalarType>::max()) | |
264 | { | ||
265 | } | ||
266 | |||
267 | 974 | void operator()(const tbb::blocked_range<size_t>& range) { | |
268 | |||
269 | 974 | ScalarType radius, tmpMin = minRadius, tmpMax = maxRadius; | |
270 | |||
271 |
2/2✓ Branch 0 taken 80000 times.
✓ Branch 1 taken 487 times.
|
160974 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
272 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 40000 times.
|
160000 | particleArray->getRadius(n, radius); |
273 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 80000 times.
|
160000 | tmpMin = std::min(radius, tmpMin); |
274 | 160000 | tmpMax = std::max(radius, tmpMax); | |
275 | } | ||
276 | |||
277 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 473 times.
|
974 | minRadius = std::min(minRadius, tmpMin); |
278 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 473 times.
|
974 | maxRadius = std::max(maxRadius, tmpMax); |
279 | 974 | } | |
280 | |||
281 | void join(const ComputeExtremas& rhs) { | ||
282 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
11 | minRadius = std::min(minRadius, rhs.minRadius); |
283 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
|
11 | maxRadius = std::max(maxRadius, rhs.maxRadius); |
284 | 11 | } | |
285 | |||
286 | ParticleArrayT const * const particleArray; | ||
287 | ScalarType minRadius, maxRadius; | ||
288 | }; // struct ComputeExtremas | ||
289 | |||
290 | |||
291 | template<typename ParticleArrayT, typename PointIndex> | ||
292 | 2 | struct SplittableParticleArray | |
293 | { | ||
294 | using Ptr = SharedPtr<SplittableParticleArray>; | ||
295 | using ConstPtr = SharedPtr<const SplittableParticleArray>; | ||
296 | using ParticleArray = ParticleArrayT; | ||
297 | |||
298 | using PosType = typename ParticleArray::PosType; | ||
299 | using ScalarType = typename PosType::value_type; | ||
300 | |||
301 | SplittableParticleArray(const ParticleArrayT& particles) | ||
302 | : mIndexMap(), mParticleArray(&particles), mSize(particles.size()) | ||
303 | { | ||
304 | updateExtremas(); | ||
305 | } | ||
306 | |||
307 | 1 | SplittableParticleArray(const ParticleArrayT& particles, double minR, double maxR) | |
308 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | : mIndexMap(), mParticleArray(&particles), mSize(particles.size()) |
309 | { | ||
310 | 1 | mMinRadius = ScalarType(minR); | |
311 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | mMaxRadius = ScalarType(maxR); |
312 | } | ||
313 | |||
314 | 1 | const ParticleArrayT& particleArray() const { return *mParticleArray; } | |
315 | |||
316 |
4/8✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
|
17 | size_t size() const { return mSize; } |
317 | |||
318 | void getPos(size_t n, PosType& xyz) const | ||
319 |
1/2✓ Branch 0 taken 40000 times.
✗ Branch 1 not taken.
|
40000 | { return mParticleArray->getPos(getGlobalIndex(n), xyz); } |
320 | void getRadius(size_t n, ScalarType& radius) const | ||
321 |
1/2✓ Branch 0 taken 40000 times.
✗ Branch 1 not taken.
|
40000 | { return mParticleArray->getRadius(getGlobalIndex(n), radius); } |
322 | |||
323 | 3 | ScalarType minRadius() const { return mMinRadius; } | |
324 | 4 | ScalarType maxRadius() const { return mMaxRadius; } | |
325 | |||
326 |
4/8✓ Branch 0 taken 40000 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 40000 times.
✓ Branch 4 taken 40000 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 40000 times.
✗ Branch 7 not taken.
|
160000 | size_t getGlobalIndex(size_t n) const { return mIndexMap ? size_t(mIndexMap[n]) : n; } |
327 | |||
328 | /// Move all particle indices that have a radius larger or equal to @a maxRadiusLimit | ||
329 | /// into a separate container. | ||
330 | 1 | Ptr split(ScalarType maxRadiusLimit) { | |
331 | |||
332 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (mMaxRadius < maxRadiusLimit) return Ptr(); |
333 | |||
334 | 1 | std::unique_ptr<bool[]> mask{new bool[mSize]}; | |
335 | |||
336 | ✗ | tbb::parallel_for(tbb::blocked_range<size_t>(0, mSize), | |
337 | MaskParticles(*this, mask, maxRadiusLimit)); | ||
338 | |||
339 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | Ptr output(new SplittableParticleArray(*this, mask)); |
340 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (output->size() == 0) return Ptr(); |
341 | |||
342 | size_t newSize = 0; | ||
343 |
2/2✓ Branch 0 taken 40000 times.
✓ Branch 1 taken 1 times.
|
40001 | for (size_t n = 0, N = mSize; n < N; ++n) { |
344 | 40000 | newSize += size_t(!mask[n]); | |
345 | } | ||
346 | |||
347 |
4/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 20000 times.
✓ Branch 7 taken 1 times.
|
20001 | std::unique_ptr<PointIndex[]> newIndexMap{new PointIndex[newSize]}; |
348 | |||
349 | 1 | setIndexMap(newIndexMap, mask, false); | |
350 | |||
351 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | mSize = newSize; |
352 | mIndexMap.swap(newIndexMap); | ||
353 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | updateExtremas(); |
354 | |||
355 | return output; | ||
356 | } | ||
357 | |||
358 | |||
359 | private: | ||
360 | // Disallow copying | ||
361 | SplittableParticleArray(const SplittableParticleArray&); | ||
362 | SplittableParticleArray& operator=(const SplittableParticleArray&); | ||
363 | |||
364 | // Masked copy constructor | ||
365 | 1 | SplittableParticleArray(const SplittableParticleArray& other, | |
366 | const std::unique_ptr<bool[]>& mask) | ||
367 | 1 | : mIndexMap(), mParticleArray(&other.particleArray()), mSize(0) | |
368 | { | ||
369 |
2/2✓ Branch 0 taken 40000 times.
✓ Branch 1 taken 1 times.
|
40001 | for (size_t n = 0, N = other.size(); n < N; ++n) { |
370 | 40000 | mSize += size_t(mask[n]); | |
371 | } | ||
372 | |||
373 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (mSize != 0) { |
374 |
4/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 20000 times.
✓ Branch 7 taken 1 times.
|
20001 | mIndexMap.reset(new PointIndex[mSize]); |
375 | 1 | other.setIndexMap(mIndexMap, mask, true); | |
376 | } | ||
377 | |||
378 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | updateExtremas(); |
379 | 1 | } | |
380 | |||
381 | struct MaskParticles { | ||
382 | 1 | MaskParticles(const SplittableParticleArray& particles, | |
383 | const std::unique_ptr<bool[]>& mask, ScalarType radius) | ||
384 | : particleArray(&particles) | ||
385 | , particleMask(mask.get()) | ||
386 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | , radiusLimit(radius) |
387 | { | ||
388 | } | ||
389 | |||
390 | 135 | void operator()(const tbb::blocked_range<size_t>& range) const { | |
391 | 135 | const ScalarType maxRadius = radiusLimit; | |
392 | ScalarType radius; | ||
393 |
2/2✓ Branch 0 taken 40000 times.
✓ Branch 1 taken 135 times.
|
40135 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
394 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 40000 times.
|
40000 | particleArray->getRadius(n, radius); |
395 | 40000 | particleMask[n] = !(radius < maxRadius); | |
396 | } | ||
397 | 135 | } | |
398 | |||
399 | SplittableParticleArray const * const particleArray; | ||
400 | bool * const particleMask; | ||
401 | ScalarType const radiusLimit; | ||
402 | }; // struct MaskParticles | ||
403 | |||
404 | 2 | inline void updateExtremas() { | |
405 | ComputeExtremas<SplittableParticleArray> op(*this); | ||
406 | 2 | tbb::parallel_reduce(tbb::blocked_range<size_t>(0, mSize), op); | |
407 | 2 | mMinRadius = op.minRadius; | |
408 | 2 | mMaxRadius = op.maxRadius; | |
409 | 2 | } | |
410 | |||
411 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | void setIndexMap(std::unique_ptr<PointIndex[]>& newIndexMap, |
412 | const std::unique_ptr<bool[]>& mask, bool maskValue) const | ||
413 | { | ||
414 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (mIndexMap.get() != nullptr) { |
415 | const PointIndex* indices = mIndexMap.get(); | ||
416 | ✗ | for (size_t idx = 0, n = 0, N = mSize; n < N; ++n) { | |
417 | ✗ | if (mask[n] == maskValue) newIndexMap[idx++] = indices[n]; | |
418 | } | ||
419 | } else { | ||
420 |
2/2✓ Branch 0 taken 80000 times.
✓ Branch 1 taken 2 times.
|
80002 | for (size_t idx = 0, n = 0, N = mSize; n < N; ++n) { |
421 |
2/2✓ Branch 0 taken 40000 times.
✓ Branch 1 taken 40000 times.
|
80000 | if (mask[n] == maskValue) { |
422 | 40000 | newIndexMap[idx++] = PointIndex(static_cast<typename PointIndex::IntType>(n)); | |
423 | } | ||
424 | } | ||
425 | } | ||
426 | 2 | } | |
427 | |||
428 | |||
429 | ////////// | ||
430 | |||
431 | std::unique_ptr<PointIndex[]> mIndexMap; | ||
432 | ParticleArrayT const * const mParticleArray; | ||
433 | size_t mSize; | ||
434 | ScalarType mMinRadius, mMaxRadius; | ||
435 | }; // struct SplittableParticleArray | ||
436 | |||
437 | |||
438 | template<typename ParticleArrayType, typename PointIndexLeafNodeType> | ||
439 | struct RemapIndices { | ||
440 | |||
441 | 2 | RemapIndices(const ParticleArrayType& particles, std::vector<PointIndexLeafNodeType*>& nodes) | |
442 | : mParticles(&particles) | ||
443 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
4 | , mNodes(nodes.empty() ? nullptr : &nodes.front()) |
444 | { | ||
445 | } | ||
446 | |||
447 | 309 | void operator()(const tbb::blocked_range<size_t>& range) const | |
448 | { | ||
449 | using PointIndexType = typename PointIndexLeafNodeType::ValueType; | ||
450 |
2/2✓ Branch 0 taken 1761 times.
✓ Branch 1 taken 309 times.
|
2070 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
451 | |||
452 |
1/2✓ Branch 0 taken 1761 times.
✗ Branch 1 not taken.
|
1761 | PointIndexLeafNodeType& node = *mNodes[n]; |
453 | const size_t numIndices = node.indices().size(); | ||
454 | |||
455 |
1/2✓ Branch 0 taken 1761 times.
✗ Branch 1 not taken.
|
1761 | if (numIndices > 0) { |
456 | PointIndexType* begin = &node.indices().front(); | ||
457 | const PointIndexType* end = begin + numIndices; | ||
458 | |||
459 |
2/2✓ Branch 0 taken 40000 times.
✓ Branch 1 taken 1761 times.
|
41761 | while (begin < end) { |
460 |
1/2✓ Branch 0 taken 40000 times.
✗ Branch 1 not taken.
|
80000 | *begin = PointIndexType(static_cast<typename PointIndexType::IntType>( |
461 |
1/2✓ Branch 0 taken 40000 times.
✗ Branch 1 not taken.
|
40000 | mParticles->getGlobalIndex(*begin))); |
462 | 40000 | ++begin; | |
463 | } | ||
464 | } | ||
465 | } | ||
466 | 309 | } | |
467 | |||
468 | ParticleArrayType const * const mParticles; | ||
469 | PointIndexLeafNodeType * const * const mNodes; | ||
470 | }; // struct RemapIndices | ||
471 | |||
472 | |||
473 | template<typename ParticleArrayType, typename IndexT> | ||
474 | struct RadialRangeFilter | ||
475 | { | ||
476 | using PosType = typename ParticleArrayType::PosType; | ||
477 | using ScalarType = typename PosType::value_type; | ||
478 | |||
479 | using Range = std::pair<const IndexT*, const IndexT*>; | ||
480 | using RangeDeque = std::deque<Range>; | ||
481 | using IndexDeque = std::deque<IndexT>; | ||
482 | |||
483 | 2 | RadialRangeFilter(RangeDeque& ranges, IndexDeque& indices, const PosType& xyz, | |
484 | ScalarType radius, const ParticleArrayType& particles, bool hasUniformRadius = false) | ||
485 | : mRanges(ranges) | ||
486 | , mIndices(indices) | ||
487 | , mCenter(xyz) | ||
488 | , mRadius(radius) | ||
489 | , mParticles(&particles) | ||
490 | 2 | , mHasUniformRadius(hasUniformRadius) | |
491 | { | ||
492 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (mHasUniformRadius) { |
493 | ScalarType uniformRadius; | ||
494 | mParticles->getRadius(0, uniformRadius); | ||
495 | 2 | mRadius = mRadius + uniformRadius; | |
496 | 2 | mRadius *= mRadius; | |
497 | } | ||
498 | } | ||
499 | |||
500 | template <typename LeafNodeType> | ||
501 | void filterLeafNode(const LeafNodeType& leaf) | ||
502 | { | ||
503 | const size_t numIndices = leaf.indices().size(); | ||
504 | ✗ | if (numIndices > 0) { | |
505 | const IndexT* begin = &leaf.indices().front(); | ||
506 | ✗ | filterVoxel(leaf.origin(), begin, begin + numIndices); | |
507 | } | ||
508 | } | ||
509 | |||
510 | 297 | void filterVoxel(const Coord&, const IndexT* begin, const IndexT* end) | |
511 | { | ||
512 | PosType pos; | ||
513 | |||
514 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 297 times.
|
297 | if (mHasUniformRadius) { |
515 | |||
516 | 297 | const ScalarType searchRadiusSqr = mRadius; | |
517 | |||
518 |
2/2✓ Branch 0 taken 1040 times.
✓ Branch 1 taken 297 times.
|
1337 | while (begin < end) { |
519 |
2/2✓ Branch 0 taken 701 times.
✓ Branch 1 taken 339 times.
|
1040 | mParticles->getPos(size_t(*begin), pos); |
520 | 1040 | const ScalarType distSqr = (mCenter - pos).lengthSqr(); | |
521 |
2/2✓ Branch 0 taken 701 times.
✓ Branch 1 taken 339 times.
|
1040 | if (distSqr < searchRadiusSqr) { |
522 | 701 | mIndices.push_back(*begin); | |
523 | } | ||
524 | 1040 | ++begin; | |
525 | } | ||
526 | } else { | ||
527 | ✗ | while (begin < end) { | |
528 | ✗ | const size_t idx = size_t(*begin); | |
529 | ✗ | mParticles->getPos(idx, pos); | |
530 | |||
531 | ScalarType radius; | ||
532 | mParticles->getRadius(idx, radius); | ||
533 | |||
534 | ✗ | ScalarType searchRadiusSqr = mRadius + radius; | |
535 | ✗ | searchRadiusSqr *= searchRadiusSqr; | |
536 | |||
537 | ✗ | const ScalarType distSqr = (mCenter - pos).lengthSqr(); | |
538 | |||
539 | ✗ | if (distSqr < searchRadiusSqr) { | |
540 | ✗ | mIndices.push_back(*begin); | |
541 | } | ||
542 | |||
543 | ✗ | ++begin; | |
544 | } | ||
545 | } | ||
546 | 297 | } | |
547 | |||
548 | private: | ||
549 | RadialRangeFilter(const RadialRangeFilter&); | ||
550 | RadialRangeFilter& operator=(const RadialRangeFilter&); | ||
551 | |||
552 | RangeDeque& mRanges; | ||
553 | IndexDeque& mIndices; | ||
554 | PosType const mCenter; | ||
555 | ScalarType mRadius; | ||
556 | ParticleArrayType const * const mParticles; | ||
557 | bool const mHasUniformRadius; | ||
558 | }; // struct RadialRangeFilter | ||
559 | |||
560 | |||
561 | template<typename ParticleArrayType, typename IndexT> | ||
562 | struct BBoxFilter | ||
563 | { | ||
564 | using PosType = typename ParticleArrayType::PosType; | ||
565 | using ScalarType = typename PosType::value_type; | ||
566 | |||
567 | using Range = std::pair<const IndexT*, const IndexT*>; | ||
568 | using RangeDeque = std::deque<Range>; | ||
569 | using IndexDeque = std::deque<IndexT>; | ||
570 | |||
571 | 2 | BBoxFilter(RangeDeque& ranges, IndexDeque& indices, | |
572 | const BBoxd& bbox, const ParticleArrayType& particles, bool hasUniformRadius = false) | ||
573 | : mRanges(ranges) | ||
574 | , mIndices(indices) | ||
575 | , mBBox(PosType(bbox.min()), PosType(bbox.max())) | ||
576 | , mCenter(mBBox.getCenter()) | ||
577 | , mParticles(&particles) | ||
578 | , mHasUniformRadius(hasUniformRadius) | ||
579 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | , mUniformRadiusSqr(ScalarType(0.0)) |
580 | { | ||
581 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (mHasUniformRadius) { |
582 | mParticles->getRadius(0, mUniformRadiusSqr); | ||
583 | 2 | mUniformRadiusSqr *= mUniformRadiusSqr; | |
584 | } | ||
585 | 2 | } | |
586 | |||
587 | template <typename LeafNodeType> | ||
588 | void filterLeafNode(const LeafNodeType& leaf) | ||
589 | { | ||
590 | const size_t numIndices = leaf.indices().size(); | ||
591 | ✗ | if (numIndices > 0) { | |
592 | const IndexT* begin = &leaf.indices().front(); | ||
593 | ✗ | filterVoxel(leaf.origin(), begin, begin + numIndices); | |
594 | } | ||
595 | } | ||
596 | |||
597 | 1032 | void filterVoxel(const Coord&, const IndexT* begin, const IndexT* end) | |
598 | { | ||
599 | PosType pos; | ||
600 | |||
601 |
1/2✓ Branch 0 taken 1032 times.
✗ Branch 1 not taken.
|
1032 | if (mHasUniformRadius) { |
602 | 1032 | const ScalarType radiusSqr = mUniformRadiusSqr; | |
603 | |||
604 |
2/2✓ Branch 0 taken 2947 times.
✓ Branch 1 taken 1032 times.
|
3979 | while (begin < end) { |
605 | |||
606 |
2/2✓ Branch 0 taken 1514 times.
✓ Branch 1 taken 1433 times.
|
2947 | mParticles->getPos(size_t(*begin), pos); |
607 |
2/2✓ Branch 0 taken 1514 times.
✓ Branch 1 taken 1433 times.
|
2947 | if (mBBox.isInside(pos)) { |
608 | 1514 | mIndices.push_back(*begin++); | |
609 | 1514 | continue; | |
610 | } | ||
611 | |||
612 | 1433 | const ScalarType distSqr = pointToBBoxDistSqr(pos); | |
613 |
2/2✓ Branch 0 taken 559 times.
✓ Branch 1 taken 874 times.
|
1433 | if (!(distSqr > radiusSqr)) { |
614 | 559 | mIndices.push_back(*begin); | |
615 | } | ||
616 | |||
617 | 1433 | ++begin; | |
618 | } | ||
619 | |||
620 | } else { | ||
621 | ✗ | while (begin < end) { | |
622 | |||
623 | ✗ | const size_t idx = size_t(*begin); | |
624 | ✗ | mParticles->getPos(idx, pos); | |
625 | ✗ | if (mBBox.isInside(pos)) { | |
626 | ✗ | mIndices.push_back(*begin++); | |
627 | continue; | ||
628 | } | ||
629 | |||
630 | ScalarType radius; | ||
631 | mParticles->getRadius(idx, radius); | ||
632 | ✗ | const ScalarType distSqr = pointToBBoxDistSqr(pos); | |
633 | ✗ | if (!(distSqr > (radius * radius))) { | |
634 | ✗ | mIndices.push_back(*begin); | |
635 | } | ||
636 | |||
637 | ✗ | ++begin; | |
638 | } | ||
639 | } | ||
640 | 1032 | } | |
641 | |||
642 | private: | ||
643 | BBoxFilter(const BBoxFilter&); | ||
644 | BBoxFilter& operator=(const BBoxFilter&); | ||
645 | |||
646 | 1433 | ScalarType pointToBBoxDistSqr(const PosType& pos) const | |
647 | { | ||
648 | ScalarType distSqr = ScalarType(0.0); | ||
649 | |||
650 |
2/2✓ Branch 0 taken 4299 times.
✓ Branch 1 taken 1433 times.
|
5732 | for (int i = 0; i < 3; ++i) { |
651 | |||
652 | const ScalarType a = pos[i]; | ||
653 | |||
654 | ScalarType b = mBBox.min()[i]; | ||
655 |
2/2✓ Branch 0 taken 1433 times.
✓ Branch 1 taken 2866 times.
|
4299 | if (a < b) { |
656 | 1433 | ScalarType delta = b - a; | |
657 | 1433 | distSqr += delta * delta; | |
658 | } | ||
659 | |||
660 | b = mBBox.max()[i]; | ||
661 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4298 times.
|
4299 | if (a > b) { |
662 | 1 | ScalarType delta = a - b; | |
663 | 1 | distSqr += delta * delta; | |
664 | } | ||
665 | } | ||
666 | |||
667 | 1433 | return distSqr; | |
668 | } | ||
669 | |||
670 | RangeDeque& mRanges; | ||
671 | IndexDeque& mIndices; | ||
672 | math::BBox<PosType> const mBBox; | ||
673 | PosType const mCenter; | ||
674 | ParticleArrayType const * const mParticles; | ||
675 | bool const mHasUniformRadius; | ||
676 | ScalarType mUniformRadiusSqr; | ||
677 | }; // struct BBoxFilter | ||
678 | |||
679 | |||
680 | } // namespace particle_atlas_internal | ||
681 | |||
682 | /// @endcond | ||
683 | |||
684 | //////////////////////////////////////// | ||
685 | |||
686 | |||
687 | template<typename PointIndexGridType> | ||
688 | template<typename ParticleArrayType> | ||
689 | inline void | ||
690 | 1 | ParticleAtlas<PointIndexGridType>::construct( | |
691 | const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels) | ||
692 | { | ||
693 | using SplittableParticleArray = | ||
694 | typename particle_atlas_internal::SplittableParticleArray<ParticleArrayType, IndexType>; | ||
695 | using SplittableParticleArrayPtr = typename SplittableParticleArray::Ptr; | ||
696 | using ScalarType = typename ParticleArrayType::ScalarType; | ||
697 | |||
698 | ///// | ||
699 | |||
700 | particle_atlas_internal::ComputeExtremas<ParticleArrayType> extremas(particles); | ||
701 | 1 | tbb::parallel_reduce(tbb::blocked_range<size_t>(0, particles.size()), extremas); | |
702 | 1 | const double firstMin = extremas.minRadius; | |
703 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | const double firstMax = extremas.maxRadius; |
704 | 1 | const double firstVoxelSize = std::max(minVoxelSize, firstMin); | |
705 | |||
706 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | if (!(firstMax < (firstVoxelSize * double(2.0))) && maxLevels > 1) { |
707 | |||
708 | 1 | std::vector<SplittableParticleArrayPtr> levels; | |
709 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | levels.push_back(SplittableParticleArrayPtr( |
710 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | new SplittableParticleArray(particles, firstMin, firstMax))); |
711 | |||
712 | std::vector<double> voxelSizeArray; | ||
713 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | voxelSizeArray.push_back(firstVoxelSize); |
714 | |||
715 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | for (size_t n = 0; n < maxLevels; ++n) { |
716 | |||
717 | const double maxParticleRadius = double(levels.back()->maxRadius()); | ||
718 | 2 | const double particleRadiusLimit = voxelSizeArray.back() * double(2.0); | |
719 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (maxParticleRadius < particleRadiusLimit) break; |
720 | |||
721 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | SplittableParticleArrayPtr newLevel = |
722 | levels.back()->split(ScalarType(particleRadiusLimit)); | ||
723 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (!newLevel) break; |
724 | |||
725 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | levels.push_back(newLevel); |
726 |
2/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
1 | voxelSizeArray.push_back(double(newLevel->minRadius())); |
727 | } | ||
728 | |||
729 | size_t numPoints = 0; | ||
730 | |||
731 | using PointIndexTreeType = typename PointIndexGridType::TreeType; | ||
732 | using PointIndexLeafNodeType = typename PointIndexTreeType::LeafNodeType; | ||
733 | |||
734 | std::vector<PointIndexLeafNodeType*> nodes; | ||
735 | |||
736 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for (size_t n = 0, N = levels.size(); n < N; ++n) { |
737 | |||
738 | const SplittableParticleArray& particleArray = *levels[n]; | ||
739 | |||
740 | numPoints += particleArray.size(); | ||
741 | |||
742 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | mMinRadiusArray.push_back(double(particleArray.minRadius())); |
743 |
2/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
2 | mMaxRadiusArray.push_back(double(particleArray.maxRadius())); |
744 | |||
745 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | PointIndexGridPtr grid = |
746 | createPointIndexGrid<PointIndexGridType>(particleArray, voxelSizeArray[n]); | ||
747 | |||
748 | nodes.clear(); | ||
749 | grid->tree().getNodes(nodes); | ||
750 | |||
751 |
1/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
2 | tbb::parallel_for(tbb::blocked_range<size_t>(0, nodes.size()), |
752 | particle_atlas_internal::RemapIndices<SplittableParticleArray, | ||
753 | PointIndexLeafNodeType>(particleArray, nodes)); | ||
754 | |||
755 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | mIndexGridArray.push_back(grid); |
756 | 1 | } | |
757 | |||
758 | } else { | ||
759 | ✗ | mMinRadiusArray.push_back(firstMin); | |
760 | ✗ | mMaxRadiusArray.push_back(firstMax); | |
761 | ✗ | mIndexGridArray.push_back( | |
762 | createPointIndexGrid<PointIndexGridType>(particles, firstVoxelSize)); | ||
763 | } | ||
764 | 1 | } | |
765 | |||
766 | |||
767 | template<typename PointIndexGridType> | ||
768 | template<typename ParticleArrayType> | ||
769 | inline typename ParticleAtlas<PointIndexGridType>::Ptr | ||
770 | ParticleAtlas<PointIndexGridType>::create( | ||
771 | const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels) | ||
772 | { | ||
773 | Ptr ret(new ParticleAtlas()); | ||
774 | ret->construct(particles, minVoxelSize, maxLevels); | ||
775 | return ret; | ||
776 | } | ||
777 | |||
778 | |||
779 | //////////////////////////////////////// | ||
780 | |||
781 | // ParticleAtlas::Iterator implementation | ||
782 | |||
783 | template<typename PointIndexGridType> | ||
784 | inline | ||
785 | 1 | ParticleAtlas<PointIndexGridType>::Iterator::Iterator(const ParticleAtlas& atlas) | |
786 | : mAtlas(&atlas) | ||
787 | , mAccessorList() | ||
788 | , mRange(static_cast<IndexType*>(nullptr), static_cast<IndexType*>(nullptr)) | ||
789 | , mRangeList() | ||
790 | , mIter(mRangeList.begin()) | ||
791 | , mIndexArray() | ||
792 | , mIndexArraySize(0) | ||
793 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
2 | , mAccessorListSize(atlas.levels()) |
794 | { | ||
795 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (mAccessorListSize > 0) { |
796 |
4/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 times.
|
3 | mAccessorList.reset(new ConstAccessorPtr[mAccessorListSize]); |
797 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for (size_t n = 0, N = mAccessorListSize; n < N; ++n) { |
798 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | mAccessorList[n].reset(new ConstAccessor(atlas.pointIndexGrid(n).tree())); |
799 | } | ||
800 | } | ||
801 | 1 | } | |
802 | |||
803 | |||
804 | template<typename PointIndexGridType> | ||
805 | inline void | ||
806 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | ParticleAtlas<PointIndexGridType>::Iterator::reset() |
807 | { | ||
808 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | mIter = mRangeList.begin(); |
809 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (!mRangeList.empty()) { |
810 | mRange = mRangeList.front(); | ||
811 | ✗ | } else if (mIndexArray) { | |
812 | ✗ | mRange.first = mIndexArray.get(); | |
813 | ✗ | mRange.second = mRange.first + mIndexArraySize; | |
814 | } else { | ||
815 | ✗ | mRange.first = static_cast<IndexType*>(nullptr); | |
816 | ✗ | mRange.second = static_cast<IndexType*>(nullptr); | |
817 | } | ||
818 | 4 | } | |
819 | |||
820 | |||
821 | template<typename PointIndexGridType> | ||
822 | inline void | ||
823 | 61967 | ParticleAtlas<PointIndexGridType>::Iterator::increment() | |
824 | { | ||
825 | 61967 | ++mRange.first; | |
826 |
4/4✓ Branch 0 taken 3279 times.
✓ Branch 1 taken 58688 times.
✓ Branch 2 taken 3277 times.
✓ Branch 3 taken 2 times.
|
61967 | if (mRange.first >= mRange.second && mIter != mRangeList.end()) { |
827 | ++mIter; | ||
828 |
2/2✓ Branch 0 taken 3273 times.
✓ Branch 1 taken 4 times.
|
3277 | if (mIter != mRangeList.end()) { |
829 | mRange = *mIter; | ||
830 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | } else if (mIndexArray) { |
831 | 2 | mRange.first = mIndexArray.get(); | |
832 | 2 | mRange.second = mRange.first + mIndexArraySize; | |
833 | } | ||
834 | } | ||
835 | 61967 | } | |
836 | |||
837 | |||
838 | template<typename PointIndexGridType> | ||
839 | inline bool | ||
840 | ParticleAtlas<PointIndexGridType>::Iterator::next() | ||
841 | { | ||
842 | if (!this->test()) return false; | ||
843 | this->increment(); | ||
844 | return this->test(); | ||
845 | } | ||
846 | |||
847 | |||
848 | template<typename PointIndexGridType> | ||
849 | inline size_t | ||
850 | 4 | ParticleAtlas<PointIndexGridType>::Iterator::size() const | |
851 | { | ||
852 | size_t count = 0; | ||
853 | typename RangeDeque::const_iterator it = | ||
854 | mRangeList.begin(), end = mRangeList.end(); | ||
855 | |||
856 |
2/2✓ Branch 0 taken 3277 times.
✓ Branch 1 taken 4 times.
|
3281 | for ( ; it != end; ++it) { |
857 |
2/2✓ Branch 0 taken 3177 times.
✓ Branch 1 taken 100 times.
|
3277 | count += it->second - it->first; |
858 | } | ||
859 | |||
860 | 4 | return count + mIndexArraySize; | |
861 | } | ||
862 | |||
863 | |||
864 | template<typename PointIndexGridType> | ||
865 | inline void | ||
866 | 4 | ParticleAtlas<PointIndexGridType>::Iterator::clear() | |
867 | { | ||
868 | 4 | mRange.first = static_cast<IndexType*>(nullptr); | |
869 | 4 | mRange.second = static_cast<IndexType*>(nullptr); | |
870 | 4 | mRangeList.clear(); | |
871 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | mIter = mRangeList.end(); |
872 | mIndexArray.reset(); | ||
873 | 4 | mIndexArraySize = 0; | |
874 | 4 | } | |
875 | |||
876 | |||
877 | template<typename PointIndexGridType> | ||
878 | inline void | ||
879 | 2 | ParticleAtlas<PointIndexGridType>::Iterator::updateFromLevel(size_t level) | |
880 | { | ||
881 | using TreeT = typename PointIndexGridType::TreeType; | ||
882 | using LeafNodeType = typename TreeType::LeafNodeType; | ||
883 | |||
884 | 2 | this->clear(); | |
885 | |||
886 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (mAccessorListSize > 0) { |
887 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | const size_t levelIdx = std::min(mAccessorListSize - 1, level); |
888 | |||
889 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | const TreeT& tree = mAtlas->pointIndexGrid(levelIdx).tree(); |
890 | |||
891 | std::vector<const LeafNodeType*> nodes; | ||
892 | tree.getNodes(nodes); | ||
893 | |||
894 |
2/2✓ Branch 0 taken 1761 times.
✓ Branch 1 taken 2 times.
|
1763 | for (size_t n = 0, N = nodes.size(); n < N; ++n) { |
895 | |||
896 |
1/2✓ Branch 0 taken 1761 times.
✗ Branch 1 not taken.
|
1761 | const LeafNodeType& node = *nodes[n]; |
897 | const size_t numIndices = node.indices().size(); | ||
898 | |||
899 |
1/2✓ Branch 0 taken 1761 times.
✗ Branch 1 not taken.
|
1761 | if (numIndices > 0) { |
900 | const IndexType* begin = &node.indices().front(); | ||
901 |
1/4✓ Branch 1 taken 1761 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
1761 | mRangeList.push_back(Range(begin, (begin + numIndices))); |
902 | } | ||
903 | } | ||
904 | } | ||
905 | |||
906 | 2 | this->reset(); | |
907 | 2 | } | |
908 | |||
909 | |||
910 | template<typename PointIndexGridType> | ||
911 | template<typename ParticleArrayType> | ||
912 | inline void | ||
913 | 1 | ParticleAtlas<PointIndexGridType>::Iterator::worldSpaceSearchAndUpdate( | |
914 | const Vec3d& center, double radius, const ParticleArrayType& particles) | ||
915 | { | ||
916 | using PosType = typename ParticleArrayType::PosType; | ||
917 | using ScalarType = typename ParticleArrayType::ScalarType; | ||
918 | |||
919 | ///// | ||
920 | |||
921 | 1 | this->clear(); | |
922 | |||
923 | std::deque<IndexType> filteredIndices; | ||
924 | std::vector<CoordBBox> searchRegions; | ||
925 | |||
926 | 1 | const double iRadius = radius * double(1.0 / std::sqrt(3.0)); | |
927 | |||
928 | 1 | const Vec3d ibMin(center[0] - iRadius, center[1] - iRadius, center[2] - iRadius); | |
929 | 1 | const Vec3d ibMax(center[0] + iRadius, center[1] + iRadius, center[2] + iRadius); | |
930 | |||
931 | 1 | const Vec3d bMin(center[0] - radius, center[1] - radius, center[2] - radius); | |
932 | 1 | const Vec3d bMax(center[0] + radius, center[1] + radius, center[2] + radius); | |
933 | |||
934 | 1 | const PosType pos = PosType(center); | |
935 | const ScalarType dist = ScalarType(radius); | ||
936 | |||
937 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for (size_t n = 0, N = mAccessorListSize; n < N; ++n) { |
938 | |||
939 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | const double maxRadius = mAtlas->maxRadius(n); |
940 | const bool uniformRadius = math::isApproxEqual(mAtlas->minRadius(n), maxRadius); | ||
941 | |||
942 | const openvdb::math::Transform& xform = mAtlas->pointIndexGrid(n).transform(); | ||
943 | |||
944 | ConstAccessor& acc = *mAccessorList[n]; | ||
945 | |||
946 | openvdb::CoordBBox inscribedRegion( | ||
947 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | xform.worldToIndexCellCentered(ibMin), |
948 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
4 | xform.worldToIndexCellCentered(ibMax)); |
949 | |||
950 | inscribedRegion.expand(-1); // erode by one voxel | ||
951 | |||
952 | // collect indices that don't need to be tested | ||
953 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | point_index_grid_internal::pointIndexSearch(mRangeList, acc, inscribedRegion); |
954 | |||
955 | searchRegions.clear(); | ||
956 | |||
957 | const openvdb::CoordBBox region( | ||
958 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | xform.worldToIndexCellCentered(bMin - maxRadius), |
959 |
2/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
4 | xform.worldToIndexCellCentered(bMax + maxRadius)); |
960 | |||
961 | inscribedRegion.expand(1); | ||
962 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | point_index_grid_internal::constructExclusiveRegions( |
963 | searchRegions, region, inscribedRegion); | ||
964 | |||
965 | using FilterType = particle_atlas_internal::RadialRangeFilter<ParticleArrayType, IndexType>; | ||
966 | FilterType filter(mRangeList, filteredIndices, pos, dist, particles, uniformRadius); | ||
967 | |||
968 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
|
14 | for (size_t i = 0, I = searchRegions.size(); i < I; ++i) { |
969 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[i]); |
970 | } | ||
971 | } | ||
972 | |||
973 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); |
974 | |||
975 | 1 | this->reset(); | |
976 | 1 | } | |
977 | |||
978 | |||
979 | template<typename PointIndexGridType> | ||
980 | template<typename ParticleArrayType> | ||
981 | inline void | ||
982 | 1 | ParticleAtlas<PointIndexGridType>::Iterator::worldSpaceSearchAndUpdate( | |
983 | const BBoxd& bbox, const ParticleArrayType& particles) | ||
984 | { | ||
985 | 1 | this->clear(); | |
986 | |||
987 | std::deque<IndexType> filteredIndices; | ||
988 | std::vector<CoordBBox> searchRegions; | ||
989 | |||
990 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for (size_t n = 0, N = mAccessorListSize; n < N; ++n) { |
991 | |||
992 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | const double maxRadius = mAtlas->maxRadius(n); |
993 | const bool uniformRadius = math::isApproxEqual(mAtlas->minRadius(n), maxRadius); | ||
994 | const openvdb::math::Transform& xform = mAtlas->pointIndexGrid(n).transform(); | ||
995 | |||
996 | ConstAccessor& acc = *mAccessorList[n]; | ||
997 | |||
998 | openvdb::CoordBBox inscribedRegion( | ||
999 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | xform.worldToIndexCellCentered(bbox.min()), |
1000 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
4 | xform.worldToIndexCellCentered(bbox.max())); |
1001 | |||
1002 | inscribedRegion.expand(-1); // erode by one voxel | ||
1003 | |||
1004 | // collect indices that don't need to be tested | ||
1005 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | point_index_grid_internal::pointIndexSearch(mRangeList, acc, inscribedRegion); |
1006 | |||
1007 | searchRegions.clear(); | ||
1008 | |||
1009 | const openvdb::CoordBBox region( | ||
1010 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | xform.worldToIndexCellCentered(bbox.min() - maxRadius), |
1011 |
2/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
4 | xform.worldToIndexCellCentered(bbox.max() + maxRadius)); |
1012 | |||
1013 | inscribedRegion.expand(1); | ||
1014 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | point_index_grid_internal::constructExclusiveRegions( |
1015 | searchRegions, region, inscribedRegion); | ||
1016 | |||
1017 | using FilterType = particle_atlas_internal::BBoxFilter<ParticleArrayType, IndexType>; | ||
1018 | 2 | FilterType filter(mRangeList, filteredIndices, bbox, particles, uniformRadius); | |
1019 | |||
1020 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
|
14 | for (size_t i = 0, I = searchRegions.size(); i < I; ++i) { |
1021 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[i]); |
1022 | } | ||
1023 | } | ||
1024 | |||
1025 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); |
1026 | |||
1027 | 1 | this->reset(); | |
1028 | 1 | } | |
1029 | |||
1030 | |||
1031 | } // namespace tools | ||
1032 | } // namespace OPENVDB_VERSION_NAME | ||
1033 | } // namespace openvdb | ||
1034 | |||
1035 | #endif // OPENVDB_TOOLS_PARTICLE_ATLAS_HAS_BEEN_INCLUDED | ||
1036 |