GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/points/PointScatter.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 122 128 95.3%
Functions: 34 34 100.0%
Branches: 157 262 59.9%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @author Nick Avramoussis
5 ///
6 /// @file points/PointScatter.h
7 ///
8 /// @brief Various point scattering methods for generating VDB Points.
9 ///
10 /// All random number calls are made to the same generator to produce
11 /// temporarily consistent results in relation to the provided seed. This
12 /// comes with some multi-threaded performance trade-offs.
13
14 #ifndef OPENVDB_POINTS_POINT_SCATTER_HAS_BEEN_INCLUDED
15 #define OPENVDB_POINTS_POINT_SCATTER_HAS_BEEN_INCLUDED
16
17 #include <type_traits>
18 #include <algorithm>
19 #include <thread>
20 #include <random>
21
22 #include <openvdb/openvdb.h>
23 #include <openvdb/Types.h>
24 #include <openvdb/tree/LeafManager.h>
25 #include <openvdb/tools/Prune.h>
26 #include <openvdb/util/NullInterrupter.h>
27
28 #include "AttributeArray.h"
29 #include "PointCount.h"
30 #include "PointDataGrid.h"
31
32 #include <tbb/parallel_sort.h>
33 #include <tbb/parallel_for.h>
34
35 namespace openvdb {
36 OPENVDB_USE_VERSION_NAMESPACE
37 namespace OPENVDB_VERSION_NAME {
38 namespace points {
39
40 /// @brief The free functions depend on the following class:
41 ///
42 /// The @c InterrupterT template argument below refers to any class
43 /// with the following interface:
44 /// @code
45 /// class Interrupter {
46 /// ...
47 /// public:
48 /// void start(const char* name = nullptr) // called when computations begin
49 /// void end() // called when computations end
50 /// bool wasInterrupted(int percent=-1) // return true to break computation
51 ///};
52 /// @endcode
53 ///
54 /// @note If no template argument is provided for this InterrupterT
55 /// the util::NullInterrupter is used which implies that all
56 /// interrupter calls are no-ops (i.e. incurs no computational overhead).
57
58
59 /// @brief Uniformly scatter a total amount of points in active regions
60 ///
61 /// @param grid A source grid. The resulting PointDataGrid will copy this grids
62 /// transform and scatter in its active voxelized topology.
63 /// @param count The total number of points to scatter
64 /// @param seed A seed for the RandGenT
65 /// @param spread The spread of points as a scale from each voxels center. A value of
66 /// 1.0f indicates points can be placed anywhere within the voxel, where
67 /// as a value of 0.0f will force all points to be created exactly at the
68 /// centers of each voxel.
69 /// @param interrupter An optional interrupter
70 /// @note returns the scattered PointDataGrid
71 template<
72 typename GridT,
73 typename RandGenT = std::mt19937,
74 typename PositionArrayT = TypedAttributeArray<Vec3f, NullCodec>,
75 typename PointDataGridT = Grid<
76 typename points::TreeConverter<typename GridT::TreeType>::Type>,
77 typename InterrupterT = util::NullInterrupter>
78 inline typename PointDataGridT::Ptr
79 uniformPointScatter(const GridT& grid,
80 const Index64 count,
81 const unsigned int seed = 0,
82 const float spread = 1.0f,
83 InterrupterT* interrupter = nullptr);
84
85 /// @brief Uniformly scatter a fixed number of points per active voxel. If the pointsPerVoxel
86 /// value provided is a fractional value, each voxel calculates a delta value of
87 /// how likely it is to contain an extra point.
88 ///
89 /// @param grid A source grid. The resulting PointDataGrid will copy this grids
90 /// transform and scatter in its active voxelized topology.
91 /// @param pointsPerVoxel The number of points to scatter per voxel
92 /// @param seed A seed for the RandGenT
93 /// @param spread The spread of points as a scale from each voxels center. A value of
94 /// 1.0f indicates points can be placed anywhere within the voxel, where
95 /// as a value of 0.0f will force all points to be created exactly at the
96 /// centers of each voxel.
97 /// @param interrupter An optional interrupter
98 /// @note returns the scattered PointDataGrid
99
100 template<
101 typename GridT,
102 typename RandGenT = std::mt19937,
103 typename PositionArrayT = TypedAttributeArray<Vec3f, NullCodec>,
104 typename PointDataGridT = Grid<
105 typename points::TreeConverter<typename GridT::TreeType>::Type>,
106 typename InterrupterT = util::NullInterrupter>
107 inline typename PointDataGridT::Ptr
108 denseUniformPointScatter(const GridT& grid,
109 const float pointsPerVoxel,
110 const unsigned int seed = 0,
111 const float spread = 1.0f,
112 InterrupterT* interrupter = nullptr);
113
114 /// @brief Non uniformly scatter points per active voxel. The pointsPerVoxel value is used
115 /// to weight each grids cell value to compute a fixed number of points for every
116 /// active voxel. If the computed result is a fractional value, each voxel calculates
117 /// a delta value of how likely it is to contain an extra point.
118 ///
119 /// @param grid A source grid. The resulting PointDataGrid will copy this grids
120 /// transform, voxelized topology and use its values to compute a
121 /// target points per voxel. The grids ValueType must be convertible
122 /// to a scalar value. Only active and larger than zero values will
123 /// contain points.
124 /// @param pointsPerVoxel The number of points to scatter per voxel
125 /// @param seed A seed for the RandGenT
126 /// @param spread The spread of points as a scale from each voxels center. A value of
127 /// 1.0f indicates points can be placed anywhere within the voxel, where
128 /// as a value of 0.0f will force all points to be created exactly at the
129 /// centers of each voxel.
130 /// @param interrupter An optional interrupter
131 /// @note returns the scattered PointDataGrid
132 template<
133 typename GridT,
134 typename RandGenT = std::mt19937,
135 typename PositionArrayT = TypedAttributeArray<Vec3f, NullCodec>,
136 typename PointDataGridT = Grid<
137 typename points::TreeConverter<typename GridT::TreeType>::Type>,
138 typename InterrupterT = util::NullInterrupter>
139 inline typename PointDataGridT::Ptr
140 nonUniformPointScatter(const GridT& grid,
141 const float pointsPerVoxel,
142 const unsigned int seed = 0,
143 const float spread = 1.0f,
144 InterrupterT* interrupter = nullptr);
145
146
147 ////////////////////////////////////////
148
149 /// @cond OPENVDB_DOCS_INTERNAL
150
151 namespace point_scatter_internal
152 {
153
154 /// @brief initialise the topology of a PointDataGrid and ensure
155 /// everything is voxelized
156 /// @param grid The source grid from which to base the topology generation
157 template<typename PointDataGridT, typename GridT>
158 inline typename PointDataGridT::Ptr
159 91 initialisePointTopology(const GridT& grid)
160 {
161
1/2
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
91 typename PointDataGridT::Ptr points(new PointDataGridT);
162
3/8
✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 51 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 51 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
182 points->setTransform(grid.transform().copy());
163 points->topologyUnion(grid);
164
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 37 times.
91 if (points->tree().hasActiveTiles()) {
165 points->tree().voxelizeActiveTiles();
166 }
167
168 91 return points;
169 }
170
171 /// @brief Generate random point positions for a leaf node
172 /// @param leaf The leaf node to initialize
173 /// @param descriptor The descriptor containing the position type
174 /// @param count The number of points to generate
175 /// @param spread The spread of points from the voxel center
176 /// @param rand01 The random number generator, expected to produce floating point
177 /// values between 0 and 1.
178 template<typename PositionType,
179 typename CodecT,
180 typename RandGenT,
181 typename LeafNodeT>
182 inline void
183 4261 generatePositions(LeafNodeT& leaf,
184 const AttributeSet::Descriptor::Ptr& descriptor,
185 const Index64& count,
186 const float spread,
187 RandGenT& rand01)
188 {
189 using PositionTraits = VecTraits<PositionType>;
190 using ValueType = typename PositionTraits::ElementType;
191 using PositionWriteHandle = AttributeWriteHandle<PositionType, CodecT>;
192
193 4261 leaf.initializeAttributes(descriptor, static_cast<Index>(count));
194
195 // directly expand to avoid needlessly setting uniform values in the
196 // write handle
197 4261 auto& array = leaf.attributeArray(0);
198 4261 array.expand(/*fill*/false);
199
200 4261 PositionWriteHandle pHandle(array, /*expand*/false);
201 PositionType P;
202
2/2
✓ Branch 0 taken 6061003 times.
✓ Branch 1 taken 4038 times.
6102579 for (Index64 index = 0; index < count; ++index) {
203 6098318 P[0] = (spread * (rand01() - ValueType(0.5)));
204 6098318 P[1] = (spread * (rand01() - ValueType(0.5)));
205 6098318 P[2] = (spread * (rand01() - ValueType(0.5)));
206 6098318 pHandle.set(static_cast<Index>(index), P);
207 }
208 4261 }
209
210 } // namespace point_scatter_internal
211
212 /// @endcond
213
214 ////////////////////////////////////////
215
216
217 template<
218 typename GridT,
219 typename RandGenT,
220 typename PositionArrayT,
221 typename PointDataGridT,
222 typename InterrupterT>
223 inline typename PointDataGridT::Ptr
224 34 uniformPointScatter(const GridT& grid,
225 const Index64 count,
226 const unsigned int seed,
227 const float spread,
228 InterrupterT* interrupter)
229 {
230 using PositionType = typename PositionArrayT::ValueType;
231 using PositionTraits = VecTraits<PositionType>;
232 using ValueType = typename PositionTraits::ElementType;
233 using CodecType = typename PositionArrayT::Codec;
234
235 using RandomGenerator = math::Rand01<ValueType, RandGenT>;
236
237 using TreeType = typename PointDataGridT::TreeType;
238 using LeafNodeType = typename TreeType::LeafNodeType;
239 using LeafManagerT = tree::LeafManager<TreeType>;
240
241 struct Local
242 {
243 /// @brief Get the prefixed voxel counts for each leaf node with an
244 /// additional value to represent the end voxel count.
245 /// See also LeafManager::getPrefixSum()
246 15 static void getPrefixSum(LeafManagerT& leafManager,
247 std::vector<Index64>& offsets)
248 {
249 15 Index64 offset = 0;
250 15 offsets.reserve(leafManager.leafCount() + 1);
251 15 offsets.push_back(0);
252 const auto leafRange = leafManager.leafRange();
253 107 for (auto leaf = leafRange.begin(); leaf; ++leaf) {
254 92 offset += leaf->onVoxelCount();
255 92 offsets.push_back(offset);
256 }
257 }
258 };
259
260 static_assert(PositionTraits::IsVec && PositionTraits::Size == 3,
261 "Invalid Position Array type.");
262
263
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
34 if (spread < 0.0f || spread > 1.0f) {
264 OPENVDB_THROW(ValueError, "Spread must be between 0 and 1.");
265 }
266
267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 if (interrupter) interrupter->start("Uniform scattering with fixed point count");
268
269 34 typename PointDataGridT::Ptr points =
270 point_scatter_internal::initialisePointTopology<PointDataGridT>(grid);
271 TreeType& tree = points->tree();
272
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 if (!tree.cbeginLeaf()) return points;
273
274
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
68 LeafManagerT leafManager(tree);
275
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
34 const Index64 voxelCount = leafManager.activeLeafVoxelCount();
276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 assert(voxelCount != 0);
277
278
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
34 const double pointsPerVolume = double(count) / double(voxelCount);
279 34 const Index32 pointsPerVoxel = static_cast<Index32>(math::RoundDown(pointsPerVolume));
280 34 const Index64 remainder = count - (pointsPerVoxel * voxelCount);
281
282
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
34 if (remainder == 0) {
283 return denseUniformPointScatter<
284 GridT, RandGenT, PositionArrayT, PointDataGridT, InterrupterT>(
285
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 grid, float(pointsPerVoxel), seed, spread, interrupter);
286 }
287
288 std::vector<Index64> voxelOffsets, values;
289
1/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
30 std::thread worker(&Local::getPrefixSum, std::ref(leafManager), std::ref(voxelOffsets));
290
291 {
292 30 math::RandInt<Index64, RandGenT> gen(seed, 0, voxelCount-1);
293
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
30 values.reserve(remainder);
294
3/4
✓ Branch 0 taken 431 times.
✓ Branch 1 taken 15 times.
✓ Branch 3 taken 431 times.
✗ Branch 4 not taken.
892 for (Index64 i = 0; i < remainder; ++i) values.emplace_back(gen());
295 }
296
297
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
30 worker.join();
298
299 if (util::wasInterrupted<InterrupterT>(interrupter)) {
300 tree.clear();
301 return points;
302 }
303
304 30 tbb::parallel_sort(values.begin(), values.end());
305 30 const bool fractionalOnly(pointsPerVoxel == 0);
306
307
2/6
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
122 leafManager.foreach([&voxelOffsets, &values, fractionalOnly]
308 (LeafNodeType& leaf, const size_t idx)
309 {
310 92 const Index64 lowerOffset = voxelOffsets[idx]; // inclusive
311 92 const Index64 upperOffset = voxelOffsets[idx + 1]; // exclusive
312
11/22
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 8 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 8 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 8 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 8 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 8 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 19 times.
92 assert(upperOffset > lowerOffset);
313
314 92 const auto valuesEnd = values.end();
315 92 auto lower = std::lower_bound(values.begin(), valuesEnd, lowerOffset);
316
317 92 auto* const data = leaf.buffer().data();
318 auto iter = leaf.beginValueOn();
319
320 Index32 currentOffset(0);
321 92 bool addedPoints(!fractionalOnly);
322
22/22
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 30 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 30 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 30 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 30 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 30 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 30 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 30 times.
✓ Branch 17 taken 1 times.
✓ Branch 18 taken 30 times.
✓ Branch 19 taken 1 times.
✓ Branch 20 taken 186 times.
✓ Branch 21 taken 7 times.
523 while (lower != valuesEnd) {
323 506 const Index64 vId = *lower;
324
21/22
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 23 times.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 23 times.
✓ Branch 7 taken 7 times.
✓ Branch 8 taken 23 times.
✓ Branch 9 taken 7 times.
✓ Branch 10 taken 23 times.
✓ Branch 11 taken 7 times.
✓ Branch 12 taken 23 times.
✓ Branch 13 taken 7 times.
✓ Branch 14 taken 23 times.
✓ Branch 15 taken 7 times.
✓ Branch 16 taken 23 times.
✓ Branch 17 taken 7 times.
✓ Branch 18 taken 23 times.
✓ Branch 19 taken 7 times.
✓ Branch 20 taken 174 times.
✓ Branch 21 taken 12 times.
506 if (vId >= upperOffset) break;
325
326 431 const Index32 nextOffset = Index32(vId - lowerOffset);
327 431 iter.increment(nextOffset - currentOffset);
328 currentOffset = nextOffset;
329
11/22
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 23 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 23 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 23 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 23 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 23 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 23 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 23 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 174 times.
431 assert(iter);
330
331 431 auto& value = data[iter.pos()];
332 431 value = value + 1; // no += operator support
333 addedPoints = true;
334 ++lower;
335 }
336
337 // deactivate this leaf if no points were added. This will speed up
338 // the unthreaded rng
339
12/22
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 8 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 8 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 8 times.
✓ Branch 20 taken 7 times.
✓ Branch 21 taken 12 times.
92 if (!addedPoints) leaf.setValuesOff();
340 });
341
342 voxelOffsets.clear();
343 values.clear();
344
345
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
30 if (fractionalOnly) {
346
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 tools::pruneInactive(tree);
347 leafManager.rebuild();
348 }
349
350
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
30 const AttributeSet::Descriptor::Ptr descriptor =
351 AttributeSet::Descriptor::create(PositionArrayT::attributeType());
352 RandomGenerator rand01(seed);
353
354 const auto leafRange = leafManager.leafRange();
355 30 auto leaf = leafRange.begin();
356
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 15 times.
200 for (; leaf; ++leaf) {
357 if (util::wasInterrupted<InterrupterT>(interrupter)) break;
358 Index32 offset(0);
359
2/2
✓ Branch 1 taken 43520 times.
✓ Branch 2 taken 85 times.
87380 for (auto iter = leaf->beginValueAll(); iter; ++iter) {
360
3/4
✓ Branch 1 taken 43520 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2322 times.
✓ Branch 4 taken 41198 times.
87040 if (iter.isValueOn()) {
361 4644 const Index32 value = Index32(pointsPerVolume + Index32(*iter));
362
2/2
✓ Branch 0 taken 1861 times.
✓ Branch 1 taken 461 times.
4644 if (value == 0) leaf->setValueOff(iter.pos());
363 922 else offset += value;
364 }
365 // @note can't use iter.setValue(offset) on point grids
366 87040 leaf->setOffsetOnly(iter.pos(), offset);
367 }
368
369 // offset should always be non zero
370
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
170 assert(offset != 0);
371 point_scatter_internal::generatePositions<PositionType, CodecType>
372
1/4
✓ Branch 2 taken 85 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
170 (*leaf, descriptor, offset, spread, rand01);
373 }
374
375 // if interrupted, remove remaining leaf nodes
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
30 if (leaf) {
377 for (; leaf; ++leaf) leaf->setValuesOff();
378 tools::pruneInactive(tree);
379 }
380
381
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
30 if (interrupter) interrupter->end();
382 return points;
383 }
384
385
386 ////////////////////////////////////////
387
388
389 template<
390 typename GridT,
391 typename RandGenT,
392 typename PositionArrayT,
393 typename PointDataGridT,
394 typename InterrupterT>
395 inline typename PointDataGridT::Ptr
396 41 denseUniformPointScatter(const GridT& grid,
397 const float pointsPerVoxel,
398 const unsigned int seed,
399 const float spread,
400 InterrupterT* interrupter)
401 {
402 using PositionType = typename PositionArrayT::ValueType;
403 using PositionTraits = VecTraits<PositionType>;
404 using ValueType = typename PositionTraits::ElementType;
405 using CodecType = typename PositionArrayT::Codec;
406
407 using RandomGenerator = math::Rand01<ValueType, RandGenT>;
408
409 using TreeType = typename PointDataGridT::TreeType;
410
411 static_assert(PositionTraits::IsVec && PositionTraits::Size == 3,
412 "Invalid Position Array type.");
413
414
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 23 times.
41 if (pointsPerVoxel < 0.0f) {
415
2/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
8 OPENVDB_THROW(ValueError, "Points per voxel must not be less than zero.");
416 }
417
418
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
39 if (spread < 0.0f || spread > 1.0f) {
419 OPENVDB_THROW(ValueError, "Spread must be between 0 and 1.");
420 }
421
422
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
39 if (interrupter) interrupter->start("Dense uniform scattering with fixed point count");
423
424 39 typename PointDataGridT::Ptr points =
425 point_scatter_internal::initialisePointTopology<PointDataGridT>(grid);
426 TreeType& tree = points->tree();
427 auto leafIter = tree.beginLeaf();
428
1/2
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
39 if (!leafIter) return points;
429
430 39 const Index32 pointsPerVoxelInt = math::Floor(pointsPerVoxel);
431
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 1 times.
39 const double delta = pointsPerVoxel - float(pointsPerVoxelInt);
432 const bool fractional = !math::isApproxZero(delta, 1.0e-6);
433 const bool fractionalOnly = pointsPerVoxelInt == 0;
434
435
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
39 const AttributeSet::Descriptor::Ptr descriptor =
436 AttributeSet::Descriptor::create(PositionArrayT::attributeType());
437 RandomGenerator rand01(seed);
438
439
2/2
✓ Branch 0 taken 3912 times.
✓ Branch 1 taken 23 times.
4048 for (; leafIter; ++leafIter) {
440 if (util::wasInterrupted<InterrupterT>(interrupter)) break;
441 Index32 offset(0);
442
2/2
✓ Branch 1 taken 2002944 times.
✓ Branch 2 taken 3912 times.
2060626 for (auto iter = leafIter->beginValueAll(); iter; ++iter) {
443
3/4
✓ Branch 1 taken 2002944 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 755822 times.
✓ Branch 4 taken 1247122 times.
2052608 if (iter.isValueOn()) {
444 758683 offset += pointsPerVoxelInt;
445
4/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 755795 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 22 times.
758737 if (fractional && rand01() < delta) ++offset;
446
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 755741 times.
758639 else if (fractionalOnly) leafIter->setValueOff(iter.pos());
447 }
448 // @note can't use iter.setValue(offset) on point grids
449 2052608 leafIter->setOffsetOnly(iter.pos(), offset);
450 }
451
452
2/2
✓ Branch 0 taken 3896 times.
✓ Branch 1 taken 16 times.
4009 if (offset != 0) {
453 point_scatter_internal::generatePositions<PositionType, CodecType>
454
1/4
✓ Branch 1 taken 3896 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3977 (*leafIter, descriptor, offset, spread, rand01);
455 }
456 }
457
458 // if interrupted, remove remaining leaf nodes
459
3/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 3 times.
39 const bool prune(leafIter || fractionalOnly);
460
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
39 for (; leafIter; ++leafIter) leafIter->setValuesOff();
461
462
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 20 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
39 if (prune) tools::pruneInactive(tree);
463
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
39 if (interrupter) interrupter->end();
464 return points;
465 }
466
467
468 ////////////////////////////////////////
469
470
471 template<
472 typename GridT,
473 typename RandGenT,
474 typename PositionArrayT,
475 typename PointDataGridT,
476 typename InterrupterT>
477 inline typename PointDataGridT::Ptr
478 12 nonUniformPointScatter(const GridT& grid,
479 const float pointsPerVoxel,
480 const unsigned int seed,
481 const float spread,
482 InterrupterT* interrupter)
483 {
484 using PositionType = typename PositionArrayT::ValueType;
485 using PositionTraits = VecTraits<PositionType>;
486 using ValueType = typename PositionTraits::ElementType;
487 using CodecType = typename PositionArrayT::Codec;
488
489 using RandomGenerator = math::Rand01<ValueType, RandGenT>;
490
491 using TreeType = typename PointDataGridT::TreeType;
492
493 static_assert(PositionTraits::IsVec && PositionTraits::Size == 3,
494 "Invalid Position Array type.");
495 static_assert(std::is_arithmetic<typename GridT::ValueType>::value,
496 "Scalar grid type required for weighted voxel scattering.");
497
498 12 if (pointsPerVoxel < 0.0f) {
499 4 OPENVDB_THROW(ValueError, "Points per voxel must not be less than zero.");
500 }
501
502 11 if (spread < 0.0f || spread > 1.0f) {
503 OPENVDB_THROW(ValueError, "Spread must be between 0 and 1.");
504 }
505
506 11 if (interrupter) interrupter->start("Non-uniform scattering with local point density");
507
508 11 typename PointDataGridT::Ptr points =
509 point_scatter_internal::initialisePointTopology<PointDataGridT>(grid);
510 TreeType& tree = points->tree();
511 auto leafIter = tree.beginLeaf();
512 11 if (!leafIter) return points;
513
514 11 const AttributeSet::Descriptor::Ptr descriptor =
515 AttributeSet::Descriptor::create(PositionArrayT::attributeType());
516 RandomGenerator rand01(seed);
517 const auto accessor = grid.getConstAccessor();
518
519 68 for (; leafIter; ++leafIter) {
520 if (util::wasInterrupted<InterrupterT>(interrupter)) break;
521 Index32 offset(0);
522 29298 for (auto iter = leafIter->beginValueAll(); iter; ++iter) {
523 29184 if (iter.isValueOn()) {
524 2712 double fractional =
525 2712 double(accessor.getValue(iter.getCoord())) * pointsPerVoxel;
526 2712 fractional = std::max(0.0, fractional);
527 2712 int count = int(fractional);
528 2712 if (rand01() < (fractional - double(count))) ++count;
529 2712 else if (count == 0) leafIter->setValueOff(iter.pos());
530 2712 offset += count;
531 }
532 // @note can't use iter.setValue(offset) on point grids
533 29184 leafIter->setOffsetOnly(iter.pos(), offset);
534 }
535
536 57 if (offset != 0) {
537 point_scatter_internal::generatePositions<PositionType, CodecType>
538 57 (*leafIter, descriptor, offset, spread, rand01);
539 }
540 }
541
542 // if interrupted, remove remaining leaf nodes
543 11 for (; leafIter; ++leafIter) leafIter->setValuesOff();
544
545 11 tools::pruneInactive(points->tree());
546 11 if (interrupter) interrupter->end();
547 return points;
548 }
549
550
551 } // namespace points
552 } // namespace OPENVDB_VERSION_NAME
553 } // namespace openvdb
554
555
556 #endif // OPENVDB_POINTS_POINT_SCATTER_HAS_BEEN_INCLUDED
557