GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/tools/PointScatter.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 77 86 89.5%
Functions: 11 12 91.7%
Branches: 68 132 51.5%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
4 /// @author Ken Museth
5 ///
6 /// @file tools/PointScatter.h
7 ///
8 /// @brief We offer three different algorithms (each in its own class)
9 /// for scattering of points in active voxels:
10 ///
11 /// 1) UniformPointScatter. Has two modes: Either randomly distributes
12 /// a fixed number of points into the active voxels, or the user can
13 /// specify a fixed probability of having a points per unit of volume.
14 ///
15 /// 2) DenseUniformPointScatter. Randomly distributes points into active
16 /// voxels using a fixed number of points per voxel.
17 ///
18 /// 3) NonIniformPointScatter. Define the local probability of having
19 /// a point in a voxel as the product of a global density and the
20 /// value of the voxel itself.
21
22 #ifndef OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED
23 #define OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED
24
25 #include <openvdb/Types.h>
26 #include <openvdb/Grid.h>
27 #include <openvdb/math/Math.h>
28 #include <openvdb/util/NullInterrupter.h>
29 #include <tbb/parallel_sort.h>
30 #include <tbb/parallel_for.h>
31 #include <iostream>
32 #include <memory>
33 #include <string>
34
35 namespace openvdb {
36 OPENVDB_USE_VERSION_NAMESPACE
37 namespace OPENVDB_VERSION_NAME {
38 namespace tools {
39
40 /// Forward declaration of base class
41 template<typename PointAccessorType,
42 typename RandomGenerator,
43 typename InterruptType = util::NullInterrupter>
44 class BasePointScatter;
45
46 /// @brief The two point scatters UniformPointScatter and
47 /// NonUniformPointScatter depend on the following two classes:
48 ///
49 /// The @c PointAccessorType template argument below refers to any class
50 /// with the following interface:
51 /// @code
52 /// class PointAccessor {
53 /// ...
54 /// public:
55 /// void add(const openvdb::Vec3R &pos);// appends point with world positions pos
56 /// };
57 /// @endcode
58 ///
59 ///
60 /// The @c InterruptType template argument below refers to any class
61 /// with the following interface:
62 /// @code
63 /// class Interrupter {
64 /// ...
65 /// public:
66 /// void start(const char* name = nullptr) // called when computations begin
67 /// void end() // called when computations end
68 /// bool wasInterrupted(int percent=-1) // return true to break computation
69 ///};
70 /// @endcode
71 ///
72 /// @note If no template argument is provided for this InterruptType
73 /// the util::NullInterrupter is used which implies that all
74 /// interrupter calls are no-ops (i.e. incurs no computational overhead).
75
76
77 /// @brief Uniformly scatters points in the active voxels.
78 /// The point count is either explicitly defined or implicitly
79 /// through the specification of a global density (=points-per-volume)
80 ///
81 /// @note This uniform scattering technique assumes that the number of
82 /// points is generally smaller than the number of active voxels
83 /// (including virtual active voxels in active tiles).
84 template<typename PointAccessorType,
85 typename RandomGenerator,
86 typename InterruptType = util::NullInterrupter>
87 class UniformPointScatter : public BasePointScatter<PointAccessorType,
88 RandomGenerator,
89 InterruptType>
90 {
91 public:
92 using BaseT = BasePointScatter<PointAccessorType, RandomGenerator, InterruptType>;
93
94 11 UniformPointScatter(PointAccessorType& points,
95 Index64 pointCount,
96 RandomGenerator& randGen,
97 double spread = 1.0,
98 InterruptType* interrupt = nullptr)
99 : BaseT(points, randGen, spread, interrupt)
100 , mTargetPointCount(pointCount)
101
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
11 , mPointsPerVolume(0.0f)
102 {
103 }
104 1 UniformPointScatter(PointAccessorType& points,
105 float pointsPerVolume,
106 RandomGenerator& randGen,
107 double spread = 1.0,
108 InterruptType* interrupt = nullptr)
109 : BaseT(points, randGen, spread, interrupt)
110 , mTargetPointCount(0)
111
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 , mPointsPerVolume(pointsPerVolume)
112 {
113 }
114
115 /// This is the main functor method implementing the actual scattering of points.
116 template<typename GridT>
117 12 bool operator()(const GridT& grid)
118 {
119 12 mVoxelCount = grid.activeVoxelCount();
120
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 times.
12 if (mVoxelCount == 0) return false;
121
122 const auto voxelVolume = grid.transform().voxelVolume();
123
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (mPointsPerVolume > 0) {
124 BaseT::start("Uniform scattering with fixed point density");
125 1 mTargetPointCount = Index64(mPointsPerVolume * voxelVolume * double(mVoxelCount));
126
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 } else if (mTargetPointCount > 0) {
127 BaseT::start("Uniform scattering with fixed point count");
128 10 mPointsPerVolume = float(mTargetPointCount) / float(voxelVolume * double(mVoxelCount));
129 } else {
130 return false;
131 }
132
133
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 std::unique_ptr<Index64[]> idList{new Index64[mTargetPointCount]};
134
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
22 math::RandInt<Index64, RandomGenerator> rand(BaseT::mRand01.engine(), 0, mVoxelCount-1);
135
2/2
✓ Branch 0 taken 328144 times.
✓ Branch 1 taken 11 times.
328155 for (Index64 i=0; i<mTargetPointCount; ++i) idList[i] = rand();
136
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 tbb::parallel_sort(idList.get(), idList.get() + mTargetPointCount);
137
138 11 CoordBBox bbox;
139 const Vec3R offset(0.5, 0.5, 0.5);
140 typename GridT::ValueOnCIter valueIter = grid.cbeginValueOn();
141
142
2/2
✓ Branch 0 taken 328144 times.
✓ Branch 1 taken 11 times.
328155 for (Index64 i=0, n=valueIter.getVoxelCount() ; i != mTargetPointCount; ++i) {
143
2/4
✓ Branch 1 taken 328144 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 328144 times.
✗ Branch 4 not taken.
328144 if (BaseT::interrupt()) return false;
144 328144 const Index64 voxelId = idList[i];
145
2/2
✓ Branch 0 taken 650138 times.
✓ Branch 1 taken 328144 times.
978282 while ( n <= voxelId ) {
146 ++valueIter;
147 650138 n += valueIter.getVoxelCount();
148 }
149 if (valueIter.isVoxelValue()) {// a majority is expected to be voxels
150
1/2
✓ Branch 1 taken 321052 times.
✗ Branch 2 not taken.
321052 BaseT::addPoint(grid, valueIter.getCoord() - offset);
151 } else {// tiles contain multiple (virtual) voxels
152
1/2
✓ Branch 1 taken 7092 times.
✗ Branch 2 not taken.
7092 valueIter.getBoundingBox(bbox);
153
1/4
✓ Branch 1 taken 7092 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
7092 BaseT::addPoint(grid, bbox.min() - offset, bbox.extents());
154 }
155 }//loop over all the active voxels and tiles
156 //}
157
158 BaseT::end();
159 return true;
160 }
161
162 // The following methods should only be called after the
163 // the operator() method was called
164 void print(const std::string &name, std::ostream& os = std::cout) const
165 {
166 os << "Uniformly scattered " << mPointCount << " points into " << mVoxelCount
167 << " active voxels in \"" << name << "\" corresponding to "
168 << mPointsPerVolume << " points per volume." << std::endl;
169 }
170
171 float getPointsPerVolume() const { return mPointsPerVolume; }
172 Index64 getTargetPointCount() const { return mTargetPointCount; }
173
174 private:
175
176 using BaseT::mPointCount;
177 using BaseT::mVoxelCount;
178 Index64 mTargetPointCount;
179 float mPointsPerVolume;
180
181 }; // class UniformPointScatter
182
183 /// @brief Scatters a fixed (and integer) number of points in all
184 /// active voxels and tiles.
185 template<typename PointAccessorType,
186 typename RandomGenerator,
187 typename InterruptType = util::NullInterrupter>
188 class DenseUniformPointScatter : public BasePointScatter<PointAccessorType,
189 RandomGenerator,
190 InterruptType>
191 {
192 public:
193 using BaseT = BasePointScatter<PointAccessorType, RandomGenerator, InterruptType>;
194
195 1 DenseUniformPointScatter(PointAccessorType& points,
196 float pointsPerVoxel,
197 RandomGenerator& randGen,
198 double spread = 1.0,
199 InterruptType* interrupt = nullptr)
200 : BaseT(points, randGen, spread, interrupt)
201
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 , mPointsPerVoxel(pointsPerVoxel)
202 {
203 }
204
205 /// This is the main functor method implementing the actual scattering of points.
206 template<typename GridT>
207 1 bool operator()(const GridT& grid)
208 {
209 using ValueIter = typename GridT::ValueOnCIter;
210
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (mPointsPerVoxel < 1.0e-6) return false;
211 1 mVoxelCount = grid.activeVoxelCount();
212
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (mVoxelCount == 0) return false;
213 BaseT::start("Dense uniform scattering with fixed point count");
214 1 CoordBBox bbox;
215 const Vec3R offset(0.5, 0.5, 0.5);
216
217
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 const int ppv = math::Floor(mPointsPerVoxel);
218
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 const double delta = mPointsPerVoxel - float(ppv);
219 const bool fractional = !math::isApproxZero(delta, 1.0e-6);
220
221
2/2
✓ Branch 0 taken 262144 times.
✓ Branch 1 taken 1 times.
262145 for (ValueIter iter = grid.cbeginValueOn(); iter; ++iter) {
222
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 262144 times.
262144 if (BaseT::interrupt()) return false;
223 if (iter.isVoxelValue()) {// a majority is expected to be voxels
224 262144 const Vec3R dmin = iter.getCoord() - offset;
225
2/2
✓ Branch 0 taken 2097152 times.
✓ Branch 1 taken 262144 times.
2359296 for (int n = 0; n != ppv; ++n) BaseT::addPoint(grid, dmin);
226
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 262144 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
262144 if (fractional && BaseT::getRand01() < delta) BaseT::addPoint(grid, dmin);
227 } else {// tiles contain multiple (virtual) voxels
228 iter.getBoundingBox(bbox);
229 const Coord size(bbox.extents());
230 const Vec3R dmin = bbox.min() - offset;
231 const double d = mPointsPerVoxel * float(iter.getVoxelCount());
232 const int m = math::Floor(d);
233 for (int n = 0; n != m; ++n) BaseT::addPoint(grid, dmin, size);
234 if (BaseT::getRand01() < d - m) BaseT::addPoint(grid, dmin, size);
235 }
236 }//loop over all the active voxels and tiles
237 //}
238 BaseT::end();
239 return true;
240 }
241
242 // The following methods should only be called after the
243 // the operator() method was called
244 void print(const std::string &name, std::ostream& os = std::cout) const
245 {
246 os << "Dense uniformly scattered " << mPointCount << " points into " << mVoxelCount
247 << " active voxels in \"" << name << "\" corresponding to "
248 << mPointsPerVoxel << " points per voxel." << std::endl;
249 }
250
251 float getPointsPerVoxel() const { return mPointsPerVoxel; }
252
253 private:
254 using BaseT::mPointCount;
255 using BaseT::mVoxelCount;
256 float mPointsPerVoxel;
257 }; // class DenseUniformPointScatter
258
259 /// @brief Non-uniform scatters of point in the active voxels.
260 /// The local point count is implicitly defined as a product of
261 /// of a global density (called pointsPerVolume) and the local voxel
262 /// (or tile) value.
263 ///
264 /// @note This scattering technique can be significantly slower
265 /// than a uniform scattering since its computational complexity
266 /// is proportional to the active voxel (and tile) count.
267 template<typename PointAccessorType,
268 typename RandomGenerator,
269 typename InterruptType = util::NullInterrupter>
270 class NonUniformPointScatter : public BasePointScatter<PointAccessorType,
271 RandomGenerator,
272 InterruptType>
273 {
274 public:
275 using BaseT = BasePointScatter<PointAccessorType, RandomGenerator, InterruptType>;
276
277 1 NonUniformPointScatter(PointAccessorType& points,
278 float pointsPerVolume,
279 RandomGenerator& randGen,
280 double spread = 1.0,
281 InterruptType* interrupt = nullptr)
282 : BaseT(points, randGen, spread, interrupt)
283
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 , mPointsPerVolume(pointsPerVolume)//note this is merely a
284 //multiplier for the local point density
285 {
286 }
287
288 /// This is the main functor method implementing the actual scattering of points.
289 template<typename GridT>
290 1 bool operator()(const GridT& grid)
291 {
292
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (mPointsPerVolume <= 0.0f) return false;
293 1 mVoxelCount = grid.activeVoxelCount();
294
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (mVoxelCount == 0) return false;
295 BaseT::start("Non-uniform scattering with local point density");
296 const Vec3d dim = grid.voxelSize();
297 1 const double volumePerVoxel = dim[0]*dim[1]*dim[2],
298 1 pointsPerVoxel = mPointsPerVolume * volumePerVoxel;
299 1 CoordBBox bbox;
300 const Vec3R offset(0.5, 0.5, 0.5);
301
2/2
✓ Branch 0 taken 262144 times.
✓ Branch 1 taken 1 times.
262145 for (typename GridT::ValueOnCIter iter = grid.cbeginValueOn(); iter; ++iter) {
302
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 262144 times.
262144 if (BaseT::interrupt()) return false;
303
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 262144 times.
262144 const double d = double(*iter) * pointsPerVoxel * double(iter.getVoxelCount());
304
1/2
✓ Branch 0 taken 262144 times.
✗ Branch 1 not taken.
262144 const int n = int(d);
305 if (iter.isVoxelValue()) { // a majority is expected to be voxels
306 262144 const Vec3R dmin =iter.getCoord() - offset;
307
2/2
✓ Branch 0 taken 441252 times.
✓ Branch 1 taken 262144 times.
703396 for (int i = 0; i < n; ++i) BaseT::addPoint(grid, dmin);
308
2/2
✓ Branch 0 taken 5278 times.
✓ Branch 1 taken 256866 times.
262144 if (BaseT::getRand01() < (d - n)) BaseT::addPoint(grid, dmin);
309 } else { // tiles contain multiple (virtual) voxels
310 iter.getBoundingBox(bbox);
311 const Coord size(bbox.extents());
312 const Vec3R dmin = bbox.min() - offset;
313 for (int i = 0; i < n; ++i) BaseT::addPoint(grid, dmin, size);
314 if (BaseT::getRand01() < (d - n)) BaseT::addPoint(grid, dmin, size);
315 }
316 }//loop over all the active voxels and tiles
317 BaseT::end();
318 return true;
319 }
320
321 // The following methods should only be called after the
322 // the operator() method was called
323 void print(const std::string &name, std::ostream& os = std::cout) const
324 {
325 os << "Non-uniformly scattered " << mPointCount << " points into " << mVoxelCount
326 << " active voxels in \"" << name << "\"." << std::endl;
327 }
328
329 float getPointPerVolume() const { return mPointsPerVolume; }
330
331 private:
332 using BaseT::mPointCount;
333 using BaseT::mVoxelCount;
334 float mPointsPerVolume;
335
336 }; // class NonUniformPointScatter
337
338 /// Base class of all the point scattering classes defined above
339 template<typename PointAccessorType,
340 typename RandomGenerator,
341 typename InterruptType>
342 class BasePointScatter
343 {
344 public:
345
346
5/10
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
5 Index64 getPointCount() const { return mPointCount; }
347
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
4 Index64 getVoxelCount() const { return mVoxelCount; }
348
349 protected:
350
351 PointAccessorType& mPoints;
352 InterruptType* mInterrupter;
353 Index64 mPointCount;
354 Index64 mVoxelCount;
355 Index64 mInterruptCount;
356 const double mSpread;
357 math::Rand01<double, RandomGenerator> mRand01;
358
359 /// This is a base class so the constructor is protected
360 14 BasePointScatter(PointAccessorType& points,
361 RandomGenerator& randGen,
362 double spread,
363 InterruptType* interrupt = nullptr)
364 : mPoints(points)
365 , mInterrupter(interrupt)
366 , mPointCount(0)
367 , mVoxelCount(0)
368 , mInterruptCount(0)
369 , mSpread(math::Clamp01(spread))
370
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
14 , mRand01(randGen)
371 {
372 }
373
374 inline void start(const char* name)
375 {
376
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
13 if (mInterrupter) mInterrupter->start(name);
377 }
378
379 inline void end()
380 {
381
3/8
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
13 if (mInterrupter) mInterrupter->end();
382 }
383
384 852432 inline bool interrupt()
385 {
386 //only check interrupter for every 32'th call
387
3/4
✓ Branch 0 taken 26645 times.
✓ Branch 1 taken 825787 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 26645 times.
852432 return !(mInterruptCount++ & ((1<<5)-1)) && util::wasInterrupted(mInterrupter);
388 }
389
390 /// @brief Return a random floating point number between zero and one
391 inline double getRand01() { return mRand01(); }
392
393 /// @brief Return a random floating point number between 0.5 -+ mSpread/2
394 8615478 inline double getRand() { return 0.5 + mSpread * (mRand01() - 0.5); }
395
396 template <typename GridT>
397 2864734 inline void addPoint(const GridT &grid, const Vec3R &dmin)
398 {
399 2864734 const Vec3R pos(dmin[0] + this->getRand(),
400 2864734 dmin[1] + this->getRand(),
401 2864734 dmin[2] + this->getRand());
402 2864734 mPoints.add(grid.indexToWorld(pos));
403 2864734 ++mPointCount;
404 2864734 }
405
406 template <typename GridT>
407 7092 inline void addPoint(const GridT &grid, const Vec3R &dmin, const Coord &size)
408 {
409 7092 const Vec3R pos(dmin[0] + size[0]*this->getRand(),
410 7092 dmin[1] + size[1]*this->getRand(),
411 7092 dmin[2] + size[2]*this->getRand());
412 7092 mPoints.add(grid.indexToWorld(pos));
413 7092 ++mPointCount;
414 7092 }
415 };// class BasePointScatter
416
417 } // namespace tools
418 } // namespace OPENVDB_VERSION_NAME
419 } // namespace openvdb
420
421 #endif // OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED
422