| 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 |