| 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 PointTransfer.h | ||
| 7 | /// | ||
| 8 | /// @brief Framework methods for rasterizing PointDataGrid data to Trees. | ||
| 9 | /// | ||
| 10 | /// @details Provides a generic inherited interface for deriving transfer | ||
| 11 | /// schemes that represent how point data should be rasterized. The provided | ||
| 12 | /// components together support the transfer of multiple attributes to | ||
| 13 | /// arbitrary and multiple grid types. Target grids must have the same | ||
| 14 | /// transform, but this transform can differ from the source PointDataGrid | ||
| 15 | /// (multiple instantiations of rasterize() should instead be invoked to | ||
| 16 | /// transfer to grids of different transforms). Arbitrary attributes can be | ||
| 17 | /// accessed and transfered to arbitrary trees. | ||
| 18 | /// | ||
| 19 | |||
| 20 | #ifndef OPENVEB_POINTS_TRANSFER_HAS_BEEN_INCLUDED | ||
| 21 | #define OPENVEB_POINTS_TRANSFER_HAS_BEEN_INCLUDED | ||
| 22 | |||
| 23 | #include <openvdb/openvdb.h> | ||
| 24 | #include <openvdb/Types.h> | ||
| 25 | #include <openvdb/Grid.h> | ||
| 26 | #include <openvdb/math/Transform.h> | ||
| 27 | #include <openvdb/util/NullInterrupter.h> | ||
| 28 | #include <openvdb/thread/Threading.h> | ||
| 29 | |||
| 30 | #include <type_traits> | ||
| 31 | #include <tuple> | ||
| 32 | |||
| 33 | namespace openvdb { | ||
| 34 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 35 | namespace OPENVDB_VERSION_NAME { | ||
| 36 | namespace points { | ||
| 37 | |||
| 38 | /// @par A transfer scheme must be configured to call the provided | ||
| 39 | /// rasterize methods. See below for an example or | ||
| 40 | /// PointRasterizeSDF.h/PointRasterizeTrilinear.h for implementations. | ||
| 41 | /// @code | ||
| 42 | /// struct Transfer | ||
| 43 | /// { | ||
| 44 | /// /// @return Returns the tree topology to loop over. This can be different | ||
| 45 | /// /// from the destination tree i.e. This can act as a mask. | ||
| 46 | /// inline auto& topology(); | ||
| 47 | /// | ||
| 48 | /// /// @brief The maximum lookup range of this transfer scheme in index | ||
| 49 | /// /// space of the source points. | ||
| 50 | /// /// @details The return value represent how far away from the destination | ||
| 51 | /// /// leaf node points should be accessed. | ||
| 52 | /// /// @param origin The leaf origin of the topology being accessed | ||
| 53 | /// /// @param idx The leaf index of the topology being accessed | ||
| 54 | /// inline Int32 range(const Coord& origin, size_t idx) const; | ||
| 55 | /// | ||
| 56 | /// /// @brief The initialize function, called on each leaf which has valid | ||
| 57 | /// /// topology to write to. | ||
| 58 | /// /// @param origin The leaf origin of the topology being accessed | ||
| 59 | /// /// @param idx The leaf index of the topology being accessed | ||
| 60 | /// /// @param bounds The active voxel bounds of the leaf | ||
| 61 | /// inline void initialize(const Coord& origin, size_t idx, const CoordBBox& bounds); | ||
| 62 | /// | ||
| 63 | /// /// @brief Run each time a point leaf is accessed. Typically this is | ||
| 64 | /// /// where attribute handles can be constructed | ||
| 65 | /// /// @param leaf The PointDataLeafNode which is being accessed. | ||
| 66 | /// /// @return Return true to continue rasterization, false to early exit | ||
| 67 | /// /// and skip the current leaf's contribution to the destination volume. | ||
| 68 | /// inline bool startPointLeaf(const PointDataTree::LeafNodeType& leaf); | ||
| 69 | /// | ||
| 70 | /// /// @brief The point stamp function. Each point which contributes to | ||
| 71 | /// /// the current leaf will call this function exactly once. | ||
| 72 | /// /// @param ijk The current voxel containing the point being rasterized. | ||
| 73 | /// /// May be outside the destination leaf node depending on the range() | ||
| 74 | /// /// @param id The point index being rasterized | ||
| 75 | /// /// @param bounds The active bounds of the leaf node. | ||
| 76 | /// void rasterizePoint(const Coord& ijk, | ||
| 77 | /// const Index id, | ||
| 78 | /// const CoordBBox& bounds); | ||
| 79 | /// | ||
| 80 | /// /// @brief Run each time a point leaf is finished with. | ||
| 81 | /// /// @param leaf The PointDataLeafNode which was being accessed. | ||
| 82 | /// /// @return Return true to continue rasterization, false to early exit | ||
| 83 | /// /// and stop rasterization to the destination leaf node. | ||
| 84 | /// inline bool endPointLeaf(const PointDataTree::LeafNodeType& leaf); | ||
| 85 | /// | ||
| 86 | /// /// @brief The finalization function for the given destination tree(s). | ||
| 87 | /// /// @param origin The leaf origin of the topology being accessed | ||
| 88 | /// /// @param idx The leaf index of the topology being accessed | ||
| 89 | /// /// @return Return true to stop, false to recursively rasterize | ||
| 90 | /// inline bool finalize(const Coord& origin, size_t idx); | ||
| 91 | /// }; | ||
| 92 | /// @endcode | ||
| 93 | /// | ||
| 94 | /// | ||
| 95 | /// Below is a full example using the native components. | ||
| 96 | /// | ||
| 97 | /// @code | ||
| 98 | /// /// @brief Sum point distances into a target float tree | ||
| 99 | /// /// Note: Using TransformTransfer to handle different index spaces, and | ||
| 100 | /// /// VolumeTransfer for automatic buffer setup | ||
| 101 | /// struct MyTransfer : | ||
| 102 | /// public TransformTransfer, | ||
| 103 | /// public VolumeTransfer<FloatTree> | ||
| 104 | /// { | ||
| 105 | /// MyTransfer(FloatGrid& dest, const PointDataGrid& source) | ||
| 106 | /// : TransformTransfer(source.transform(), dest.transform()) | ||
| 107 | /// , VolumeTransfer(dest.tree()) | ||
| 108 | /// , mHandle(nullptr) {} | ||
| 109 | /// | ||
| 110 | /// MyTransfer(const MyTransfer& other) | ||
| 111 | /// : TransformTransfer(other) | ||
| 112 | /// , VolumeTransfer(other) | ||
| 113 | /// , mHandle(nullptr) {} | ||
| 114 | /// | ||
| 115 | /// /// @brief Range in index space of the source points | ||
| 116 | /// Int32 range(const Coord&, size_t) const { return Int32(1); } | ||
| 117 | /// | ||
| 118 | /// /// @brief Every time we start a new point leaf, init the position array. | ||
| 119 | /// /// Always return true as we don't skip any leaf nodes. | ||
| 120 | /// bool startPointLeaf(const PointDataTree::LeafNodeType& leaf) | ||
| 121 | /// { | ||
| 122 | /// mHandle.reset(new AttributeHandle<Vec3f>(leaf.constAttributeArray("P")); | ||
| 123 | /// return true; | ||
| 124 | /// } | ||
| 125 | /// | ||
| 126 | /// /// @brief For each point, compute its relative index space position in | ||
| 127 | /// /// the destination tree and sum the length of its distance | ||
| 128 | /// void rasterizePoint(const Coord& ijk, const Index id, const CoordBBox& bounds) | ||
| 129 | /// { | ||
| 130 | /// Vec3d P = ijk.asVec3d() + Vec3d(this->mHandle->get(id)); | ||
| 131 | /// P = this->transformSourceToTarget(P); // TransformTransfer::transformSourceToTarget | ||
| 132 | /// // for each active voxel, accumulate distance | ||
| 133 | /// const auto* mask = this->mask(); // VolumeTransfer::mask | ||
| 134 | /// for (auto& coord : bounds) { | ||
| 135 | /// const Index voxel = FloatTree::LeafNodeType::coordToOffset(coord); | ||
| 136 | /// if (!mask->isOn(voxel)) continue; | ||
| 137 | /// Vec3d dist = coord.asVec3d() - P; | ||
| 138 | /// this->buffer()[voxel] += dist.length(); // VolumeTransfer::buffer | ||
| 139 | /// } | ||
| 140 | /// } | ||
| 141 | /// | ||
| 142 | /// /// @brief Return true for endPointLeaf() to continue, false for finalize() so | ||
| 143 | /// /// we don't recurse. | ||
| 144 | /// bool endPointLeaf(const PointDataTree::LeafNodeType&) { return true; } | ||
| 145 | /// bool finalize(const Coord&, size_t) { return false; } | ||
| 146 | /// | ||
| 147 | /// private: | ||
| 148 | /// std::unique_ptr<AttributeHandle<Vec3f>> mHandle; | ||
| 149 | /// }; | ||
| 150 | /// @endcode | ||
| 151 | |||
| 152 | |||
| 153 | /// @brief Perform potentially complex rasterization from a user defined | ||
| 154 | /// transfer scheme. | ||
| 155 | /// @details The method works by looping over a single Tree topology, looking | ||
| 156 | /// up point data at a position relative to that topology and passing that | ||
| 157 | /// data to a transfer scheme TransferT. | ||
| 158 | /// @note Each thread receives a copy of the transfer scheme object. | ||
| 159 | /// @param points the point data grid to rasterize | ||
| 160 | /// @param transfer the transfer scheme | ||
| 161 | /// @param filter optional point filter | ||
| 162 | /// @param interrupter optional interrupter | ||
| 163 | template <typename PointDataTreeOrGridT, | ||
| 164 | typename TransferT, | ||
| 165 | typename FilterT = NullFilter, | ||
| 166 | typename InterrupterT = util::NullInterrupter> | ||
| 167 | inline void | ||
| 168 | rasterize(const PointDataTreeOrGridT& points, | ||
| 169 | TransferT& transfer, | ||
| 170 | const FilterT& filter = NullFilter(), | ||
| 171 | InterrupterT* interrupter = nullptr); | ||
| 172 | |||
| 173 | |||
| 174 | /////////////////////////////////////////////////// | ||
| 175 | |||
| 176 | /// @brief The TransformTransfer module should be used if the source transform | ||
| 177 | /// of the input points and the target transforms of the destination volumes | ||
| 178 | /// differ. The default rasterizer will skip index to world (and vice versa) | ||
| 179 | /// transformations unless a transfer scheme derives from a TransformTransfer. | ||
| 180 | struct TransformTransfer | ||
| 181 | { | ||
| 182 | TransformTransfer(const math::Transform& st, | ||
| 183 | const math::Transform& tt) | ||
| 184 | 49 | : mSourceTransform(st) | |
| 185 | 12 | , mTargetTransform(tt) {} | |
| 186 | |||
| 187 | template <typename T> | ||
| 188 | 3677 | inline auto transformSourceToTarget(const T& value) const | |
| 189 | { | ||
| 190 | 3677 | const auto result = mSourceTransform.indexToWorld(value); | |
| 191 | 3677 | return mTargetTransform.worldToIndex(result); | |
| 192 | } | ||
| 193 | |||
| 194 | template <typename T> | ||
| 195 | inline auto transformTargetToSource(const T& value) const | ||
| 196 | { | ||
| 197 | const auto result = mTargetTransform.indexToWorld(value); | ||
| 198 | return mSourceTransform.worldToIndex(result); | ||
| 199 | } | ||
| 200 | |||
| 201 | 2739 | const math::Transform& sourceTransform() const { return mSourceTransform; } | |
| 202 | 104346 | const math::Transform& targetTransform() const { return mTargetTransform; } | |
| 203 | |||
| 204 | private: | ||
| 205 | const math::Transform& mSourceTransform; | ||
| 206 | const math::Transform& mTargetTransform; | ||
| 207 | }; | ||
| 208 | |||
| 209 | /// @brief The VolumeTransfer module provides methods to automatically setup | ||
| 210 | /// and access destination buffers for multiple target volumes of arbitrary | ||
| 211 | /// types. Deriving from a VolumeTransfer ensures that the available | ||
| 212 | /// buffers correlate to the order of the provided tree arguments. | ||
| 213 | template <typename ...TreeTypes> | ||
| 214 | struct VolumeTransfer | ||
| 215 | { | ||
| 216 | static const size_t Size = sizeof...(TreeTypes); | ||
| 217 | using TreeTupleT = std::tuple<TreeTypes*...>; | ||
| 218 | |||
| 219 | template <size_t Idx> using TreeType = typename std::tuple_element<Idx, std::tuple<TreeTypes...>>::type; | ||
| 220 | template <size_t Idx> using ValueType = typename TreeType<Idx>::ValueType; | ||
| 221 | template <typename T> struct TypeResolver { using Type = typename T::ValueType; }; | ||
| 222 | using NodeMaskT = typename TreeType<0>::LeafNodeType::NodeMaskType; | ||
| 223 | |||
| 224 | VolumeTransfer(TreeTypes*... trees); | ||
| 225 | |||
| 226 | 2 | VolumeTransfer(TreeTypes&... trees) | |
| 227 | 2 | : VolumeTransfer(&trees...) {} | |
| 228 | |||
| 229 | 301 | VolumeTransfer(const VolumeTransfer& other) | |
| 230 | : mTreeArray(other.mTreeArray) | ||
| 231 | , mBuffers() | ||
| 232 |
4/4✓ Branch 0 taken 295 times.
✓ Branch 1 taken 295 times.
✓ Branch 2 taken 295 times.
✓ Branch 3 taken 295 times.
|
1204 | , mMasks() |
| 233 | { | ||
| 234 | mBuffers.fill(nullptr); | ||
| 235 | mMasks.fill(nullptr); | ||
| 236 | 301 | } | |
| 237 | |||
| 238 | 14 | inline TreeType<0>& topology() { return *(std::get<0>(mTreeArray)); } | |
| 239 | |||
| 240 | inline void initialize(const Coord& origin, const size_t, const CoordBBox&); | ||
| 241 | |||
| 242 | template <size_t Idx> | ||
| 243 | inline ValueType<Idx>* buffer() | ||
| 244 | { | ||
| 245 | 3448 | return static_cast<ValueType<Idx>*>(mBuffers[Idx]); | |
| 246 | } | ||
| 247 | |||
| 248 | template <size_t Idx> | ||
| 249 | inline const ValueType<Idx>* buffer() const | ||
| 250 | { | ||
| 251 | return static_cast<ValueType<Idx>*>(mBuffers[Idx]); | ||
| 252 | } | ||
| 253 | |||
| 254 | template <size_t Idx> | ||
| 255 |
2/8✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 192 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 20 taken 192 times.
✗ Branch 21 not taken.
|
4945 | inline NodeMaskT* mask() { return mMasks[Idx]; } |
| 256 | inline NodeMaskT* mask(const size_t idx) { return mMasks[idx]; } | ||
| 257 | |||
| 258 | template <size_t Idx> | ||
| 259 | inline const NodeMaskT* mask() const { return mMasks[Idx]; } | ||
| 260 | inline const NodeMaskT* mask(const size_t idx) const { return mMasks[idx]; } | ||
| 261 | |||
| 262 | template <typename FunctorT> | ||
| 263 | inline void foreach(const FunctorT& functor); | ||
| 264 | |||
| 265 | private: | ||
| 266 | const TreeTupleT mTreeArray; | ||
| 267 | std::array<void*, Size> mBuffers; | ||
| 268 | std::array<NodeMaskT*, Size> mMasks; | ||
| 269 | }; | ||
| 270 | |||
| 271 | /// @brief VolumeTransfer specialization for a single target volume | ||
| 272 | /// @todo this specialization should avoid the probe | ||
| 273 | template <typename TreeT> | ||
| 274 | struct VolumeTransfer<TreeT> | ||
| 275 | { | ||
| 276 | using TreeType = TreeT; | ||
| 277 | using ValueType = typename TreeType::ValueType; | ||
| 278 | using NodeMaskT = typename TreeType::LeafNodeType::NodeMaskType; | ||
| 279 | |||
| 280 | static_assert(std::is_base_of<TreeBase, TreeType>::value, | ||
| 281 | "One or more template arguments to VolumeTransfer " | ||
| 282 | "are not a valid openvdb::Tree type."); | ||
| 283 | |||
| 284 | 48 | VolumeTransfer(TreeType* tree) | |
| 285 | : mTree(tree) | ||
| 286 | , mBuffer(nullptr) | ||
| 287 |
13/70✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 10 taken 9 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 16 taken 8 times.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 22 taken 17 times.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✓ Branch 46 taken 1 times.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✓ Branch 58 taken 1 times.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✓ Branch 70 taken 1 times.
✗ Branch 71 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✓ Branch 82 taken 1 times.
✗ Branch 83 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✓ Branch 94 taken 1 times.
✗ Branch 95 not taken.
✓ Branch 97 taken 1 times.
✗ Branch 98 not taken.
✓ Branch 100 taken 1 times.
✗ Branch 101 not taken.
✓ Branch 103 taken 1 times.
✗ Branch 104 not taken.
|
48 | , mMask(nullptr) { |
| 288 | assert(tree); | ||
| 289 | } | ||
| 290 | |||
| 291 | VolumeTransfer(TreeType& tree) | ||
| 292 | : VolumeTransfer(&tree) {} | ||
| 293 | |||
| 294 | 55 | VolumeTransfer(const VolumeTransfer& other) | |
| 295 | 55 | : mTree(other.mTree) | |
| 296 | , mBuffer(nullptr) | ||
| 297 | 55 | , mMask(nullptr) {} | |
| 298 | |||
| 299 | 48 | inline TreeType& topology() { return *mTree; } | |
| 300 | |||
| 301 | 1755 | inline void initialize(const Coord& origin, const size_t, const CoordBBox&) | |
| 302 | { | ||
| 303 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1695 times.
|
1755 | assert(mTree); |
| 304 |
1/2✓ Branch 0 taken 1695 times.
✗ Branch 1 not taken.
|
1755 | if (auto leaf = mTree->probeLeaf(origin)) { |
| 305 | 1755 | mBuffer = leaf->buffer().data(); | |
| 306 | 1755 | mMask = &(leaf->getValueMask()); | |
| 307 | } | ||
| 308 | else { | ||
| 309 | ✗ | mBuffer = nullptr; | |
| 310 | ✗ | mMask = nullptr; | |
| 311 | } | ||
| 312 | 1755 | } | |
| 313 | |||
| 314 | 5591 | inline ValueType* buffer() { return mBuffer; } | |
| 315 | inline const ValueType* buffer() const { return mBuffer; } | ||
| 316 |
4/8✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 10 taken 502 times.
✓ Branch 11 taken 280 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 20 taken 790 times.
✓ Branch 21 taken 880 times.
|
9196 | inline NodeMaskT* mask() { return mMask; } |
| 317 | inline const NodeMaskT* mask() const { return mMask; } | ||
| 318 | |||
| 319 | // compatibility with multi tree containers | ||
| 320 | template <size_t> inline ValueType* buffer() { return this->buffer(); } | ||
| 321 | template <size_t> inline const ValueType* buffer() const { return this->buffer(); } | ||
| 322 | template <size_t> inline NodeMaskT* mask() { return this->mask(); } | ||
| 323 | template <size_t> inline const NodeMaskT* mask() const { return this->mask(); } | ||
| 324 | |||
| 325 | private: | ||
| 326 | TreeType* const mTree; | ||
| 327 | ValueType* mBuffer; | ||
| 328 | NodeMaskT* mMask; | ||
| 329 | }; | ||
| 330 | |||
| 331 | namespace transfer_internal | ||
| 332 | { | ||
| 333 | template<typename T, typename F, size_t... Is> | ||
| 334 | 1568 | void foreach(T&& t, const F& func, std::integer_sequence<size_t, Is...>) | |
| 335 | { | ||
| 336 | 1568 | auto init = { (func(std::get<Is>(t), Is), 0)... }; | |
| 337 | (void)init; | ||
| 338 | 1568 | } | |
| 339 | |||
| 340 | template<typename T, typename F, size_t... Is> | ||
| 341 | void foreach(void** buffers, const F& func, std::integer_sequence<size_t, Is...>) | ||
| 342 | { | ||
| 343 | int init[sizeof...(Is)] = { | ||
| 344 | (func(static_cast<typename std::tuple_element<Is, T>::type*> | ||
| 345 | (*(buffers + Is)), Is), 0)... | ||
| 346 | }; | ||
| 347 | } | ||
| 348 | |||
| 349 | template<typename T, template <typename> class R, typename F, size_t... Is> | ||
| 350 | void foreach(void** buffers, const F& func, std::integer_sequence<size_t, Is...>) | ||
| 351 | { | ||
| 352 | int init[sizeof...(Is)] = { | ||
| 353 | (func(static_cast<typename R<typename std::tuple_element<Is, T>::type>::Type*> | ||
| 354 | (*(buffers + Is)), Is), 0)... | ||
| 355 | }; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | template <typename ...TreeTypes> | ||
| 360 | 16 | VolumeTransfer<TreeTypes...>::VolumeTransfer(TreeTypes*... trees) | |
| 361 | : mTreeArray({ trees... }) | ||
| 362 | , mBuffers() | ||
| 363 |
4/4✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 14 times.
|
48 | , mMasks() |
| 364 | { | ||
| 365 | 16 | transfer_internal::foreach(mTreeArray, [](auto&& tree, const size_t) { | |
| 366 | using TreeT = typename std::remove_pointer<typename std::decay<decltype(tree)>::type>::type; | ||
| 367 | static_assert(std::is_base_of<TreeBase, TreeT>::value, | ||
| 368 | "One or more template arguments to VolumeTransfer " | ||
| 369 | "are not a valid openvdb::Tree type."); | ||
| 370 |
4/8✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
|
14 | assert(tree); |
| 371 | }, std::make_integer_sequence<size_t, Size>()); | ||
| 372 | |||
| 373 | mBuffers.fill(nullptr); | ||
| 374 | mMasks.fill(nullptr); | ||
| 375 | 16 | } | |
| 376 | |||
| 377 | template <typename ...TreeTypes> | ||
| 378 | inline void VolumeTransfer<TreeTypes...>::initialize(const Coord& origin, const size_t, const CoordBBox&) | ||
| 379 | { | ||
| 380 | 772 | transfer_internal::foreach(mTreeArray, | |
| 381 | 3082 | [&](auto&& tree, const size_t i) { | |
| 382 |
4/8✗ Branch 0 not taken.
✓ Branch 1 taken 769 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 769 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
|
1540 | assert(tree); |
| 383 |
4/8✓ Branch 0 taken 769 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 769 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1540 | if (auto leaf = tree->probeLeaf(origin)) { |
| 384 | 1540 | mBuffers[i] = static_cast<void*>(leaf->buffer().data()); | |
| 385 | 1540 | mMasks[i] = &(leaf->getValueMask()); | |
| 386 | } | ||
| 387 | else { | ||
| 388 | ✗ | mBuffers[i] = nullptr; | |
| 389 | ✗ | mMasks[i] = nullptr; | |
| 390 | } | ||
| 391 | }, std::make_integer_sequence<size_t, Size>()); | ||
| 392 | } | ||
| 393 | |||
| 394 | template <typename ...TreeTypes> | ||
| 395 | template <typename FunctorT> | ||
| 396 | inline void VolumeTransfer<TreeTypes...>::foreach(const FunctorT& functor) | ||
| 397 | { | ||
| 398 | transfer_internal::foreach<TreeTupleT, TypeResolver>(mBuffers.data(), functor, | ||
| 399 | std::make_integer_sequence<size_t, Size>()); | ||
| 400 | } | ||
| 401 | |||
| 402 | namespace transfer_internal | ||
| 403 | { | ||
| 404 | template <typename TransferT, | ||
| 405 | typename TopologyT, | ||
| 406 | typename PointFilterT = points::NullFilter, | ||
| 407 | typename InterrupterT = util::NullInterrupter> | ||
| 408 |
6/12✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
|
7 | struct RasterizePoints |
| 409 | { | ||
| 410 | using LeafManagerT = tree::LeafManager<TopologyT>; | ||
| 411 | using LeafNodeT = typename LeafManagerT::LeafNodeType; | ||
| 412 | |||
| 413 | static const Index DIM = TopologyT::LeafNodeType::DIM; | ||
| 414 | static const Int32 DIM32 = static_cast<Int32>(DIM); | ||
| 415 | static const Index LOG2DIM = TopologyT::LeafNodeType::LOG2DIM; | ||
| 416 | |||
| 417 |
6/16✓ 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 not taken.
✗ Branch 14 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 22 taken 3 times.
✗ Branch 23 not taken.
|
114 | RasterizePoints(const points::PointDataTree& tree, |
| 418 | const TransferT& transfer, | ||
| 419 | const PointFilterT& filter = PointFilterT(), | ||
| 420 | InterrupterT* interrupter = nullptr) | ||
| 421 | : mPointAccessor(tree) | ||
| 422 | , mTransfer(transfer) | ||
| 423 | , mFilter(filter) | ||
| 424 | 114 | , mInterrupter(interrupter) {} | |
| 425 | |||
| 426 | 4930 | void operator()(LeafNodeT& leaf, const size_t idx) const | |
| 427 | { | ||
| 428 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2465 times.
|
4930 | if (util::wasInterrupted(mInterrupter)) { |
| 429 | ✗ | thread::cancelGroupExecution(); | |
| 430 | ✗ | return; | |
| 431 | } | ||
| 432 | |||
| 433 | const Coord& origin = leaf.origin(); | ||
| 434 | auto& mask = leaf.getValueMask(); | ||
| 435 | |||
| 436 | 4930 | CoordBBox bounds; | |
| 437 | |||
| 438 | bool state; | ||
| 439 |
2/2✓ Branch 0 taken 416 times.
✓ Branch 1 taken 724 times.
|
2280 | if (mask.isConstant(state)) { |
| 440 |
1/2✓ Branch 0 taken 416 times.
✗ Branch 1 not taken.
|
832 | if (!state) return; // all inactive |
| 441 | 832 | else bounds = leaf.getNodeBoundingBox(); | |
| 442 | } | ||
| 443 | else { | ||
| 444 | // Use evalActiveBoundingBox over getNodeBoundingBox() | ||
| 445 | // to get a better approximation | ||
| 446 | 4098 | leaf.evalActiveBoundingBox(bounds); | |
| 447 | ✗ | assert(!bounds.empty()); | |
| 448 | } | ||
| 449 | |||
| 450 | 4898 | mTransfer.initialize(origin, idx, bounds); | |
| 451 | |||
| 452 | 4930 | CoordBBox search = bounds.expandBy(mTransfer.range(origin, idx)); | |
| 453 | 4790 | this->transform<>(search); | |
| 454 | |||
| 455 | // start the iteration from a leaf origin | ||
| 456 | const Coord min = (search.min() & ~(DIM-1)); | ||
| 457 | const Coord& max = search.max(); | ||
| 458 | 1020 | PointFilterT localFilter(mFilter); | |
| 459 | |||
| 460 | // loop over overlapping leaf nodes | ||
| 461 | Coord leafOrigin; | ||
| 462 |
2/2✓ Branch 0 taken 26386 times.
✓ Branch 1 taken 2465 times.
|
57702 | for (leafOrigin[0] = min[0]; leafOrigin[0] <= max[0]; leafOrigin[0]+=DIM32) { |
| 463 |
2/2✓ Branch 0 taken 1335361 times.
✓ Branch 1 taken 26386 times.
|
2723494 | for (leafOrigin[1] = min[1]; leafOrigin[1] <= max[1]; leafOrigin[1]+=DIM32) { |
| 464 |
2/2✓ Branch 0 taken 92466698 times.
✓ Branch 1 taken 1335361 times.
|
187604118 | for (leafOrigin[2] = min[2]; leafOrigin[2] <= max[2]; leafOrigin[2]+=DIM32) { |
| 465 | |||
| 466 | // if no overlap, continue | ||
| 467 | CoordBBox pbox = CoordBBox::createCube(leafOrigin, DIM32); | ||
| 468 | 184933396 | pbox.intersect(search); | |
| 469 | 184916854 | if (pbox.empty()) continue; | |
| 470 | |||
| 471 | // if no points, continue | ||
| 472 | 184933396 | const auto* pointLeaf = mPointAccessor.probeConstLeaf(leafOrigin); | |
| 473 |
2/2✓ Branch 0 taken 92458427 times.
✓ Branch 1 taken 8271 times.
|
184933396 | if (!pointLeaf) continue; |
| 474 |
3/4✓ Branch 1 taken 2287 times.
✓ Branch 2 taken 5977 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2287 times.
|
16528 | if (!mTransfer.startPointLeaf(*pointLeaf)) continue; |
| 475 |
1/2✓ Branch 1 taken 2288 times.
✗ Branch 2 not taken.
|
4576 | localFilter.reset(*pointLeaf); |
| 476 | |||
| 477 | // loop over point voxels which contribute to this leaf | ||
| 478 | const Coord& pmin(pbox.min()); | ||
| 479 | const Coord& pmax(pbox.max()); | ||
| 480 |
2/2✓ Branch 0 taken 56017 times.
✓ Branch 1 taken 8271 times.
|
128576 | for (Coord ijk = pmin; ijk.x() <= pmax.x(); ++ijk.x()) { |
| 481 | 112034 | const Index i = ((ijk.x() & (DIM-1u)) << 2*LOG2DIM); // unsigned bit shift mult | |
| 482 |
2/2✓ Branch 0 taken 386198 times.
✓ Branch 1 taken 56017 times.
|
884430 | for (ijk.y() = pmin.y(); ijk.y() <= pmax.y(); ++ijk.y()) { |
| 483 | 772396 | const Index ij = i + ((ijk.y() & (DIM-1u)) << LOG2DIM); | |
| 484 |
2/2✓ Branch 0 taken 2708203 times.
✓ Branch 1 taken 386198 times.
|
6188802 | for (ijk.z() = pmin.z(); ijk.z() <= pmax.z(); ++ijk.z()) { |
| 485 | // voxel should be in this points leaf | ||
| 486 | ✗ | assert((ijk & ~(DIM-1u)) == leafOrigin); | |
| 487 | 5416406 | const Index index = ij + /*k*/(ijk.z() & (DIM-1u)); | |
| 488 |
1/2✓ Branch 1 taken 2708203 times.
✗ Branch 2 not taken.
|
5416406 | const Index end = pointLeaf->getValue(index); |
| 489 |
3/4✓ Branch 0 taken 2703106 times.
✓ Branch 1 taken 5097 times.
✓ Branch 3 taken 2703106 times.
✗ Branch 4 not taken.
|
6984372 | Index id = (index == 0) ? 0 : Index(pointLeaf->getValue(index - 1)); |
| 490 |
2/2✓ Branch 0 taken 8233 times.
✓ Branch 1 taken 2708203 times.
|
5432872 | for (; id < end; ++id) { |
| 491 |
3/4✓ Branch 1 taken 2291 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1739 times.
✓ Branch 4 taken 552 times.
|
4582 | if (!localFilter.valid(&id)) continue; |
| 492 |
1/2✓ Branch 1 taken 6417 times.
✗ Branch 2 not taken.
|
12974 | mTransfer.rasterizePoint(ijk, id, bounds); |
| 493 | } //point idx | ||
| 494 | } | ||
| 495 | } | ||
| 496 | } // outer point voxel | ||
| 497 | |||
| 498 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4232 times.
|
8464 | if (!mTransfer.endPointLeaf(*pointLeaf)) { |
| 499 | // rescurse if necessary | ||
| 500 | ✗ | if (!mTransfer.finalize(origin, idx)) { | |
| 501 | ✗ | this->operator()(leaf, idx); | |
| 502 | } | ||
| 503 | ✗ | return; | |
| 504 | } | ||
| 505 | } | ||
| 506 | } | ||
| 507 | } // outer leaf node | ||
| 508 | |||
| 509 | // rescurse if necessary | ||
| 510 |
3/4✓ Branch 1 taken 317 times.
✓ Branch 2 taken 2143 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 317 times.
|
4920 | if (!mTransfer.finalize(origin, idx)) { |
| 511 | ✗ | this->operator()(leaf, idx); | |
| 512 | } | ||
| 513 | } | ||
| 514 | |||
| 515 | void operator()(const typename LeafManagerT::LeafRange& range) const | ||
| 516 | { | ||
| 517 | for (auto leaf = range.begin(); leaf; ++leaf) { | ||
| 518 | (*this)(*leaf, leaf.pos()); | ||
| 519 | } | ||
| 520 | } | ||
| 521 | |||
| 522 | private: | ||
| 523 | |||
| 524 | template <typename EnableT = TransferT> | ||
| 525 | typename std::enable_if<std::is_base_of<TransformTransfer, EnableT>::value>::type | ||
| 526 | 4790 | transform(CoordBBox& bounds) const | |
| 527 | { | ||
| 528 | const TransformTransfer* transform = | ||
| 529 | static_cast<TransformTransfer*>(&mTransfer); | ||
| 530 | 4790 | const BBoxd bbox(bounds.min().asVec3d(), bounds.max().asVec3d()); | |
| 531 | 4790 | bounds = transform->sourceTransform().worldToIndexCellCentered( | |
| 532 | 4790 | transform->targetTransform().indexToWorld(bbox)); | |
| 533 | } | ||
| 534 | |||
| 535 | template <typename EnableT = TransferT> | ||
| 536 | typename std::enable_if<!std::is_base_of<TransformTransfer, EnableT>::value>::type | ||
| 537 | transform(CoordBBox&) const {} | ||
| 538 | |||
| 539 | private: | ||
| 540 | const PointDataGrid::ConstAccessor mPointAccessor; | ||
| 541 | mutable TransferT mTransfer; | ||
| 542 | const PointFilterT& mFilter; | ||
| 543 | InterrupterT* mInterrupter; | ||
| 544 | }; | ||
| 545 | |||
| 546 | } // namespace transfer_internal | ||
| 547 | |||
| 548 | /////////////////////////////////////////////////// | ||
| 549 | /////////////////////////////////////////////////// | ||
| 550 | |||
| 551 | template <typename PointDataTreeOrGridT, | ||
| 552 | typename TransferT, | ||
| 553 | typename FilterT, | ||
| 554 | typename InterrupterT> | ||
| 555 | inline void | ||
| 556 | 124 | rasterize(const PointDataTreeOrGridT& points, | |
| 557 | TransferT& transfer, | ||
| 558 | const FilterT& filter, | ||
| 559 | InterrupterT* interrupter) | ||
| 560 | { | ||
| 561 | using PointTreeT = typename TreeAdapter<PointDataTreeOrGridT>::TreeType; | ||
| 562 | static_assert(std::is_base_of<TreeBase, PointTreeT>::value, | ||
| 563 | "Provided points to rasterize is not a derived TreeBase type."); | ||
| 564 | |||
| 565 | const auto& tree = TreeAdapter<PointDataTreeOrGridT>::tree(points); | ||
| 566 | |||
| 567 | auto& topology = transfer.topology(); | ||
| 568 | using TreeT = typename std::decay<decltype(topology)>::type; | ||
| 569 | 248 | tree::LeafManager<TreeT> manager(topology); | |
| 570 | transfer_internal::RasterizePoints<TransferT, TreeT, FilterT, InterrupterT> | ||
| 571 |
1/2✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
|
220 | raster(tree, transfer, filter, interrupter); |
| 572 |
1/2✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
|
124 | manager.foreach(raster); |
| 573 | } | ||
| 574 | |||
| 575 | } // namespace points | ||
| 576 | } // namespace OPENVDB_VERSION_NAME | ||
| 577 | } // namespace openvdb | ||
| 578 | |||
| 579 | #endif //OPENVEB_POINTS_TRANSFER_HAS_BEEN_INCLUDED | ||
| 580 |