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 |