| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | /// @author Dan Bailey, Nick Avramoussis | ||
| 5 | /// | ||
| 6 | /// @file points/PointConversion.h | ||
| 7 | /// | ||
| 8 | /// @brief Convert points and attributes to and from VDB Point Data grids. | ||
| 9 | |||
| 10 | #ifndef OPENVDB_POINTS_POINT_CONVERSION_HAS_BEEN_INCLUDED | ||
| 11 | #define OPENVDB_POINTS_POINT_CONVERSION_HAS_BEEN_INCLUDED | ||
| 12 | |||
| 13 | #include <openvdb/math/Transform.h> | ||
| 14 | |||
| 15 | #include <openvdb/tools/PointIndexGrid.h> | ||
| 16 | #include <openvdb/tools/PointsToMask.h> | ||
| 17 | #include <openvdb/util/NullInterrupter.h> | ||
| 18 | |||
| 19 | #include "AttributeArrayString.h" | ||
| 20 | #include "AttributeSet.h" | ||
| 21 | #include "IndexFilter.h" | ||
| 22 | #include "PointAttribute.h" | ||
| 23 | #include "PointDataGrid.h" | ||
| 24 | #include "PointGroup.h" | ||
| 25 | |||
| 26 | #include <tbb/parallel_reduce.h> | ||
| 27 | |||
| 28 | #include <type_traits> | ||
| 29 | |||
| 30 | namespace openvdb { | ||
| 31 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 32 | namespace OPENVDB_VERSION_NAME { | ||
| 33 | namespace points { | ||
| 34 | |||
| 35 | |||
| 36 | /// @brief Localises points with position into a @c PointDataGrid into two stages: | ||
| 37 | /// allocation of the leaf attribute data and population of the positions. | ||
| 38 | /// | ||
| 39 | /// @param pointIndexGrid a PointIndexGrid into the points. | ||
| 40 | /// @param positions list of world space point positions. | ||
| 41 | /// @param xform world to index space transform. | ||
| 42 | /// @param positionDefaultValue metadata default position value | ||
| 43 | /// | ||
| 44 | /// @note The position data must be supplied in a Point-Partitioner compatible | ||
| 45 | /// data structure. A convenience PointAttributeVector class is offered. | ||
| 46 | /// | ||
| 47 | /// @note The position data is populated separately to perform world space to | ||
| 48 | /// voxel space conversion and apply quantisation. | ||
| 49 | /// | ||
| 50 | /// @note A @c PointIndexGrid to the points must be supplied to perform this | ||
| 51 | /// operation. Typically this is built implicitly by the PointDataGrid constructor. | ||
| 52 | |||
| 53 | template< | ||
| 54 | typename CompressionT, | ||
| 55 | typename PointDataGridT, | ||
| 56 | typename PositionArrayT, | ||
| 57 | typename PointIndexGridT> | ||
| 58 | inline typename PointDataGridT::Ptr | ||
| 59 | createPointDataGrid(const PointIndexGridT& pointIndexGrid, | ||
| 60 | const PositionArrayT& positions, | ||
| 61 | const math::Transform& xform, | ||
| 62 | const Metadata* positionDefaultValue = nullptr); | ||
| 63 | |||
| 64 | |||
| 65 | /// @brief Convenience method to create a @c PointDataGrid from a std::vector of | ||
| 66 | /// point positions. | ||
| 67 | /// | ||
| 68 | /// @param positions list of world space point positions. | ||
| 69 | /// @param xform world to index space transform. | ||
| 70 | /// @param positionDefaultValue metadata default position value | ||
| 71 | /// | ||
| 72 | /// @note This method implicitly wraps the std::vector for a Point-Partitioner compatible | ||
| 73 | /// data structure and creates the required @c PointIndexGrid to the points. | ||
| 74 | |||
| 75 | template <typename CompressionT, typename PointDataGridT, typename ValueT> | ||
| 76 | inline typename PointDataGridT::Ptr | ||
| 77 | createPointDataGrid(const std::vector<ValueT>& positions, | ||
| 78 | const math::Transform& xform, | ||
| 79 | const Metadata* positionDefaultValue = nullptr); | ||
| 80 | |||
| 81 | |||
| 82 | /// @brief Stores point attribute data in an existing @c PointDataGrid attribute. | ||
| 83 | /// | ||
| 84 | /// @param tree the PointDataGrid to be populated. | ||
| 85 | /// @param pointIndexTree a PointIndexTree into the points. | ||
| 86 | /// @param attributeName the name of the VDB Points attribute to be populated. | ||
| 87 | /// @param data a wrapper to the attribute data. | ||
| 88 | /// @param stride the stride of the attribute | ||
| 89 | /// @param insertMetadata true if strings are to be automatically inserted as metadata. | ||
| 90 | /// | ||
| 91 | /// @note A @c PointIndexGrid to the points must be supplied to perform this | ||
| 92 | /// operation. This is required to ensure the same point index ordering. | ||
| 93 | template <typename PointDataTreeT, typename PointIndexTreeT, typename PointArrayT> | ||
| 94 | inline void | ||
| 95 | populateAttribute( PointDataTreeT& tree, | ||
| 96 | const PointIndexTreeT& pointIndexTree, | ||
| 97 | const openvdb::Name& attributeName, | ||
| 98 | const PointArrayT& data, | ||
| 99 | const Index stride = 1, | ||
| 100 | const bool insertMetadata = true); | ||
| 101 | |||
| 102 | /// @brief Convert the position attribute from a Point Data Grid | ||
| 103 | /// | ||
| 104 | /// @param positionAttribute the position attribute to be populated. | ||
| 105 | /// @param grid the PointDataGrid to be converted. | ||
| 106 | /// @param pointOffsets a vector of cumulative point offsets for each leaf | ||
| 107 | /// @param startOffset a value to shift all the point offsets by | ||
| 108 | /// @param filter an index filter | ||
| 109 | /// @param inCoreOnly true if out-of-core leaf nodes are to be ignored | ||
| 110 | /// | ||
| 111 | |||
| 112 | template <typename PositionAttribute, typename PointDataGridT, typename FilterT = NullFilter> | ||
| 113 | inline void | ||
| 114 | convertPointDataGridPosition( PositionAttribute& positionAttribute, | ||
| 115 | const PointDataGridT& grid, | ||
| 116 | const std::vector<Index64>& pointOffsets, | ||
| 117 | const Index64 startOffset, | ||
| 118 | const FilterT& filter = NullFilter(), | ||
| 119 | const bool inCoreOnly = false); | ||
| 120 | |||
| 121 | |||
| 122 | /// @brief Convert the attribute from a PointDataGrid | ||
| 123 | /// | ||
| 124 | /// @param attribute the attribute to be populated. | ||
| 125 | /// @param tree the PointDataTree to be converted. | ||
| 126 | /// @param pointOffsets a vector of cumulative point offsets for each leaf. | ||
| 127 | /// @param startOffset a value to shift all the point offsets by | ||
| 128 | /// @param arrayIndex the index in the Descriptor of the array to be converted. | ||
| 129 | /// @param stride the stride of the attribute | ||
| 130 | /// @param filter an index filter | ||
| 131 | /// @param inCoreOnly true if out-of-core leaf nodes are to be ignored | ||
| 132 | template <typename TypedAttribute, typename PointDataTreeT, typename FilterT = NullFilter> | ||
| 133 | inline void | ||
| 134 | convertPointDataGridAttribute( TypedAttribute& attribute, | ||
| 135 | const PointDataTreeT& tree, | ||
| 136 | const std::vector<Index64>& pointOffsets, | ||
| 137 | const Index64 startOffset, | ||
| 138 | const unsigned arrayIndex, | ||
| 139 | const Index stride = 1, | ||
| 140 | const FilterT& filter = NullFilter(), | ||
| 141 | const bool inCoreOnly = false); | ||
| 142 | |||
| 143 | |||
| 144 | /// @brief Convert the group from a PointDataGrid | ||
| 145 | /// | ||
| 146 | /// @param group the group to be populated. | ||
| 147 | /// @param tree the PointDataTree to be converted. | ||
| 148 | /// @param pointOffsets a vector of cumulative point offsets for each leaf | ||
| 149 | /// @param startOffset a value to shift all the point offsets by | ||
| 150 | /// @param index the group index to be converted. | ||
| 151 | /// @param filter an index filter | ||
| 152 | /// @param inCoreOnly true if out-of-core leaf nodes are to be ignored | ||
| 153 | /// | ||
| 154 | |||
| 155 | template <typename Group, typename PointDataTreeT, typename FilterT = NullFilter> | ||
| 156 | inline void | ||
| 157 | convertPointDataGridGroup( Group& group, | ||
| 158 | const PointDataTreeT& tree, | ||
| 159 | const std::vector<Index64>& pointOffsets, | ||
| 160 | const Index64 startOffset, | ||
| 161 | const AttributeSet::Descriptor::GroupIndex index, | ||
| 162 | const FilterT& filter = NullFilter(), | ||
| 163 | const bool inCoreOnly = false); | ||
| 164 | |||
| 165 | // for internal use only - this traits class extracts T::value_type if defined, | ||
| 166 | // otherwise falls back to using Vec3R | ||
| 167 | namespace internal { | ||
| 168 | template <typename...> using void_t = void; | ||
| 169 | template <typename T, typename = void> | ||
| 170 | struct ValueTypeTraits { using Type = Vec3R; /* default type if T::value_type is not defined*/ }; | ||
| 171 | template <typename T> | ||
| 172 | struct ValueTypeTraits <T, void_t<typename T::value_type>> { using Type = typename T::value_type; }; | ||
| 173 | } // namespace internal | ||
| 174 | |||
| 175 | /// @ brief Given a container of world space positions and a target points per voxel, | ||
| 176 | /// compute a uniform voxel size that would best represent the storage of the points in a grid. | ||
| 177 | /// This voxel size is typically used for conversion of the points into a PointDataGrid. | ||
| 178 | /// | ||
| 179 | /// @param positions array of world space positions | ||
| 180 | /// @param pointsPerVoxel the target number of points per voxel, must be positive and non-zero | ||
| 181 | /// @param transform voxel size will be computed using this optional transform if provided | ||
| 182 | /// @param decimalPlaces for readability, truncate voxel size to this number of decimals | ||
| 183 | /// @param interrupter an optional interrupter | ||
| 184 | /// | ||
| 185 | /// @note VecT will be PositionWrapper::value_type or Vec3R (if there is no value_type defined) | ||
| 186 | /// | ||
| 187 | /// @note if none or one point provided in positions, the default voxel size of 0.1 will be returned | ||
| 188 | /// | ||
| 189 | template< typename PositionWrapper, | ||
| 190 | typename InterrupterT = openvdb::util::NullInterrupter, | ||
| 191 | typename VecT = typename internal::ValueTypeTraits<PositionWrapper>::Type> | ||
| 192 | inline float | ||
| 193 | computeVoxelSize( const PositionWrapper& positions, | ||
| 194 | const uint32_t pointsPerVoxel, | ||
| 195 | const math::Mat4d transform = math::Mat4d::identity(), | ||
| 196 | const Index decimalPlaces = 5, | ||
| 197 | InterrupterT* const interrupter = nullptr); | ||
| 198 | |||
| 199 | |||
| 200 | //////////////////////////////////////// | ||
| 201 | |||
| 202 | |||
| 203 | /// @brief Point-partitioner compatible STL vector attribute wrapper for convenience | ||
| 204 | template<typename ValueType> | ||
| 205 | class PointAttributeVector { | ||
| 206 | public: | ||
| 207 | using PosType = ValueType; | ||
| 208 | using value_type= ValueType; | ||
| 209 | |||
| 210 | 752 | PointAttributeVector(const std::vector<value_type>& data, | |
| 211 | const Index stride = 1) | ||
| 212 | : mData(data) | ||
| 213 |
28/54✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 55 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 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.
✓ Branch 28 taken 1 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 1 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 1 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 1 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 1 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 1 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 1 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 1 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 1 times.
✗ Branch 59 not taken.
✓ Branch 61 taken 1 times.
✗ Branch 62 not taken.
✓ Branch 64 taken 1 times.
✗ Branch 65 not taken.
✓ Branch 67 taken 1 times.
✗ Branch 68 not taken.
✓ Branch 70 taken 1 times.
✗ Branch 71 not taken.
✓ Branch 73 taken 1 times.
✗ Branch 74 not taken.
✓ Branch 76 taken 1 times.
✗ Branch 77 not taken.
|
748 | , mStride(stride) { } |
| 214 | |||
| 215 |
12/25✓ Branch 0 taken 658 times.
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 703 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 703 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 699 times.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
|
3984 | size_t size() const { return mData.size(); } |
| 216 |
5/14✓ Branch 0 taken 5092 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 90004 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
|
95105 | void getPos(size_t n, ValueType& xyz) const { xyz = mData[n]; } |
| 217 | void get(ValueType& value, size_t n) const { value = mData[n]; } | ||
| 218 | 259 | void get(ValueType& value, size_t n, openvdb::Index m) const { value = mData[n * mStride + m]; } | |
| 219 | |||
| 220 | private: | ||
| 221 | const std::vector<value_type>& mData; | ||
| 222 | const Index mStride; | ||
| 223 | }; // PointAttributeVector | ||
| 224 | |||
| 225 | |||
| 226 | //////////////////////////////////////// | ||
| 227 | |||
| 228 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 229 | |||
| 230 | namespace point_conversion_internal { | ||
| 231 | |||
| 232 | |||
| 233 | // ConversionTraits to create the relevant Attribute Handles from a LeafNode | ||
| 234 | template <typename T> struct ConversionTraits | ||
| 235 | { | ||
| 236 | using Handle = AttributeHandle<T, UnknownCodec>; | ||
| 237 | using WriteHandle = AttributeWriteHandle<T, UnknownCodec>; | ||
| 238 | static T zero() { return zeroVal<T>(); } | ||
| 239 | template <typename LeafT> | ||
| 240 | static std::unique_ptr<Handle> handleFromLeaf(const LeafT& leaf, const Index index) { | ||
| 241 | const AttributeArray& array = leaf.constAttributeArray(index); | ||
| 242 |
4/40✓ Branch 1 taken 5800 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5800 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5407 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 7 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ 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 not taken.
✗ 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 not taken.
✗ 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 not taken.
✗ Branch 59 not taken.
|
17014 | return std::make_unique<Handle>(array); |
| 243 | } | ||
| 244 | template <typename LeafT> | ||
| 245 | static std::unique_ptr<WriteHandle> writeHandleFromLeaf(LeafT& leaf, const Index index) { | ||
| 246 |
19/40✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 4 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 4 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 4 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 8 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 20 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 16 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 4 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 4 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 4 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 8 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 32 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 4 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 44 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 4 times.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✓ Branch 58 taken 12 times.
✗ Branch 59 not taken.
|
11566 | AttributeArray& array = leaf.attributeArray(index); |
| 247 |
19/40✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 4 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 4 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 4 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 8 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 20 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 16 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 4 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 4 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 4 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 8 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 32 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 4 times.
✗ Branch 47 not taken.
✓ Branch 49 taken 44 times.
✗ Branch 50 not taken.
✓ Branch 52 taken 4 times.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✓ Branch 58 taken 12 times.
✗ Branch 59 not taken.
|
11566 | return std::make_unique<WriteHandle>(array); |
| 248 | } | ||
| 249 | }; // ConversionTraits | ||
| 250 | template <> struct ConversionTraits<openvdb::Name> | ||
| 251 | { | ||
| 252 | using Handle = StringAttributeHandle; | ||
| 253 | using WriteHandle = StringAttributeWriteHandle; | ||
| 254 | static openvdb::Name zero() { return ""; } | ||
| 255 | template <typename LeafT> | ||
| 256 | 5819 | static std::unique_ptr<Handle> handleFromLeaf(const LeafT& leaf, const Index index) { | |
| 257 | 5819 | const AttributeArray& array = leaf.constAttributeArray(index); | |
| 258 | const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor(); | ||
| 259 | 5819 | return std::make_unique<Handle>(array, descriptor.getMetadata()); | |
| 260 | } | ||
| 261 | template <typename LeafT> | ||
| 262 | 3075 | static std::unique_ptr<WriteHandle> writeHandleFromLeaf(LeafT& leaf, const Index index) { | |
| 263 | 3075 | AttributeArray& array = leaf.attributeArray(index); | |
| 264 | const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor(); | ||
| 265 | 3075 | return std::make_unique<WriteHandle>(array, descriptor.getMetadata()); | |
| 266 | } | ||
| 267 | }; // ConversionTraits<openvdb::Name> | ||
| 268 | |||
| 269 | template< typename PointDataTreeType, | ||
| 270 | typename PointIndexTreeType, | ||
| 271 | typename AttributeListType> | ||
| 272 | struct PopulateAttributeOp { | ||
| 273 | |||
| 274 | using LeafManagerT = typename tree::LeafManager<PointDataTreeType>; | ||
| 275 | using LeafRangeT = typename LeafManagerT::LeafRange; | ||
| 276 | using PointIndexLeafNode = typename PointIndexTreeType::LeafNodeType; | ||
| 277 | using IndexArray = typename PointIndexLeafNode::IndexArray; | ||
| 278 | using ValueType = typename AttributeListType::value_type; | ||
| 279 | using HandleT = typename ConversionTraits<ValueType>::WriteHandle; | ||
| 280 | |||
| 281 | 47 | PopulateAttributeOp(const PointIndexTreeType& pointIndexTree, | |
| 282 | const AttributeListType& data, | ||
| 283 | const size_t index, | ||
| 284 | const Index stride = 1) | ||
| 285 | : mPointIndexTree(pointIndexTree) | ||
| 286 | , mData(data) | ||
| 287 | , mIndex(index) | ||
| 288 |
3/6✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
|
47 | , mStride(stride) { } |
| 289 | |||
| 290 | 1700 | void operator()(const typename LeafManagerT::LeafRange& range) const { | |
| 291 | |||
| 292 |
2/2✓ Branch 1 taken 14269 times.
✓ Branch 2 taken 873 times.
|
30192 | for (auto leaf = range.begin(); leaf; ++leaf) { |
| 293 | |||
| 294 | // obtain the PointIndexLeafNode (using the origin of the current leaf) | ||
| 295 | |||
| 296 | const PointIndexLeafNode* pointIndexLeaf = | ||
| 297 | 28492 | mPointIndexTree.probeConstLeaf(leaf->origin()); | |
| 298 | |||
| 299 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14269 times.
|
28492 | if (!pointIndexLeaf) continue; |
| 300 | |||
| 301 | 11628 | auto attributeWriteHandle = | |
| 302 | 28492 | ConversionTraits<ValueType>::writeHandleFromLeaf(*leaf, static_cast<Index>(mIndex)); | |
| 303 | |||
| 304 | Index64 index = 0; | ||
| 305 | |||
| 306 | const IndexArray& indices = pointIndexLeaf->indices(); | ||
| 307 | |||
| 308 |
2/2✓ Branch 0 taken 3080312 times.
✓ Branch 1 taken 14269 times.
|
6189049 | for (const Index64 leafIndex: indices) |
| 309 | { | ||
| 310 | ValueType value; | ||
| 311 |
2/2✓ Branch 0 taken 3160322 times.
✓ Branch 1 taken 3080312 times.
|
12481134 | for (Index i = 0; i < mStride; i++) { |
| 312 |
1/2✓ Branch 1 taken 3160322 times.
✗ Branch 2 not taken.
|
6320577 | mData.get(value, leafIndex, i); |
| 313 |
1/2✓ Branch 1 taken 3160322 times.
✗ Branch 2 not taken.
|
6320577 | attributeWriteHandle->set(static_cast<Index>(index), i, value); |
| 314 | } | ||
| 315 | 6160557 | index++; | |
| 316 | } | ||
| 317 | |||
| 318 | // attempt to compact the array | ||
| 319 | |||
| 320 |
1/2✓ Branch 1 taken 14269 times.
✗ Branch 2 not taken.
|
28492 | attributeWriteHandle->compact(); |
| 321 | } | ||
| 322 | 1700 | } | |
| 323 | |||
| 324 | ////////// | ||
| 325 | |||
| 326 | const PointIndexTreeType& mPointIndexTree; | ||
| 327 | const AttributeListType& mData; | ||
| 328 | const size_t mIndex; | ||
| 329 | const Index mStride; | ||
| 330 | }; | ||
| 331 | |||
| 332 | template<typename PointDataTreeType, typename Attribute, typename FilterT> | ||
| 333 | struct ConvertPointDataGridPositionOp { | ||
| 334 | |||
| 335 | using LeafNode = typename PointDataTreeType::LeafNodeType; | ||
| 336 | using ValueType = typename Attribute::ValueType; | ||
| 337 | using HandleT = typename Attribute::Handle; | ||
| 338 | using SourceHandleT = AttributeHandle<ValueType>; | ||
| 339 | using LeafManagerT = typename tree::LeafManager<const PointDataTreeType>; | ||
| 340 | using LeafRangeT = typename LeafManagerT::LeafRange; | ||
| 341 | |||
| 342 | 4 | ConvertPointDataGridPositionOp( Attribute& attribute, | |
| 343 | const std::vector<Index64>& pointOffsets, | ||
| 344 | const Index64 startOffset, | ||
| 345 | const math::Transform& transform, | ||
| 346 | const size_t index, | ||
| 347 | const FilterT& filter, | ||
| 348 | const bool inCoreOnly) | ||
| 349 | : mAttribute(attribute) | ||
| 350 | , mPointOffsets(pointOffsets) | ||
| 351 | , mStartOffset(startOffset) | ||
| 352 | , mTransform(transform) | ||
| 353 | , mIndex(index) | ||
| 354 | , mFilter(filter) | ||
| 355 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
4 | , mInCoreOnly(inCoreOnly) |
| 356 | { | ||
| 357 | // only accept Vec3f as ValueType | ||
| 358 | static_assert(VecTraits<ValueType>::Size == 3 && | ||
| 359 | std::is_floating_point<typename ValueType::ValueType>::value, | ||
| 360 | "ValueType is not Vec3f"); | ||
| 361 | } | ||
| 362 | |||
| 363 | template <typename IterT> | ||
| 364 | 17014 | void convert(IterT& iter, HandleT& targetHandle, | |
| 365 | SourceHandleT& sourceHandle, Index64& offset) const | ||
| 366 | { | ||
| 367 |
2/2✓ Branch 0 taken 1540021 times.
✓ Branch 1 taken 8507 times.
|
3097056 | for (; iter; ++iter) { |
| 368 | 3080042 | const Vec3d xyz = iter.getCoord().asVec3d(); | |
| 369 | 3080042 | const Vec3d pos = sourceHandle.get(*iter); | |
| 370 | 3080042 | targetHandle.set(static_cast<Index>(offset++), /*stride=*/0, | |
| 371 | 3080042 | mTransform.indexToWorld(pos + xyz)); | |
| 372 | } | ||
| 373 | 17014 | } | |
| 374 | |||
| 375 | 882 | void operator()(const LeafRangeT& range) const | |
| 376 | { | ||
| 377 | 882 | HandleT pHandle(mAttribute); | |
| 378 | |||
| 379 |
2/2✓ Branch 1 taken 8507 times.
✓ Branch 2 taken 441 times.
|
17896 | for (auto leaf = range.begin(); leaf; ++leaf) { |
| 380 | |||
| 381 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8507 times.
|
17014 | assert(leaf.pos() < mPointOffsets.size()); |
| 382 | |||
| 383 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 8507 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
17014 | if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue; |
| 384 | |||
| 385 |
2/2✓ Branch 0 taken 8503 times.
✓ Branch 1 taken 4 times.
|
17014 | Index64 offset = mStartOffset; |
| 386 | |||
| 387 |
2/2✓ Branch 0 taken 8503 times.
✓ Branch 1 taken 4 times.
|
17014 | if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1]; |
| 388 | |||
| 389 |
2/4✓ Branch 1 taken 8507 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8507 times.
✗ Branch 5 not taken.
|
17014 | auto handle = SourceHandleT::create(leaf->constAttributeArray(mIndex)); |
| 390 | |||
| 391 |
2/2✓ Branch 0 taken 2900 times.
✓ Branch 1 taken 2900 times.
|
11600 | if (mFilter.state() == index::ALL) { |
| 392 |
1/2✓ Branch 1 taken 5607 times.
✗ Branch 2 not taken.
|
11214 | auto iter = leaf->beginIndexOn(); |
| 393 |
1/2✓ Branch 1 taken 5607 times.
✗ Branch 2 not taken.
|
11214 | convert(iter, pHandle, *handle, offset); |
| 394 | } | ||
| 395 | else { | ||
| 396 | auto iter = leaf->beginIndexOn(mFilter); | ||
| 397 |
1/2✓ Branch 1 taken 2900 times.
✗ Branch 2 not taken.
|
5800 | convert(iter, pHandle, *handle, offset); |
| 398 | } | ||
| 399 | } | ||
| 400 | 882 | } | |
| 401 | |||
| 402 | ////////// | ||
| 403 | |||
| 404 | Attribute& mAttribute; | ||
| 405 | const std::vector<Index64>& mPointOffsets; | ||
| 406 | const Index64 mStartOffset; | ||
| 407 | const math::Transform& mTransform; | ||
| 408 | const size_t mIndex; | ||
| 409 | const FilterT& mFilter; | ||
| 410 | const bool mInCoreOnly; | ||
| 411 | }; // ConvertPointDataGridPositionOp | ||
| 412 | |||
| 413 | |||
| 414 | template<typename PointDataTreeType, typename Attribute, typename FilterT> | ||
| 415 | struct ConvertPointDataGridAttributeOp { | ||
| 416 | |||
| 417 | using LeafNode = typename PointDataTreeType::LeafNodeType; | ||
| 418 | using ValueType = typename Attribute::ValueType; | ||
| 419 | using HandleT = typename Attribute::Handle; | ||
| 420 | using SourceHandleT = typename ConversionTraits<ValueType>::Handle; | ||
| 421 | using LeafManagerT = typename tree::LeafManager<const PointDataTreeType>; | ||
| 422 | using LeafRangeT = typename LeafManagerT::LeafRange; | ||
| 423 | |||
| 424 | 11 | ConvertPointDataGridAttributeOp(Attribute& attribute, | |
| 425 | const std::vector<Index64>& pointOffsets, | ||
| 426 | const Index64 startOffset, | ||
| 427 | const size_t index, | ||
| 428 | const Index stride, | ||
| 429 | const FilterT& filter, | ||
| 430 | const bool inCoreOnly) | ||
| 431 | : mAttribute(attribute) | ||
| 432 | , mPointOffsets(pointOffsets) | ||
| 433 | , mStartOffset(startOffset) | ||
| 434 | , mIndex(index) | ||
| 435 | , mStride(stride) | ||
| 436 | , mFilter(filter) | ||
| 437 | 11 | , mInCoreOnly(inCoreOnly) { } | |
| 438 | |||
| 439 | template <typename IterT> | ||
| 440 | 45642 | void convert(IterT& iter, HandleT& targetHandle, | |
| 441 | SourceHandleT& sourceHandle, Index64& offset) const | ||
| 442 | { | ||
| 443 |
3/3✓ Branch 0 taken 75 times.
✓ Branch 1 taken 11740 times.
✓ Branch 2 taken 11006 times.
|
45642 | if (sourceHandle.isUniform()) { |
| 444 | 12166 | const ValueType uniformValue(sourceHandle.get(0)); | |
| 445 |
2/2✓ Branch 0 taken 1500304 times.
✓ Branch 1 taken 6083 times.
|
3012774 | for (; iter; ++iter) { |
| 446 |
2/2✓ Branch 0 taken 1500304 times.
✓ Branch 1 taken 1500304 times.
|
6001216 | for (Index i = 0; i < mStride; i++) { |
| 447 |
1/2✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
|
3000608 | targetHandle.set(static_cast<Index>(offset), i, uniformValue); |
| 448 | } | ||
| 449 |
1/2✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
|
3000608 | offset++; |
| 450 | } | ||
| 451 | } | ||
| 452 | else { | ||
| 453 |
2/2✓ Branch 0 taken 3079759 times.
✓ Branch 1 taken 16738 times.
|
6192994 | for (; iter; ++iter) { |
| 454 |
2/2✓ Branch 0 taken 3159759 times.
✓ Branch 1 taken 3079759 times.
|
12479036 | for (Index i = 0; i < mStride; i++) { |
| 455 |
1/2✓ Branch 3 taken 1499929 times.
✗ Branch 4 not taken.
|
9319376 | targetHandle.set(static_cast<Index>(offset), i, |
| 456 | sourceHandle.get(*iter, /*stride=*/i)); | ||
| 457 | } | ||
| 458 | 6159518 | offset++; | |
| 459 | } | ||
| 460 | } | ||
| 461 | 45642 | } | |
| 462 | |||
| 463 | 2266 | void operator()(const LeafRangeT& range) const | |
| 464 | { | ||
| 465 | 2266 | HandleT pHandle(mAttribute); | |
| 466 | |||
| 467 |
2/2✓ Branch 1 taken 22821 times.
✓ Branch 2 taken 1133 times.
|
47908 | for (auto leaf = range.begin(); leaf; ++leaf) { |
| 468 | |||
| 469 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22821 times.
|
45642 | assert(leaf.pos() < mPointOffsets.size()); |
| 470 | |||
| 471 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 22821 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
45642 | if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue; |
| 472 | |||
| 473 |
2/2✓ Branch 0 taken 22810 times.
✓ Branch 1 taken 11 times.
|
45642 | Index64 offset = mStartOffset; |
| 474 | |||
| 475 |
2/2✓ Branch 0 taken 22810 times.
✓ Branch 1 taken 11 times.
|
45642 | if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1]; |
| 476 | |||
| 477 |
1/2✓ Branch 1 taken 5807 times.
✗ Branch 2 not taken.
|
23228 | auto handle = ConversionTraits<ValueType>::handleFromLeaf( |
| 478 | 45642 | *leaf, static_cast<Index>(mIndex)); | |
| 479 | |||
| 480 |
2/2✓ Branch 0 taken 8700 times.
✓ Branch 1 taken 8700 times.
|
34800 | if (mFilter.state() == index::ALL) { |
| 481 |
1/2✓ Branch 1 taken 14121 times.
✗ Branch 2 not taken.
|
28242 | auto iter = leaf->beginIndexOn(); |
| 482 |
1/2✓ Branch 1 taken 14121 times.
✗ Branch 2 not taken.
|
28242 | convert(iter, pHandle, *handle, offset); |
| 483 | } else { | ||
| 484 | auto iter = leaf->beginIndexOn(mFilter); | ||
| 485 |
1/2✓ Branch 1 taken 8700 times.
✗ Branch 2 not taken.
|
17400 | convert(iter, pHandle, *handle, offset); |
| 486 | } | ||
| 487 | } | ||
| 488 | 2266 | } | |
| 489 | |||
| 490 | ////////// | ||
| 491 | |||
| 492 | Attribute& mAttribute; | ||
| 493 | const std::vector<Index64>& mPointOffsets; | ||
| 494 | const Index64 mStartOffset; | ||
| 495 | const size_t mIndex; | ||
| 496 | const Index mStride; | ||
| 497 | const FilterT& mFilter; | ||
| 498 | const bool mInCoreOnly; | ||
| 499 | }; // ConvertPointDataGridAttributeOp | ||
| 500 | |||
| 501 | template<typename PointDataTreeType, typename Group, typename FilterT> | ||
| 502 | struct ConvertPointDataGridGroupOp { | ||
| 503 | |||
| 504 | using LeafNode = typename PointDataTreeType::LeafNodeType; | ||
| 505 | using GroupIndex = AttributeSet::Descriptor::GroupIndex; | ||
| 506 | using LeafManagerT = typename tree::LeafManager<const PointDataTreeType>; | ||
| 507 | using LeafRangeT = typename LeafManagerT::LeafRange; | ||
| 508 | |||
| 509 | 3 | ConvertPointDataGridGroupOp(Group& group, | |
| 510 | const std::vector<Index64>& pointOffsets, | ||
| 511 | const Index64 startOffset, | ||
| 512 | const AttributeSet::Descriptor::GroupIndex index, | ||
| 513 | const FilterT& filter, | ||
| 514 | const bool inCoreOnly) | ||
| 515 | : mGroup(group) | ||
| 516 | , mPointOffsets(pointOffsets) | ||
| 517 | , mStartOffset(startOffset) | ||
| 518 | , mIndex(index) | ||
| 519 | , mFilter(filter) | ||
| 520 | 3 | , mInCoreOnly(inCoreOnly) { } | |
| 521 | |||
| 522 | template <typename IterT> | ||
| 523 | 11614 | void convert(IterT& iter, const GroupAttributeArray& groupArray, Index64& offset) const | |
| 524 | { | ||
| 525 |
2/2✓ Branch 0 taken 75 times.
✓ Branch 1 taken 5732 times.
|
11614 | const auto bitmask = static_cast<GroupType>(1 << mIndex.second); |
| 526 | |||
| 527 |
2/2✓ Branch 0 taken 75 times.
✓ Branch 1 taken 5732 times.
|
11614 | if (groupArray.isUniform()) { |
| 528 |
2/2✓ Branch 1 taken 37 times.
✓ Branch 2 taken 38 times.
|
150 | if (groupArray.get(0) & bitmask) { |
| 529 |
2/2✓ Branch 0 taken 61 times.
✓ Branch 1 taken 37 times.
|
196 | for (; iter; ++iter) { |
| 530 | 122 | mGroup.setOffsetOn(static_cast<Index>(offset)); | |
| 531 | 122 | offset++; | |
| 532 | } | ||
| 533 | } | ||
| 534 | } | ||
| 535 | else { | ||
| 536 |
2/2✓ Branch 0 taken 1499929 times.
✓ Branch 1 taken 5732 times.
|
3011322 | for (; iter; ++iter) { |
| 537 |
2/2✓ Branch 2 taken 999948 times.
✓ Branch 3 taken 499981 times.
|
2999858 | if (groupArray.get(*iter) & bitmask) { |
| 538 | 1999896 | mGroup.setOffsetOn(static_cast<Index>(offset)); | |
| 539 | } | ||
| 540 | 2999858 | offset++; | |
| 541 | } | ||
| 542 | } | ||
| 543 | 11614 | } | |
| 544 | |||
| 545 | 540 | void operator()(const LeafRangeT& range) const | |
| 546 | { | ||
| 547 |
2/2✓ Branch 1 taken 5807 times.
✓ Branch 2 taken 270 times.
|
12154 | for (auto leaf = range.begin(); leaf; ++leaf) { |
| 548 | |||
| 549 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5807 times.
|
11614 | assert(leaf.pos() < mPointOffsets.size()); |
| 550 | |||
| 551 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 5807 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
11614 | if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue; |
| 552 | |||
| 553 |
2/2✓ Branch 0 taken 5804 times.
✓ Branch 1 taken 3 times.
|
11614 | Index64 offset = mStartOffset; |
| 554 | |||
| 555 |
2/2✓ Branch 0 taken 5804 times.
✓ Branch 1 taken 3 times.
|
11614 | if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1]; |
| 556 | |||
| 557 | 11614 | const AttributeArray& array = leaf->constAttributeArray(mIndex.first); | |
| 558 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5807 times.
|
11614 | assert(isGroup(array)); |
| 559 | 11614 | const GroupAttributeArray& groupArray = GroupAttributeArray::cast(array); | |
| 560 | |||
| 561 |
2/2✓ Branch 0 taken 2900 times.
✓ Branch 1 taken 2900 times.
|
11600 | if (mFilter.state() == index::ALL) { |
| 562 | 5814 | auto iter = leaf->beginIndexOn(); | |
| 563 | 5814 | convert(iter, groupArray, offset); | |
| 564 | } | ||
| 565 | else { | ||
| 566 | auto iter = leaf->beginIndexOn(mFilter); | ||
| 567 |
1/2✓ Branch 1 taken 2900 times.
✗ Branch 2 not taken.
|
5800 | convert(iter, groupArray, offset); |
| 568 | } | ||
| 569 | } | ||
| 570 | 540 | } | |
| 571 | |||
| 572 | ////////// | ||
| 573 | |||
| 574 | Group& mGroup; | ||
| 575 | const std::vector<Index64>& mPointOffsets; | ||
| 576 | const Index64 mStartOffset; | ||
| 577 | const GroupIndex mIndex; | ||
| 578 | const FilterT& mFilter; | ||
| 579 | const bool mInCoreOnly; | ||
| 580 | }; // ConvertPointDataGridGroupOp | ||
| 581 | |||
| 582 | template<typename PositionArrayT, typename VecT = Vec3R> | ||
| 583 | struct CalculatePositionBounds | ||
| 584 | { | ||
| 585 | CalculatePositionBounds(const PositionArrayT& positions, | ||
| 586 | const math::Mat4d& inverse) | ||
| 587 | : mPositions(positions) | ||
| 588 | , mInverseMat(inverse) | ||
| 589 | , mMin(std::numeric_limits<Real>::max()) | ||
| 590 | 32 | , mMax(-std::numeric_limits<Real>::max()) {} | |
| 591 | |||
| 592 | 57 | CalculatePositionBounds(const CalculatePositionBounds& other, tbb::split) | |
| 593 | 57 | : mPositions(other.mPositions) | |
| 594 | 57 | , mInverseMat(other.mInverseMat) | |
| 595 | , mMin(std::numeric_limits<Real>::max()) | ||
| 596 | 57 | , mMax(-std::numeric_limits<Real>::max()) {} | |
| 597 | |||
| 598 | 3482 | void operator()(const tbb::blocked_range<size_t>& range) { | |
| 599 | VecT pos; | ||
| 600 |
2/2✓ Branch 0 taken 1108087 times.
✓ Branch 1 taken 1741 times.
|
2219656 | for (size_t n = range.begin(), N = range.end(); n != N; ++n) { |
| 601 | 2216174 | mPositions.getPos(n, pos); | |
| 602 | 2216174 | pos = mInverseMat.transform(pos); | |
| 603 | 2216174 | mMin = math::minComponent(mMin, pos); | |
| 604 | 2216174 | mMax = math::maxComponent(mMax, pos); | |
| 605 | } | ||
| 606 | 3482 | } | |
| 607 | |||
| 608 | 114 | void join(const CalculatePositionBounds& other) { | |
| 609 | 114 | mMin = math::minComponent(mMin, other.mMin); | |
| 610 | 114 | mMax = math::maxComponent(mMax, other.mMax); | |
| 611 | } | ||
| 612 | |||
| 613 | BBoxd getBoundingBox() const { | ||
| 614 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 25 times.
|
30 | return BBoxd(mMin, mMax); |
| 615 | } | ||
| 616 | |||
| 617 | private: | ||
| 618 | const PositionArrayT& mPositions; | ||
| 619 | const math::Mat4d& mInverseMat; | ||
| 620 | VecT mMin, mMax; | ||
| 621 | }; | ||
| 622 | |||
| 623 | } // namespace point_conversion_internal | ||
| 624 | |||
| 625 | /// @endcond | ||
| 626 | |||
| 627 | //////////////////////////////////////// | ||
| 628 | |||
| 629 | |||
| 630 | template<typename CompressionT, typename PointDataGridT, typename PositionArrayT, typename PointIndexGridT> | ||
| 631 | inline typename PointDataGridT::Ptr | ||
| 632 | 740 | createPointDataGrid(const PointIndexGridT& pointIndexGrid, | |
| 633 | const PositionArrayT& positions, | ||
| 634 | const math::Transform& xform, | ||
| 635 | const Metadata* positionDefaultValue) | ||
| 636 | { | ||
| 637 | using PointDataTreeT = typename PointDataGridT::TreeType; | ||
| 638 | using LeafT = typename PointDataTreeT::LeafNodeType; | ||
| 639 | using PointIndexLeafT = typename PointIndexGridT::TreeType::LeafNodeType; | ||
| 640 | using PointIndexT = typename PointIndexLeafT::ValueType; | ||
| 641 | using LeafManagerT = typename tree::LeafManager<PointDataTreeT>; | ||
| 642 | using PositionAttributeT = TypedAttributeArray<Vec3f, CompressionT>; | ||
| 643 | |||
| 644 | 1480 | const NamePair positionType = PositionAttributeT::attributeType(); | |
| 645 | |||
| 646 | // construct the Tree using a topology copy of the PointIndexGrid | ||
| 647 | |||
| 648 | const auto& pointIndexTree = pointIndexGrid.tree(); | ||
| 649 |
2/4✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 724 times.
✗ Branch 5 not taken.
|
740 | typename PointDataTreeT::Ptr treePtr(new PointDataTreeT(pointIndexTree)); |
| 650 | |||
| 651 | // create attribute descriptor from position type | ||
| 652 | |||
| 653 |
1/2✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
|
740 | auto descriptor = AttributeSet::Descriptor::create(positionType); |
| 654 | |||
| 655 | // add default value for position if provided | ||
| 656 | |||
| 657 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 724 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
740 | if (positionDefaultValue) descriptor->setDefaultValue("P", *positionDefaultValue); |
| 658 | |||
| 659 | // retrieve position index | ||
| 660 | |||
| 661 |
2/4✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 724 times.
✗ Branch 5 not taken.
|
740 | const size_t positionIndex = descriptor->find("P"); |
| 662 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 724 times.
|
740 | assert(positionIndex != AttributeSet::INVALID_POS); |
| 663 | |||
| 664 | // acquire registry lock to avoid locking when appending attributes in parallel | ||
| 665 | |||
| 666 |
1/2✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
|
740 | AttributeArray::ScopedRegistryLock lock; |
| 667 | |||
| 668 | // populate position attribute | ||
| 669 | |||
| 670 |
1/2✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
|
740 | LeafManagerT leafManager(*treePtr); |
| 671 |
1/2✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
|
740 | leafManager.foreach( |
| 672 |
1/2✓ Branch 2 taken 5090 times.
✗ Branch 3 not taken.
|
1544957 | [&](LeafT& leaf, size_t /*idx*/) { |
| 673 | |||
| 674 | // obtain the PointIndexLeafNode (using the origin of the current leaf) | ||
| 675 | |||
| 676 | const auto* pointIndexLeaf = pointIndexTree.probeConstLeaf(leaf.origin()); | ||
| 677 |
3/8✗ Branch 0 not taken.
✓ Branch 1 taken 4304 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 26611 times.
|
30917 | assert(pointIndexLeaf); |
| 678 | |||
| 679 | // initialise the attribute storage | ||
| 680 | |||
| 681 | 30917 | Index pointCount(static_cast<Index>(pointIndexLeaf->indices().size())); | |
| 682 | 30917 | leaf.initializeAttributes(descriptor, pointCount, &lock); | |
| 683 | |||
| 684 | // create write handle for position | ||
| 685 | |||
| 686 | 30917 | auto attributeWriteHandle = AttributeWriteHandle<Vec3f, CompressionT>::create( | |
| 687 | leaf.attributeArray(positionIndex)); | ||
| 688 | |||
| 689 | Index index = 0; | ||
| 690 | |||
| 691 | const PointIndexT | ||
| 692 | 30917 | *begin = static_cast<PointIndexT*>(nullptr), | |
| 693 | 30917 | *end = static_cast<PointIndexT*>(nullptr); | |
| 694 | |||
| 695 | // iterator over every active voxel in the point index leaf | ||
| 696 | |||
| 697 |
6/8✓ Branch 0 taken 44763 times.
✓ Branch 1 taken 4304 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 366490 times.
✓ Branch 7 taken 26611 times.
|
442172 | for (auto iter = pointIndexLeaf->cbeginValueOn(); iter; ++iter) { |
| 698 | |||
| 699 | // find the voxel center | ||
| 700 | |||
| 701 |
3/8✓ Branch 1 taken 44763 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 10 taken 366490 times.
✗ Branch 11 not taken.
|
411255 | const Coord& ijk = iter.getCoord(); |
| 702 | const Vec3d& positionCellCenter(ijk.asVec3d()); | ||
| 703 | |||
| 704 | // obtain pointers for this voxel from begin to end in the indices array | ||
| 705 | |||
| 706 |
3/8✓ Branch 1 taken 44763 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 10 taken 366490 times.
✗ Branch 11 not taken.
|
411255 | pointIndexLeaf->getIndices(ijk, begin, end); |
| 707 | |||
| 708 |
6/8✓ Branch 0 taken 95096 times.
✓ Branch 1 taken 44763 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1388025 times.
✓ Branch 7 taken 366490 times.
|
1894378 | while (begin < end) { |
| 709 | |||
| 710 | typename PositionArrayT::value_type positionWorldSpace; | ||
| 711 |
3/8✓ Branch 1 taken 95095 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 10 taken 1388025 times.
✗ Branch 11 not taken.
|
1483122 | positions.getPos(*begin, positionWorldSpace); |
| 712 | |||
| 713 | // compute the index-space position and then subtract the voxel center | ||
| 714 | |||
| 715 |
0/6✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
1391706 | const Vec3d positionIndexSpace = xform.worldToIndex(positionWorldSpace); |
| 716 | 1483123 | const Vec3f positionVoxelSpace(positionIndexSpace - positionCellCenter); | |
| 717 | |||
| 718 | 1483123 | attributeWriteHandle->set(index++, positionVoxelSpace); | |
| 719 | |||
| 720 | 1483123 | ++begin; | |
| 721 | } | ||
| 722 | } | ||
| 723 | }, | ||
| 724 | /*threaded=*/true); | ||
| 725 | |||
| 726 |
2/4✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 724 times.
✗ Branch 5 not taken.
|
1480 | auto grid = PointDataGridT::create(treePtr); |
| 727 |
2/6✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 724 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
1480 | grid->setTransform(xform.copy()); |
| 728 | 740 | return grid; | |
| 729 | } | ||
| 730 | |||
| 731 | |||
| 732 | //////////////////////////////////////// | ||
| 733 | |||
| 734 | |||
| 735 | template <typename CompressionT, typename PointDataGridT, typename ValueT> | ||
| 736 | inline typename PointDataGridT::Ptr | ||
| 737 | 620 | createPointDataGrid(const std::vector<ValueT>& positions, | |
| 738 | const math::Transform& xform, | ||
| 739 | const Metadata* positionDefaultValue) | ||
| 740 | { | ||
| 741 | const PointAttributeVector<ValueT> pointList(positions); | ||
| 742 | |||
| 743 | 620 | tools::PointIndexGrid::Ptr pointIndexGrid = | |
| 744 | tools::createPointIndexGrid<tools::PointIndexGrid>(pointList, xform); | ||
| 745 | return createPointDataGrid<CompressionT, PointDataGridT>( | ||
| 746 |
1/2✓ Branch 1 taken 615 times.
✗ Branch 2 not taken.
|
1240 | *pointIndexGrid, pointList, xform, positionDefaultValue); |
| 747 | } | ||
| 748 | |||
| 749 | |||
| 750 | //////////////////////////////////////// | ||
| 751 | |||
| 752 | |||
| 753 | template <typename PointDataTreeT, typename PointIndexTreeT, typename PointArrayT> | ||
| 754 | inline void | ||
| 755 | 55 | populateAttribute(PointDataTreeT& tree, const PointIndexTreeT& pointIndexTree, | |
| 756 | const openvdb::Name& attributeName, const PointArrayT& data, const Index stride, | ||
| 757 | const bool insertMetadata) | ||
| 758 | { | ||
| 759 | using point_conversion_internal::PopulateAttributeOp; | ||
| 760 | using ValueType = typename PointArrayT::value_type; | ||
| 761 | |||
| 762 | auto iter = tree.cbeginLeaf(); | ||
| 763 | |||
| 764 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
55 | if (!iter) return; |
| 765 | |||
| 766 | 55 | const size_t index = iter->attributeSet().find(attributeName); | |
| 767 | |||
| 768 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
55 | if (index == AttributeSet::INVALID_POS) { |
| 769 | ✗ | OPENVDB_THROW(KeyError, "Attribute not found to populate - " << attributeName << "."); | |
| 770 | } | ||
| 771 | |||
| 772 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
4 | if (insertMetadata) { |
| 773 | 4 | point_attribute_internal::MetadataStorage<PointDataTreeT, ValueType>::add(tree, data); | |
| 774 | } | ||
| 775 | |||
| 776 | // populate attribute | ||
| 777 | |||
| 778 | 55 | typename tree::LeafManager<PointDataTreeT> leafManager(tree); | |
| 779 | |||
| 780 | PopulateAttributeOp<PointDataTreeT, | ||
| 781 | PointIndexTreeT, | ||
| 782 | PointArrayT> populate(pointIndexTree, data, index, stride); | ||
| 783 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
55 | tbb::parallel_for(leafManager.leafRange(), populate); |
| 784 | } | ||
| 785 | |||
| 786 | |||
| 787 | //////////////////////////////////////// | ||
| 788 | |||
| 789 | |||
| 790 | template <typename PositionAttribute, typename PointDataGridT, typename FilterT> | ||
| 791 | inline void | ||
| 792 | 8 | convertPointDataGridPosition( PositionAttribute& positionAttribute, | |
| 793 | const PointDataGridT& grid, | ||
| 794 | const std::vector<Index64>& pointOffsets, | ||
| 795 | const Index64 startOffset, | ||
| 796 | const FilterT& filter, | ||
| 797 | const bool inCoreOnly) | ||
| 798 | { | ||
| 799 | using TreeType = typename PointDataGridT::TreeType; | ||
| 800 | using LeafManagerT = typename tree::LeafManager<const TreeType>; | ||
| 801 | |||
| 802 | using point_conversion_internal::ConvertPointDataGridPositionOp; | ||
| 803 | |||
| 804 | const TreeType& tree = grid.tree(); | ||
| 805 | auto iter = tree.cbeginLeaf(); | ||
| 806 | |||
| 807 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
8 | if (!iter) return; |
| 808 | |||
| 809 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
8 | const size_t positionIndex = iter->attributeSet().find("P"); |
| 810 | |||
| 811 | positionAttribute.expand(); | ||
| 812 | 8 | LeafManagerT leafManager(tree); | |
| 813 | ConvertPointDataGridPositionOp<TreeType, PositionAttribute, FilterT> convert( | ||
| 814 | positionAttribute, pointOffsets, startOffset, grid.transform(), positionIndex, | ||
| 815 | filter, inCoreOnly); | ||
| 816 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | tbb::parallel_for(leafManager.leafRange(), convert); |
| 817 | positionAttribute.compact(); | ||
| 818 | } | ||
| 819 | |||
| 820 | |||
| 821 | //////////////////////////////////////// | ||
| 822 | |||
| 823 | |||
| 824 | template <typename TypedAttribute, typename PointDataTreeT, typename FilterT> | ||
| 825 | inline void | ||
| 826 | 22 | convertPointDataGridAttribute( TypedAttribute& attribute, | |
| 827 | const PointDataTreeT& tree, | ||
| 828 | const std::vector<Index64>& pointOffsets, | ||
| 829 | const Index64 startOffset, | ||
| 830 | const unsigned arrayIndex, | ||
| 831 | const Index stride, | ||
| 832 | const FilterT& filter, | ||
| 833 | const bool inCoreOnly) | ||
| 834 | { | ||
| 835 | using LeafManagerT = typename tree::LeafManager<const PointDataTreeT>; | ||
| 836 | |||
| 837 | using point_conversion_internal::ConvertPointDataGridAttributeOp; | ||
| 838 | |||
| 839 | auto iter = tree.cbeginLeaf(); | ||
| 840 | |||
| 841 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
22 | if (!iter) return; |
| 842 | |||
| 843 | attribute.expand(); | ||
| 844 | 22 | LeafManagerT leafManager(tree); | |
| 845 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
22 | ConvertPointDataGridAttributeOp<PointDataTreeT, TypedAttribute, FilterT> convert( |
| 846 | attribute, pointOffsets, startOffset, arrayIndex, stride, | ||
| 847 | filter, inCoreOnly); | ||
| 848 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
22 | tbb::parallel_for(leafManager.leafRange(), convert); |
| 849 | attribute.compact(); | ||
| 850 | } | ||
| 851 | |||
| 852 | |||
| 853 | //////////////////////////////////////// | ||
| 854 | |||
| 855 | |||
| 856 | template <typename Group, typename PointDataTreeT, typename FilterT> | ||
| 857 | inline void | ||
| 858 | 6 | convertPointDataGridGroup( Group& group, | |
| 859 | const PointDataTreeT& tree, | ||
| 860 | const std::vector<Index64>& pointOffsets, | ||
| 861 | const Index64 startOffset, | ||
| 862 | const AttributeSet::Descriptor::GroupIndex index, | ||
| 863 | const FilterT& filter, | ||
| 864 | const bool inCoreOnly) | ||
| 865 | { | ||
| 866 | using LeafManagerT= typename tree::LeafManager<const PointDataTreeT>; | ||
| 867 | |||
| 868 | using point_conversion_internal::ConvertPointDataGridGroupOp; | ||
| 869 | |||
| 870 | auto iter = tree.cbeginLeaf(); | ||
| 871 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
6 | if (!iter) return; |
| 872 | |||
| 873 | 6 | LeafManagerT leafManager(tree); | |
| 874 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | ConvertPointDataGridGroupOp<PointDataTreeT, Group, FilterT> convert( |
| 875 | group, pointOffsets, startOffset, index, | ||
| 876 | filter, inCoreOnly); | ||
| 877 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | tbb::parallel_for(leafManager.leafRange(), convert); |
| 878 | |||
| 879 | // must call this after modifying point groups in parallel | ||
| 880 | |||
| 881 | group.finalize(); | ||
| 882 | } | ||
| 883 | |||
| 884 | template<typename PositionWrapper, typename InterrupterT, typename VecT> | ||
| 885 | inline float | ||
| 886 | 34 | computeVoxelSize( const PositionWrapper& positions, | |
| 887 | const uint32_t pointsPerVoxel, | ||
| 888 | const math::Mat4d transform, | ||
| 889 | const Index decimalPlaces, | ||
| 890 | InterrupterT* const interrupter) | ||
| 891 | { | ||
| 892 | using namespace point_conversion_internal; | ||
| 893 | |||
| 894 | struct Local { | ||
| 895 | |||
| 896 | static bool voxelSizeFromVolume(const double volume, | ||
| 897 | const size_t estimatedVoxelCount, | ||
| 898 | float& voxelSize) | ||
| 899 | { | ||
| 900 | // dictated by the math::ScaleMap limit | ||
| 901 | static const double minimumVoxelVolume(3e-15); | ||
| 902 | static const double maximumVoxelVolume(std::numeric_limits<float>::max()); | ||
| 903 | |||
| 904 | 130 | double voxelVolume = volume / static_cast<double>(estimatedVoxelCount); | |
| 905 | bool valid = true; | ||
| 906 | |||
| 907 | 130 | if (voxelVolume < minimumVoxelVolume) { | |
| 908 | voxelVolume = minimumVoxelVolume; | ||
| 909 | valid = false; | ||
| 910 | } | ||
| 911 | 122 | else if (voxelVolume > maximumVoxelVolume) { | |
| 912 | voxelVolume = maximumVoxelVolume; | ||
| 913 | valid = false; | ||
| 914 | } | ||
| 915 | |||
| 916 | 130 | voxelSize = static_cast<float>(math::Pow(voxelVolume, 1.0/3.0)); | |
| 917 | return valid; | ||
| 918 | } | ||
| 919 | |||
| 920 | static float truncate(const float voxelSize, Index decPlaces) | ||
| 921 | { | ||
| 922 | float truncatedVoxelSize = voxelSize; | ||
| 923 | |||
| 924 | // attempt to truncate from decPlaces -> 11 | ||
| 925 | 28 | for (int i = decPlaces; i < 11; i++) { | |
| 926 | 28 | truncatedVoxelSize = static_cast<float>(math::Truncate(double(voxelSize), i)); | |
| 927 | 28 | if (truncatedVoxelSize != 0.0f) break; | |
| 928 | } | ||
| 929 | |||
| 930 | return truncatedVoxelSize; | ||
| 931 | } | ||
| 932 | }; | ||
| 933 | |||
| 934 | 34 | if (pointsPerVoxel == 0) OPENVDB_THROW(ValueError, "Points per voxel cannot be zero."); | |
| 935 | |||
| 936 | // constructed with the default voxel size as specified by openvdb interface values | ||
| 937 | |||
| 938 | float voxelSize(0.1f); | ||
| 939 | |||
| 940 | const size_t numPoints = positions.size(); | ||
| 941 | |||
| 942 | // return the default voxel size if we have zero or only 1 point | ||
| 943 | |||
| 944 | 34 | if (numPoints <= 1) return voxelSize; | |
| 945 | |||
| 946 | 32 | size_t targetVoxelCount(numPoints / size_t(pointsPerVoxel)); | |
| 947 | 32 | if (targetVoxelCount == 0) targetVoxelCount++; | |
| 948 | |||
| 949 | // calculate the world space, transform-oriented bounding box | ||
| 950 | |||
| 951 | 32 | math::Mat4d inverseTransform = transform.inverse(); | |
| 952 | 32 | inverseTransform = math::unit(inverseTransform); | |
| 953 | |||
| 954 | tbb::blocked_range<size_t> range(0, numPoints); | ||
| 955 | CalculatePositionBounds<PositionWrapper, VecT> calculateBounds(positions, inverseTransform); | ||
| 956 | 32 | tbb::parallel_reduce(range, calculateBounds); | |
| 957 | |||
| 958 | BBoxd bbox = calculateBounds.getBoundingBox(); | ||
| 959 | |||
| 960 | // return default size if points are coincident | ||
| 961 | |||
| 962 | if (bbox.min() == bbox.max()) return voxelSize; | ||
| 963 | |||
| 964 | double volume = bbox.volume(); | ||
| 965 | |||
| 966 | // handle points that are collinear or coplanar by expanding the volume | ||
| 967 | |||
| 968 | if (math::isApproxZero(volume)) { | ||
| 969 | 12 | Vec3d extents = bbox.extents().sorted().reversed(); | |
| 970 | if (math::isApproxZero(extents[1])) { | ||
| 971 | // colinear (maxExtent^3) | ||
| 972 | 8 | volume = extents[0]*extents[0]*extents[0]; | |
| 973 | } | ||
| 974 | else { | ||
| 975 | // coplanar (maxExtent*nextMaxExtent^2) | ||
| 976 | 4 | volume = extents[0]*extents[1]*extents[1]; | |
| 977 | } | ||
| 978 | } | ||
| 979 | |||
| 980 | double previousVolume = volume; | ||
| 981 | |||
| 982 | 32 | if (!Local::voxelSizeFromVolume(volume, targetVoxelCount, voxelSize)) { | |
| 983 | OPENVDB_LOG_DEBUG("Out of range, clamping voxel size."); | ||
| 984 | return voxelSize; | ||
| 985 | } | ||
| 986 | |||
| 987 | size_t previousVoxelCount(0); | ||
| 988 | size_t voxelCount(1); | ||
| 989 | |||
| 990 | 23 | if (interrupter) interrupter->start("Computing voxel size"); | |
| 991 | |||
| 992 | 109 | while (voxelCount > previousVoxelCount) | |
| 993 | { | ||
| 994 | 104 | math::Transform::Ptr newTransform; | |
| 995 | |||
| 996 | 104 | if (!math::isIdentity(transform)) | |
| 997 | { | ||
| 998 | // if using a custom transform, pre-scale by coefficients | ||
| 999 | // which define the new voxel size | ||
| 1000 | |||
| 1001 | 16 | math::Mat4d matrix(transform); | |
| 1002 | 16 | matrix.preScale(Vec3d(voxelSize) / math::getScale(matrix)); | |
| 1003 | 32 | newTransform = math::Transform::createLinearTransform(matrix); | |
| 1004 | } | ||
| 1005 | else | ||
| 1006 | { | ||
| 1007 | 176 | newTransform = math::Transform::createLinearTransform(voxelSize); | |
| 1008 | } | ||
| 1009 | |||
| 1010 | // create a mask grid of the points from the calculated voxel size | ||
| 1011 | // this is the same function call as tools::createPointMask() which has | ||
| 1012 | // been duplicated to provide an interrupter | ||
| 1013 | |||
| 1014 | 104 | MaskGrid::Ptr mask = createGrid<MaskGrid>(false); | |
| 1015 | 208 | mask->setTransform(newTransform); | |
| 1016 | tools::PointsToMask<MaskGrid, InterrupterT> pointMaskOp(*mask, interrupter); | ||
| 1017 | 104 | pointMaskOp.template addPoints<PositionWrapper, VecT>(positions); | |
| 1018 | |||
| 1019 | 104 | if (interrupter && util::wasInterrupted(interrupter)) break; | |
| 1020 | |||
| 1021 | previousVoxelCount = voxelCount; | ||
| 1022 | 104 | voxelCount = mask->activeVoxelCount(); | |
| 1023 | 104 | volume = math::Pow3(voxelSize) * static_cast<float>(voxelCount); | |
| 1024 | |||
| 1025 | // stop if no change in the volume or the volume has increased | ||
| 1026 | |||
| 1027 | 104 | if (volume >= previousVolume) break; | |
| 1028 | previousVolume = volume; | ||
| 1029 | |||
| 1030 | const float previousVoxelSize = voxelSize; | ||
| 1031 | |||
| 1032 | // compute the new voxel size and if invalid return the previous value | ||
| 1033 | |||
| 1034 | 98 | if (!Local::voxelSizeFromVolume(volume, targetVoxelCount, voxelSize)) { | |
| 1035 | voxelSize = previousVoxelSize; | ||
| 1036 | break; | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | // halt convergence if the voxel size has decreased by less | ||
| 1040 | // than 10% in this iteration | ||
| 1041 | |||
| 1042 | 96 | if (voxelSize / previousVoxelSize > 0.9f) break; | |
| 1043 | } | ||
| 1044 | |||
| 1045 | 23 | if (interrupter) interrupter->end(); | |
| 1046 | |||
| 1047 | // truncate the voxel size for readability and return the value | ||
| 1048 | |||
| 1049 | return Local::truncate(voxelSize, decimalPlaces); | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | |||
| 1053 | //////////////////////////////////////// | ||
| 1054 | |||
| 1055 | |||
| 1056 | } // namespace points | ||
| 1057 | } // namespace OPENVDB_VERSION_NAME | ||
| 1058 | } // namespace openvdb | ||
| 1059 | |||
| 1060 | #endif // OPENVDB_POINTS_POINT_CONVERSION_HAS_BEEN_INCLUDED | ||
| 1061 |