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 |