OpenVDB  9.0.1
PointConversion.h
Go to the documentation of this file.
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 
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 {
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>
206 public:
207  using PosType = ValueType;
208  using value_type= ValueType;
209 
210  PointAttributeVector(const std::vector<value_type>& data,
211  const Index stride = 1)
212  : mData(data)
213  , mStride(stride) { }
214 
215  size_t size() const { return mData.size(); }
216  void getPos(size_t n, ValueType& xyz) const { xyz = mData[n]; }
217  void get(ValueType& value, size_t n) const { value = mData[n]; }
218  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 typename Handle::Ptr handleFromLeaf(LeafT& leaf, Index index) {
241  const AttributeArray& array = leaf.constAttributeArray(index);
242  return Handle::create(array);
243  }
244  template <typename LeafT>
245  static typename WriteHandle::Ptr writeHandleFromLeaf(LeafT& leaf, Index index) {
246  AttributeArray& array = leaf.attributeArray(index);
247  return WriteHandle::create(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  static typename Handle::Ptr handleFromLeaf(LeafT& leaf, Index index) {
257  const AttributeArray& array = leaf.constAttributeArray(index);
258  const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor();
259  return Handle::create(array, descriptor.getMetadata());
260  }
261  template <typename LeafT>
262  static typename WriteHandle::Ptr writeHandleFromLeaf(LeafT& leaf, Index index) {
263  AttributeArray& array = leaf.attributeArray(index);
264  const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor();
265  return WriteHandle::create(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  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  , mStride(stride) { }
289 
290  void operator()(const typename LeafManagerT::LeafRange& range) const {
291 
292  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  mPointIndexTree.probeConstLeaf(leaf->origin());
298 
299  if (!pointIndexLeaf) continue;
300 
301  typename HandleT::Ptr attributeWriteHandle =
302  ConversionTraits<ValueType>::writeHandleFromLeaf(*leaf, static_cast<Index>(mIndex));
303 
304  Index64 index = 0;
305 
306  const IndexArray& indices = pointIndexLeaf->indices();
307 
308  for (const Index64 leafIndex: indices)
309  {
310  ValueType value;
311  for (Index i = 0; i < mStride; i++) {
312  mData.get(value, leafIndex, i);
313  attributeWriteHandle->set(static_cast<Index>(index), i, value);
314  }
315  index++;
316  }
317 
318  // attempt to compact the array
319 
320  attributeWriteHandle->compact();
321  }
322  }
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  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  , mInCoreOnly(inCoreOnly)
356  {
357  // only accept Vec3f as ValueType
358  static_assert(VecTraits<ValueType>::Size == 3 &&
360  "ValueType is not Vec3f");
361  }
362 
363  template <typename IterT>
364  void convert(IterT& iter, HandleT& targetHandle,
365  SourceHandleT& sourceHandle, Index64& offset) const
366  {
367  for (; iter; ++iter) {
368  const Vec3d xyz = iter.getCoord().asVec3d();
369  const Vec3d pos = sourceHandle.get(*iter);
370  targetHandle.set(static_cast<Index>(offset++), /*stride=*/0,
371  mTransform.indexToWorld(pos + xyz));
372  }
373  }
374 
375  void operator()(const LeafRangeT& range) const
376  {
377  HandleT pHandle(mAttribute);
378 
379  for (auto leaf = range.begin(); leaf; ++leaf) {
380 
381  assert(leaf.pos() < mPointOffsets.size());
382 
383  if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue;
384 
385  Index64 offset = mStartOffset;
386 
387  if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1];
388 
389  auto handle = SourceHandleT::create(leaf->constAttributeArray(mIndex));
390 
391  if (mFilter.state() == index::ALL) {
392  auto iter = leaf->beginIndexOn();
393  convert(iter, pHandle, *handle, offset);
394  }
395  else {
396  auto iter = leaf->beginIndexOn(mFilter);
397  convert(iter, pHandle, *handle, offset);
398  }
399  }
400  }
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  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  , mInCoreOnly(inCoreOnly) { }
438 
439  template <typename IterT>
440  void convert(IterT& iter, HandleT& targetHandle,
441  SourceHandleT& sourceHandle, Index64& offset) const
442  {
443  if (sourceHandle.isUniform()) {
444  const ValueType uniformValue(sourceHandle.get(0));
445  for (; iter; ++iter) {
446  for (Index i = 0; i < mStride; i++) {
447  targetHandle.set(static_cast<Index>(offset), i, uniformValue);
448  }
449  offset++;
450  }
451  }
452  else {
453  for (; iter; ++iter) {
454  for (Index i = 0; i < mStride; i++) {
455  targetHandle.set(static_cast<Index>(offset), i,
456  sourceHandle.get(*iter, /*stride=*/i));
457  }
458  offset++;
459  }
460  }
461  }
462 
463  void operator()(const LeafRangeT& range) const
464  {
465  HandleT pHandle(mAttribute);
466 
467  for (auto leaf = range.begin(); leaf; ++leaf) {
468 
469  assert(leaf.pos() < mPointOffsets.size());
470 
471  if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue;
472 
473  Index64 offset = mStartOffset;
474 
475  if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1];
476 
477  typename SourceHandleT::Ptr handle = ConversionTraits<ValueType>::handleFromLeaf(
478  *leaf, static_cast<Index>(mIndex));
479 
480  if (mFilter.state() == index::ALL) {
481  auto iter = leaf->beginIndexOn();
482  convert(iter, pHandle, *handle, offset);
483  } else {
484  auto iter = leaf->beginIndexOn(mFilter);
485  convert(iter, pHandle, *handle, offset);
486  }
487  }
488  }
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  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  , mInCoreOnly(inCoreOnly) { }
521 
522  template <typename IterT>
523  void convert(IterT& iter, const GroupAttributeArray& groupArray, Index64& offset) const
524  {
525  const auto bitmask = static_cast<GroupType>(1 << mIndex.second);
526 
527  if (groupArray.isUniform()) {
528  if (groupArray.get(0) & bitmask) {
529  for (; iter; ++iter) {
530  mGroup.setOffsetOn(static_cast<Index>(offset));
531  offset++;
532  }
533  }
534  }
535  else {
536  for (; iter; ++iter) {
537  if (groupArray.get(*iter) & bitmask) {
538  mGroup.setOffsetOn(static_cast<Index>(offset));
539  }
540  offset++;
541  }
542  }
543  }
544 
545  void operator()(const LeafRangeT& range) const
546  {
547  for (auto leaf = range.begin(); leaf; ++leaf) {
548 
549  assert(leaf.pos() < mPointOffsets.size());
550 
551  if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue;
552 
553  Index64 offset = mStartOffset;
554 
555  if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1];
556 
557  const AttributeArray& array = leaf->constAttributeArray(mIndex.first);
558  assert(isGroup(array));
559  const GroupAttributeArray& groupArray = GroupAttributeArray::cast(array);
560 
561  if (mFilter.state() == index::ALL) {
562  auto iter = leaf->beginIndexOn();
563  convert(iter, groupArray, offset);
564  }
565  else {
566  auto iter = leaf->beginIndexOn(mFilter);
567  convert(iter, groupArray, offset);
568  }
569  }
570  }
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)
590  , mMax(-std::numeric_limits<Real>::max()) {}
591 
592  CalculatePositionBounds(const CalculatePositionBounds& other, tbb::split)
593  : mPositions(other.mPositions)
594  , mInverseMat(other.mInverseMat)
596  , mMax(-std::numeric_limits<Real>::max()) {}
597 
598  void operator()(const tbb::blocked_range<size_t>& range) {
599  VecT pos;
600  for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
601  mPositions.getPos(n, pos);
602  pos = mInverseMat.transform(pos);
603  mMin = math::minComponent(mMin, pos);
604  mMax = math::maxComponent(mMax, pos);
605  }
606  }
607 
608  void join(const CalculatePositionBounds& other) {
609  mMin = math::minComponent(mMin, other.mMin);
610  mMax = math::maxComponent(mMax, other.mMax);
611  }
612 
613  BBoxd getBoundingBox() const {
614  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 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  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  typename PointDataTreeT::Ptr treePtr(new PointDataTreeT(pointIndexTree));
650 
651  // create attribute descriptor from position type
652 
653  auto descriptor = AttributeSet::Descriptor::create(positionType);
654 
655  // add default value for position if provided
656 
657  if (positionDefaultValue) descriptor->setDefaultValue("P", *positionDefaultValue);
658 
659  // retrieve position index
660 
661  const size_t positionIndex = descriptor->find("P");
662  assert(positionIndex != AttributeSet::INVALID_POS);
663 
664  // acquire registry lock to avoid locking when appending attributes in parallel
665 
667 
668  // populate position attribute
669 
670  LeafManagerT leafManager(*treePtr);
671  leafManager.foreach(
672  [&](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  assert(pointIndexLeaf);
678 
679  // initialise the attribute storage
680 
681  Index pointCount(static_cast<Index>(pointIndexLeaf->indices().size()));
682  leaf.initializeAttributes(descriptor, pointCount, &lock);
683 
684  // create write handle for position
685 
686  auto attributeWriteHandle = AttributeWriteHandle<Vec3f, CompressionT>::create(
687  leaf.attributeArray(positionIndex));
688 
689  Index index = 0;
690 
691  const PointIndexT
692  *begin = static_cast<PointIndexT*>(nullptr),
693  *end = static_cast<PointIndexT*>(nullptr);
694 
695  // iterator over every active voxel in the point index leaf
696 
697  for (auto iter = pointIndexLeaf->cbeginValueOn(); iter; ++iter) {
698 
699  // find the voxel center
700 
701  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  pointIndexLeaf->getIndices(ijk, begin, end);
707 
708  while (begin < end) {
709 
710  typename PositionArrayT::value_type positionWorldSpace;
711  positions.getPos(*begin, positionWorldSpace);
712 
713  // compute the index-space position and then subtract the voxel center
714 
715  const Vec3d positionIndexSpace = xform.worldToIndex(positionWorldSpace);
716  const Vec3f positionVoxelSpace(positionIndexSpace - positionCellCenter);
717 
718  attributeWriteHandle->set(index++, positionVoxelSpace);
719 
720  ++begin;
721  }
722  }
723  },
724  /*threaded=*/true);
725 
726  auto grid = PointDataGridT::create(treePtr);
727  grid->setTransform(xform.copy());
728  return grid;
729 }
730 
731 
732 ////////////////////////////////////////
733 
734 
735 template <typename CompressionT, typename PointDataGridT, typename ValueT>
736 inline typename PointDataGridT::Ptr
737 createPointDataGrid(const std::vector<ValueT>& positions,
738  const math::Transform& xform,
739  const Metadata* positionDefaultValue)
740 {
741  const PointAttributeVector<ValueT> pointList(positions);
742 
743  tools::PointIndexGrid::Ptr pointIndexGrid =
744  tools::createPointIndexGrid<tools::PointIndexGrid>(pointList, xform);
745  return createPointDataGrid<CompressionT, PointDataGridT>(
746  *pointIndexGrid, pointList, xform, positionDefaultValue);
747 }
748 
749 
750 ////////////////////////////////////////
751 
752 
753 template <typename PointDataTreeT, typename PointIndexTreeT, typename PointArrayT>
754 inline void
755 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  if (!iter) return;
765 
766  const size_t index = iter->attributeSet().find(attributeName);
767 
768  if (index == AttributeSet::INVALID_POS) {
769  OPENVDB_THROW(KeyError, "Attribute not found to populate - " << attributeName << ".");
770  }
771 
772  if (insertMetadata) {
773  point_attribute_internal::MetadataStorage<PointDataTreeT, ValueType>::add(tree, data);
774  }
775 
776  // populate attribute
777 
778  typename tree::LeafManager<PointDataTreeT> leafManager(tree);
779 
780  PopulateAttributeOp<PointDataTreeT,
781  PointIndexTreeT,
782  PointArrayT> populate(pointIndexTree, data, index, stride);
783  tbb::parallel_for(leafManager.leafRange(), populate);
784 }
785 
786 
787 ////////////////////////////////////////
788 
789 
790 template <typename PositionAttribute, typename PointDataGridT, typename FilterT>
791 inline void
792 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  if (!iter) return;
808 
809  const size_t positionIndex = iter->attributeSet().find("P");
810 
811  positionAttribute.expand();
812  LeafManagerT leafManager(tree);
813  ConvertPointDataGridPositionOp<TreeType, PositionAttribute, FilterT> convert(
814  positionAttribute, pointOffsets, startOffset, grid.transform(), positionIndex,
815  filter, inCoreOnly);
816  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 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  if (!iter) return;
842 
843  attribute.expand();
844  LeafManagerT leafManager(tree);
845  ConvertPointDataGridAttributeOp<PointDataTreeT, TypedAttribute, FilterT> convert(
846  attribute, pointOffsets, startOffset, arrayIndex, stride,
847  filter, inCoreOnly);
848  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
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  if (!iter) return;
872 
873  LeafManagerT leafManager(tree);
874  ConvertPointDataGridGroupOp<PointDataTreeT, Group, FilterT> convert(
875  group, pointOffsets, startOffset, index,
876  filter, inCoreOnly);
877  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 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  double voxelVolume = volume / static_cast<double>(estimatedVoxelCount);
905  bool valid = true;
906 
907  if (voxelVolume < minimumVoxelVolume) {
908  voxelVolume = minimumVoxelVolume;
909  valid = false;
910  }
911  else if (voxelVolume > maximumVoxelVolume) {
912  voxelVolume = maximumVoxelVolume;
913  valid = false;
914  }
915 
916  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  for (int i = decPlaces; i < 11; i++) {
926  truncatedVoxelSize = static_cast<float>(math::Truncate(double(voxelSize), i));
927  if (truncatedVoxelSize != 0.0f) break;
928  }
929 
930  return truncatedVoxelSize;
931  }
932  };
933 
934  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  if (numPoints <= 1) return voxelSize;
945 
946  size_t targetVoxelCount(numPoints / size_t(pointsPerVoxel));
947  if (targetVoxelCount == 0) targetVoxelCount++;
948 
949  // calculate the world space, transform-oriented bounding box
950 
951  math::Mat4d inverseTransform = transform.inverse();
952  inverseTransform = math::unit(inverseTransform);
953 
954  tbb::blocked_range<size_t> range(0, numPoints);
955  CalculatePositionBounds<PositionWrapper, VecT> calculateBounds(positions, inverseTransform);
956  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  Vec3d extents = bbox.extents().sorted().reversed();
970  if (math::isApproxZero(extents[1])) {
971  // colinear (maxExtent^3)
972  volume = extents[0]*extents[0]*extents[0];
973  }
974  else {
975  // coplanar (maxExtent*nextMaxExtent^2)
976  volume = extents[0]*extents[1]*extents[1];
977  }
978  }
979 
980  double previousVolume = volume;
981 
982  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  if (interrupter) interrupter->start("Computing voxel size");
991 
992  while (voxelCount > previousVoxelCount)
993  {
994  math::Transform::Ptr newTransform;
995 
996  if (!math::isIdentity(transform))
997  {
998  // if using a custom transform, pre-scale by coefficients
999  // which define the new voxel size
1000 
1001  math::Mat4d matrix(transform);
1002  matrix.preScale(Vec3d(voxelSize) / math::getScale(matrix));
1003  newTransform = math::Transform::createLinearTransform(matrix);
1004  }
1005  else
1006  {
1007  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  MaskGrid::Ptr mask = createGrid<MaskGrid>(false);
1015  mask->setTransform(newTransform);
1016  tools::PointsToMask<MaskGrid, InterrupterT> pointMaskOp(*mask, interrupter);
1017  pointMaskOp.template addPoints<PositionWrapper, VecT>(positions);
1018 
1019  if (interrupter && util::wasInterrupted(interrupter)) break;
1020 
1021  previousVoxelCount = voxelCount;
1022  voxelCount = mask->activeVoxelCount();
1023  volume = math::Pow3(voxelSize) * static_cast<float>(voxelCount);
1024 
1025  // stop if no change in the volume or the volume has increased
1026 
1027  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  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  if (voxelSize / previousVoxelSize > 0.9f) break;
1043  }
1044 
1045  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
Vec3< T > sorted() const
Return a vector with the components of this in ascending order.
Definition: Vec3.h:454
std::vector< Index > IndexArray
Definition: PointMove.h:161
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
void populateAttribute(PointDataTreeT &tree, const PointIndexTreeT &pointIndexTree, const openvdb::Name &attributeName, const PointArrayT &data, const Index stride=1, const bool insertMetadata=true)
Stores point attribute data in an existing PointDataGrid attribute.
Definition: PointConversion.h:755
ValueType value_type
Definition: PointConversion.h:208
Vec3T extents() const
Return the extents of this bounding box, i.e., the length along each axis.
Definition: BBox.h:253
Index64 pointCount(const PointDataTreeT &tree, const FilterT &filter=NullFilter(), const bool inCoreOnly=false, const bool threaded=true)
Count the total number of points in a PointDataTree.
Definition: PointCount.h:88
SharedPtr< Grid > Ptr
Definition: Grid.h:579
Point-partitioner compatible STL vector attribute wrapper for convenience.
Definition: PointConversion.h:205
LeafRange leafRange(size_t grainsize=1) const
Return a TBB-compatible LeafRange.
Definition: LeafManager.h:345
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
const TreeType & tree() const
Return a const reference to tree associated with this manager.
Definition: LeafManager.h:302
Space-partitioning acceleration structure for points. Partitions the points into voxels to accelerate...
#define OPENVDB_LOG_DEBUG(message)
In debug builds only, log a debugging message of the form &#39;someVar << "text" << ...&#39;.
Definition: logging.h:266
float computeVoxelSize(const PositionWrapper &positions, const uint32_t pointsPerVoxel, const math::Mat4d transform=math::Mat4d::identity(), const Index decimalPlaces=5, InterrupterT *const interrupter=nullptr)
Definition: PointConversion.h:886
SharedPtr< Transform > Ptr
Definition: Transform.h:42
const Vec3T & min() const
Return a const reference to the minimum point of this bounding box.
Definition: BBox.h:62
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:499
PointAttributeVector(const std::vector< value_type > &data, const Index stride=1)
Definition: PointConversion.h:210
bool isIdentity(const MatType &m)
Determine if a matrix is an identity matrix.
Definition: Mat.h:882
void getPos(size_t n, ValueType &xyz) const
Definition: PointConversion.h:216
Vec3d worldToIndex(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:110
Type Pow(Type x, int n)
Return xn.
Definition: Math.h:564
math::BBox< Vec3d > BBoxd
Definition: Types.h:84
Definition: AttributeArrayString.h:186
bool isApproxZero(const Type &x)
Return true if x is equal to zero to within the default floating-point comparison tolerance...
Definition: Math.h:350
Definition: Transform.h:39
Definition: AttributeArrayString.h:153
Definition: Exceptions.h:65
Base class for storing attribute data.
Definition: AttributeArray.h:92
Makes every voxel of a grid active if it contains a point.
Definition: PointsToMask.h:71
Definition: Exceptions.h:59
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
Point attribute manipulation in a VDB Point Grid.
Point group manipulation in a VDB Point Grid.
void convertPointDataGridGroup(Group &group, const PointDataTreeT &tree, const std::vector< Index64 > &pointOffsets, const Index64 startOffset, const AttributeSet::Descriptor::GroupIndex index, const FilterT &filter=NullFilter(), const bool inCoreOnly=false)
Convert the group from a PointDataGrid.
Definition: PointConversion.h:858
Definition: Types.h:204
Index filters primarily designed to be used with a FilterIndexIter.
Base class for storing metadata information in a grid.
Definition: Metadata.h:23
MatType unit(const MatType &mat, typename MatType::value_type eps=1.0e-8)
Return a copy of the given matrix with its upper 3×3 rows normalized.
Definition: Mat.h:670
ValueType PosType
Definition: PointConversion.h:207
Index64 pointOffsets(std::vector< Index64 > &pointOffsets, const PointDataTreeT &tree, const FilterT &filter=NullFilter(), const bool inCoreOnly=false, const bool threaded=true)
Populate an array of cumulative point offsets per leaf node.
Definition: PointCount.h:122
Type Pow3(Type x)
Return x3.
Definition: Math.h:555
void convertPointDataGridAttribute(TypedAttribute &attribute, const PointDataTreeT &tree, const std::vector< Index64 > &pointOffsets, const Index64 startOffset, const unsigned arrayIndex, const Index stride=1, const FilterT &filter=NullFilter(), const bool inCoreOnly=false)
Convert the attribute from a PointDataGrid.
Definition: PointConversion.h:826
const Vec3T & max() const
Return a const reference to the maximum point of this bounding box.
Definition: BBox.h:64
std::string Name
Definition: Name.h:17
math::Vec3< Real > Vec3R
Definition: Types.h:72
Vec3< T > reversed() const
Return the vector (z, y, x)
Definition: Vec3.h:464
uint8_t GroupType
Definition: AttributeSet.h:31
uint64_t Index64
Definition: Types.h:53
Definition: Exceptions.h:13
ValueT value
Definition: GridBuilder.h:1287
This tool produces a grid where every voxel that contains a point is active. It employs thread-local ...
Definition: IndexIterator.h:43
This class manages a linear array of pointers to a given tree&#39;s leaf nodes, as well as optional auxil...
Definition: LeafManager.h:84
Index32 Index
Definition: Types.h:54
bool isUniform() const override
Return true if this array is stored as a single uniform value.
Definition: AttributeArray.h:679
ValueType get(Index n) const
Return the value at index n.
Definition: AttributeArray.h:1393
PointDataGridT::Ptr createPointDataGrid(const std::vector< ValueT > &positions, const math::Transform &xform, const Metadata *positionDefaultValue=nullptr)
Convenience method to create a PointDataGrid from a std::vector of point positions.
Definition: PointConversion.h:737
OPENVDB_API void calculateBounds(const Transform &t, const Vec3d &minWS, const Vec3d &maxWS, Vec3d &minIS, Vec3d &maxIS)
Calculate an axis-aligned bounding box in index space from an axis-aligned bounding box in world spac...
void void_t
Definition: PointConversion.h:168
Write-able version of AttributeHandle.
Definition: AttributeArray.h:881
Vec3< typename MatType::value_type > getScale(const MatType &mat)
Return a Vec3 representing the lengths of the passed matrix&#39;s upper 3×3&#39;s rows.
Definition: Mat.h:655
bool isGroup(const AttributeArray &array)
Definition: AttributeGroup.h:63
Definition: AttributeArray.h:810
Ptr copy() const
Definition: Transform.h:50
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
std::pair< Name, Name > NamePair
Definition: AttributeArray.h:39
void convertPointDataGridPosition(PositionAttribute &positionAttribute, const PointDataGridT &grid, const std::vector< Index64 > &pointOffsets, const Index64 startOffset, const FilterT &filter=NullFilter(), const bool inCoreOnly=false)
Convert the position attribute from a Point Data Grid.
Definition: PointConversion.h:792
Attribute array storage for string data using Descriptor Metadata.
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:508
ElementType volume() const
Return the volume enclosed by this bounding box.
Definition: BBox.h:100
size_t size() const
Definition: PointConversion.h:215
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
Typed class for storing attribute data.
Definition: AttributeArray.h:532
Set of Attribute Arrays which tracks metadata about each array.
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:750
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:517
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202
Definition: PointConversion.h:170
Type Truncate(Type x, unsigned int digits)
Return x truncated to the given number of decimal digits.
Definition: Math.h:873