OpenVDB  11.0.0
PointConversionImpl.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 PointConversionImpl.h
7 ///
8 
9 #ifndef OPENVDB_POINTS_POINT_CONVERSION_IMPL_HAS_BEEN_INCLUDED
10 #define OPENVDB_POINTS_POINT_CONVERSION_IMPL_HAS_BEEN_INCLUDED
11 
12 namespace openvdb {
14 namespace OPENVDB_VERSION_NAME {
15 namespace points {
16 
17 /// @cond OPENVDB_DOCS_INTERNAL
18 
19 namespace point_conversion_internal {
20 
21 
22 // ConversionTraits to create the relevant Attribute Handles from a LeafNode
23 template <typename T> struct ConversionTraits
24 {
25  using Handle = AttributeHandle<T, UnknownCodec>;
26  using WriteHandle = AttributeWriteHandle<T, UnknownCodec>;
27  static T zero() { return zeroVal<T>(); }
28  template <typename LeafT>
29  static std::unique_ptr<Handle> handleFromLeaf(const LeafT& leaf, const Index index) {
30  const AttributeArray& array = leaf.constAttributeArray(index);
31  return std::make_unique<Handle>(array);
32  }
33  template <typename LeafT>
34  static std::unique_ptr<WriteHandle> writeHandleFromLeaf(LeafT& leaf, const Index index) {
35  AttributeArray& array = leaf.attributeArray(index);
36  return std::make_unique<WriteHandle>(array);
37  }
38 }; // ConversionTraits
39 template <> struct ConversionTraits<openvdb::Name>
40 {
41  using Handle = StringAttributeHandle;
42  using WriteHandle = StringAttributeWriteHandle;
43  static openvdb::Name zero() { return ""; }
44  template <typename LeafT>
45  static std::unique_ptr<Handle> handleFromLeaf(const LeafT& leaf, const Index index) {
46  const AttributeArray& array = leaf.constAttributeArray(index);
47  const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor();
48  return std::make_unique<Handle>(array, descriptor.getMetadata());
49  }
50  template <typename LeafT>
51  static std::unique_ptr<WriteHandle> writeHandleFromLeaf(LeafT& leaf, const Index index) {
52  AttributeArray& array = leaf.attributeArray(index);
53  const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor();
54  return std::make_unique<WriteHandle>(array, descriptor.getMetadata());
55  }
56 }; // ConversionTraits<openvdb::Name>
57 
58 template< typename PointDataTreeType,
59  typename PointIndexTreeType,
60  typename AttributeListType>
61 struct PopulateAttributeOp {
62 
63  using LeafManagerT = typename tree::LeafManager<PointDataTreeType>;
64  using LeafRangeT = typename LeafManagerT::LeafRange;
65  using PointIndexLeafNode = typename PointIndexTreeType::LeafNodeType;
67  using ValueType = typename AttributeListType::value_type;
68  using HandleT = typename ConversionTraits<ValueType>::WriteHandle;
69 
70  PopulateAttributeOp(const PointIndexTreeType& pointIndexTree,
71  const AttributeListType& data,
72  const size_t index,
73  const Index stride = 1)
74  : mPointIndexTree(pointIndexTree)
75  , mData(data)
76  , mIndex(index)
77  , mStride(stride) { }
78 
79  void operator()(const typename LeafManagerT::LeafRange& range) const {
80 
81  for (auto leaf = range.begin(); leaf; ++leaf) {
82 
83  // obtain the PointIndexLeafNode (using the origin of the current leaf)
84 
85  const PointIndexLeafNode* pointIndexLeaf =
86  mPointIndexTree.probeConstLeaf(leaf->origin());
87 
88  if (!pointIndexLeaf) continue;
89 
90  auto attributeWriteHandle =
91  ConversionTraits<ValueType>::writeHandleFromLeaf(*leaf, static_cast<Index>(mIndex));
92 
93  Index64 index = 0;
94 
95  const IndexArray& indices = pointIndexLeaf->indices();
96 
97  for (const Index64 leafIndex: indices)
98  {
99  ValueType value;
100  for (Index i = 0; i < mStride; i++) {
101  mData.get(value, leafIndex, i);
102  attributeWriteHandle->set(static_cast<Index>(index), i, value);
103  }
104  index++;
105  }
106 
107  // attempt to compact the array
108 
109  attributeWriteHandle->compact();
110  }
111  }
112 
113  //////////
114 
115  const PointIndexTreeType& mPointIndexTree;
116  const AttributeListType& mData;
117  const size_t mIndex;
118  const Index mStride;
119 };
120 
121 template<typename PointDataTreeType, typename Attribute, typename FilterT>
122 struct ConvertPointDataGridPositionOp {
123 
124  using LeafNode = typename PointDataTreeType::LeafNodeType;
125  using ValueType = typename Attribute::ValueType;
126  using HandleT = typename Attribute::Handle;
127  using SourceHandleT = AttributeHandle<ValueType>;
128  using LeafManagerT = typename tree::LeafManager<const PointDataTreeType>;
129  using LeafRangeT = typename LeafManagerT::LeafRange;
130 
131  ConvertPointDataGridPositionOp( Attribute& attribute,
132  const std::vector<Index64>& pointOffsets,
133  const Index64 startOffset,
134  const math::Transform& transform,
135  const size_t index,
136  const FilterT& filter,
137  const bool inCoreOnly)
138  : mAttribute(attribute)
139  , mPointOffsets(pointOffsets)
140  , mStartOffset(startOffset)
141  , mTransform(transform)
142  , mIndex(index)
143  , mFilter(filter)
144  , mInCoreOnly(inCoreOnly)
145  {
146  // only accept Vec3f as ValueType
147  static_assert(VecTraits<ValueType>::Size == 3 &&
148  std::is_floating_point<typename ValueType::ValueType>::value,
149  "ValueType is not Vec3f");
150  }
151 
152  template <typename IterT>
153  void convert(IterT& iter, HandleT& targetHandle,
154  SourceHandleT& sourceHandle, Index64& offset) const
155  {
156  for (; iter; ++iter) {
157  const Vec3d xyz = iter.getCoord().asVec3d();
158  const Vec3d pos = sourceHandle.get(*iter);
159  targetHandle.set(static_cast<Index>(offset++), /*stride=*/0,
160  mTransform.indexToWorld(pos + xyz));
161  }
162  }
163 
164  void operator()(const LeafRangeT& range) const
165  {
166  HandleT pHandle(mAttribute);
167 
168  for (auto leaf = range.begin(); leaf; ++leaf) {
169 
170  assert(leaf.pos() < mPointOffsets.size());
171 
172  if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue;
173 
174  Index64 offset = mStartOffset;
175 
176  if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1];
177 
178  auto handle = SourceHandleT::create(leaf->constAttributeArray(mIndex));
179 
180  if (mFilter.state() == index::ALL) {
181  auto iter = leaf->beginIndexOn();
182  convert(iter, pHandle, *handle, offset);
183  }
184  else {
185  auto iter = leaf->beginIndexOn(mFilter);
186  convert(iter, pHandle, *handle, offset);
187  }
188  }
189  }
190 
191  //////////
192 
193  Attribute& mAttribute;
194  const std::vector<Index64>& mPointOffsets;
195  const Index64 mStartOffset;
196  const math::Transform& mTransform;
197  const size_t mIndex;
198  const FilterT& mFilter;
199  const bool mInCoreOnly;
200 }; // ConvertPointDataGridPositionOp
201 
202 
203 template<typename PointDataTreeType, typename Attribute, typename FilterT>
204 struct ConvertPointDataGridAttributeOp {
205 
206  using LeafNode = typename PointDataTreeType::LeafNodeType;
207  using ValueType = typename Attribute::ValueType;
208  using HandleT = typename Attribute::Handle;
209  using SourceHandleT = typename ConversionTraits<ValueType>::Handle;
210  using LeafManagerT = typename tree::LeafManager<const PointDataTreeType>;
211  using LeafRangeT = typename LeafManagerT::LeafRange;
212 
213  ConvertPointDataGridAttributeOp(Attribute& attribute,
214  const std::vector<Index64>& pointOffsets,
215  const Index64 startOffset,
216  const size_t index,
217  const Index stride,
218  const FilterT& filter,
219  const bool inCoreOnly)
220  : mAttribute(attribute)
221  , mPointOffsets(pointOffsets)
222  , mStartOffset(startOffset)
223  , mIndex(index)
224  , mStride(stride)
225  , mFilter(filter)
226  , mInCoreOnly(inCoreOnly) { }
227 
228  template <typename IterT>
229  void convert(IterT& iter, HandleT& targetHandle,
230  SourceHandleT& sourceHandle, Index64& offset) const
231  {
232  if (sourceHandle.isUniform()) {
233  const ValueType uniformValue(sourceHandle.get(0));
234  for (; iter; ++iter) {
235  for (Index i = 0; i < mStride; i++) {
236  targetHandle.set(static_cast<Index>(offset), i, uniformValue);
237  }
238  offset++;
239  }
240  }
241  else {
242  for (; iter; ++iter) {
243  for (Index i = 0; i < mStride; i++) {
244  targetHandle.set(static_cast<Index>(offset), i,
245  sourceHandle.get(*iter, /*stride=*/i));
246  }
247  offset++;
248  }
249  }
250  }
251 
252  void operator()(const LeafRangeT& range) const
253  {
254  HandleT pHandle(mAttribute);
255 
256  for (auto leaf = range.begin(); leaf; ++leaf) {
257 
258  assert(leaf.pos() < mPointOffsets.size());
259 
260  if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue;
261 
262  Index64 offset = mStartOffset;
263 
264  if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1];
265 
266  auto handle = ConversionTraits<ValueType>::handleFromLeaf(
267  *leaf, static_cast<Index>(mIndex));
268 
269  if (mFilter.state() == index::ALL) {
270  auto iter = leaf->beginIndexOn();
271  convert(iter, pHandle, *handle, offset);
272  } else {
273  auto iter = leaf->beginIndexOn(mFilter);
274  convert(iter, pHandle, *handle, offset);
275  }
276  }
277  }
278 
279  //////////
280 
281  Attribute& mAttribute;
282  const std::vector<Index64>& mPointOffsets;
283  const Index64 mStartOffset;
284  const size_t mIndex;
285  const Index mStride;
286  const FilterT& mFilter;
287  const bool mInCoreOnly;
288 }; // ConvertPointDataGridAttributeOp
289 
290 template<typename PointDataTreeType, typename Group, typename FilterT>
291 struct ConvertPointDataGridGroupOp {
292 
293  using LeafNode = typename PointDataTreeType::LeafNodeType;
294  using GroupIndex = AttributeSet::Descriptor::GroupIndex;
295  using LeafManagerT = typename tree::LeafManager<const PointDataTreeType>;
296  using LeafRangeT = typename LeafManagerT::LeafRange;
297 
298  ConvertPointDataGridGroupOp(Group& group,
299  const std::vector<Index64>& pointOffsets,
300  const Index64 startOffset,
301  const AttributeSet::Descriptor::GroupIndex index,
302  const FilterT& filter,
303  const bool inCoreOnly)
304  : mGroup(group)
305  , mPointOffsets(pointOffsets)
306  , mStartOffset(startOffset)
307  , mIndex(index)
308  , mFilter(filter)
309  , mInCoreOnly(inCoreOnly) { }
310 
311  template <typename IterT>
312  void convert(IterT& iter, const GroupAttributeArray& groupArray, Index64& offset) const
313  {
314  const auto bitmask = static_cast<GroupType>(1 << mIndex.second);
315 
316  if (groupArray.isUniform()) {
317  if (groupArray.get(0) & bitmask) {
318  for (; iter; ++iter) {
319  mGroup.setOffsetOn(static_cast<Index>(offset));
320  offset++;
321  }
322  }
323  }
324  else {
325  for (; iter; ++iter) {
326  if (groupArray.get(*iter) & bitmask) {
327  mGroup.setOffsetOn(static_cast<Index>(offset));
328  }
329  offset++;
330  }
331  }
332  }
333 
334  void operator()(const LeafRangeT& range) const
335  {
336  for (auto leaf = range.begin(); leaf; ++leaf) {
337 
338  assert(leaf.pos() < mPointOffsets.size());
339 
340  if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue;
341 
342  Index64 offset = mStartOffset;
343 
344  if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1];
345 
346  const AttributeArray& array = leaf->constAttributeArray(mIndex.first);
347  assert(isGroup(array));
348  const GroupAttributeArray& groupArray = GroupAttributeArray::cast(array);
349 
350  if (mFilter.state() == index::ALL) {
351  auto iter = leaf->beginIndexOn();
352  convert(iter, groupArray, offset);
353  }
354  else {
355  auto iter = leaf->beginIndexOn(mFilter);
356  convert(iter, groupArray, offset);
357  }
358  }
359  }
360 
361  //////////
362 
363  Group& mGroup;
364  const std::vector<Index64>& mPointOffsets;
365  const Index64 mStartOffset;
366  const GroupIndex mIndex;
367  const FilterT& mFilter;
368  const bool mInCoreOnly;
369 }; // ConvertPointDataGridGroupOp
370 
371 template<typename PositionArrayT, typename VecT = Vec3R>
372 struct CalculatePositionBounds
373 {
374  CalculatePositionBounds(const PositionArrayT& positions,
375  const math::Mat4d& inverse)
376  : mPositions(positions)
377  , mInverseMat(inverse)
378  , mMin(std::numeric_limits<Real>::max())
379  , mMax(-std::numeric_limits<Real>::max()) {}
380 
381  CalculatePositionBounds(const CalculatePositionBounds& other, tbb::split)
382  : mPositions(other.mPositions)
383  , mInverseMat(other.mInverseMat)
384  , mMin(std::numeric_limits<Real>::max())
385  , mMax(-std::numeric_limits<Real>::max()) {}
386 
387  void operator()(const tbb::blocked_range<size_t>& range) {
388  VecT pos;
389  for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
390  mPositions.getPos(n, pos);
391  pos = mInverseMat.transform(pos);
392  mMin = math::minComponent(mMin, pos);
393  mMax = math::maxComponent(mMax, pos);
394  }
395  }
396 
397  void join(const CalculatePositionBounds& other) {
398  mMin = math::minComponent(mMin, other.mMin);
399  mMax = math::maxComponent(mMax, other.mMax);
400  }
401 
402  BBoxd getBoundingBox() const {
403  return BBoxd(mMin, mMax);
404  }
405 
406 private:
407  const PositionArrayT& mPositions;
408  const math::Mat4d& mInverseMat;
409  VecT mMin, mMax;
410 };
411 
412 } // namespace point_conversion_internal
413 
414 /// @endcond
415 
416 ////////////////////////////////////////
417 
418 
419 template<typename CompressionT, typename PointDataGridT, typename PositionArrayT, typename PointIndexGridT>
420 inline typename PointDataGridT::Ptr
421 createPointDataGrid(const PointIndexGridT& pointIndexGrid,
422  const PositionArrayT& positions,
423  const math::Transform& xform,
424  const Metadata* positionDefaultValue)
425 {
426  using PointDataTreeT = typename PointDataGridT::TreeType;
427  using LeafT = typename PointDataTreeT::LeafNodeType;
428  using PointIndexLeafT = typename PointIndexGridT::TreeType::LeafNodeType;
429  using PointIndexT = typename PointIndexLeafT::ValueType;
430  using LeafManagerT = typename tree::LeafManager<PointDataTreeT>;
431  using PositionAttributeT = TypedAttributeArray<Vec3f, CompressionT>;
432 
433  const NamePair positionType = PositionAttributeT::attributeType();
434 
435  // construct the Tree using a topology copy of the PointIndexGrid
436 
437  const auto& pointIndexTree = pointIndexGrid.tree();
438  typename PointDataTreeT::Ptr treePtr(new PointDataTreeT(pointIndexTree));
439 
440  // create attribute descriptor from position type
441 
442  auto descriptor = AttributeSet::Descriptor::create(positionType);
443 
444  // add default value for position if provided
445 
446  if (positionDefaultValue) descriptor->setDefaultValue("P", *positionDefaultValue);
447 
448  // retrieve position index
449 
450  const size_t positionIndex = descriptor->find("P");
451  assert(positionIndex != AttributeSet::INVALID_POS);
452 
453  // acquire registry lock to avoid locking when appending attributes in parallel
454 
456 
457  // populate position attribute
458 
459  LeafManagerT leafManager(*treePtr);
460  leafManager.foreach(
461  [&](LeafT& leaf, size_t /*idx*/) {
462 
463  // obtain the PointIndexLeafNode (using the origin of the current leaf)
464 
465  const auto* pointIndexLeaf = pointIndexTree.probeConstLeaf(leaf.origin());
466  assert(pointIndexLeaf);
467 
468  // initialise the attribute storage
469 
470  Index pointCount(static_cast<Index>(pointIndexLeaf->indices().size()));
471  leaf.initializeAttributes(descriptor, pointCount, &lock);
472 
473  // create write handle for position
474 
475  auto attributeWriteHandle = AttributeWriteHandle<Vec3f, CompressionT>::create(
476  leaf.attributeArray(positionIndex));
477 
478  Index index = 0;
479 
480  const PointIndexT
481  *begin = static_cast<PointIndexT*>(nullptr),
482  *end = static_cast<PointIndexT*>(nullptr);
483 
484  // iterator over every active voxel in the point index leaf
485 
486  for (auto iter = pointIndexLeaf->cbeginValueOn(); iter; ++iter) {
487 
488  // find the voxel center
489 
490  const Coord& ijk = iter.getCoord();
491  const Vec3d& positionCellCenter(ijk.asVec3d());
492 
493  // obtain pointers for this voxel from begin to end in the indices array
494 
495  pointIndexLeaf->getIndices(ijk, begin, end);
496 
497  while (begin < end) {
498 
499  typename PositionArrayT::value_type positionWorldSpace;
500  positions.getPos(*begin, positionWorldSpace);
501 
502  // compute the index-space position and then subtract the voxel center
503 
504  const Vec3d positionIndexSpace = xform.worldToIndex(positionWorldSpace);
505  const Vec3f positionVoxelSpace(positionIndexSpace - positionCellCenter);
506 
507  attributeWriteHandle->set(index++, positionVoxelSpace);
508 
509  ++begin;
510  }
511  }
512  },
513  /*threaded=*/true);
514 
515  auto grid = PointDataGridT::create(treePtr);
516  grid->setTransform(xform.copy());
517  return grid;
518 }
519 
520 
521 ////////////////////////////////////////
522 
523 
524 template <typename CompressionT, typename PointDataGridT, typename ValueT>
525 inline typename PointDataGridT::Ptr
526 createPointDataGrid(const std::vector<ValueT>& positions,
527  const math::Transform& xform,
528  const Metadata* positionDefaultValue)
529 {
530  const PointAttributeVector<ValueT> pointList(positions);
531 
532  tools::PointIndexGrid::Ptr pointIndexGrid =
533  tools::createPointIndexGrid<tools::PointIndexGrid>(pointList, xform);
534  return createPointDataGrid<CompressionT, PointDataGridT>(
535  *pointIndexGrid, pointList, xform, positionDefaultValue);
536 }
537 
538 
539 ////////////////////////////////////////
540 
541 
542 template <typename PointDataTreeT, typename PointIndexTreeT, typename PointArrayT>
543 inline void
544 populateAttribute(PointDataTreeT& tree, const PointIndexTreeT& pointIndexTree,
545  const openvdb::Name& attributeName, const PointArrayT& data, const Index stride,
546  const bool insertMetadata)
547 {
548  using point_conversion_internal::PopulateAttributeOp;
549  using ValueType = typename PointArrayT::value_type;
550 
551  auto iter = tree.cbeginLeaf();
552 
553  if (!iter) return;
554 
555  const size_t index = iter->attributeSet().find(attributeName);
556 
557  if (index == AttributeSet::INVALID_POS) {
558  OPENVDB_THROW(KeyError, "Attribute not found to populate - " << attributeName << ".");
559  }
560 
561  if (insertMetadata) {
562  point_attribute_internal::MetadataStorage<PointDataTreeT, ValueType>::add(tree, data);
563  }
564 
565  // populate attribute
566 
567  typename tree::LeafManager<PointDataTreeT> leafManager(tree);
568 
569  PopulateAttributeOp<PointDataTreeT,
570  PointIndexTreeT,
571  PointArrayT> populate(pointIndexTree, data, index, stride);
572  tbb::parallel_for(leafManager.leafRange(), populate);
573 }
574 
575 
576 ////////////////////////////////////////
577 
578 
579 template <typename PositionAttribute, typename PointDataGridT, typename FilterT>
580 inline void
581 convertPointDataGridPosition( PositionAttribute& positionAttribute,
582  const PointDataGridT& grid,
583  const std::vector<Index64>& pointOffsets,
584  const Index64 startOffset,
585  const FilterT& filter,
586  const bool inCoreOnly)
587 {
588  using TreeType = typename PointDataGridT::TreeType;
589  using LeafManagerT = typename tree::LeafManager<const TreeType>;
590 
591  using point_conversion_internal::ConvertPointDataGridPositionOp;
592 
593  const TreeType& tree = grid.tree();
594  auto iter = tree.cbeginLeaf();
595 
596  if (!iter) return;
597 
598  const size_t positionIndex = iter->attributeSet().find("P");
599 
600  positionAttribute.expand();
601  LeafManagerT leafManager(tree);
602  ConvertPointDataGridPositionOp<TreeType, PositionAttribute, FilterT> convert(
603  positionAttribute, pointOffsets, startOffset, grid.transform(), positionIndex,
604  filter, inCoreOnly);
605  tbb::parallel_for(leafManager.leafRange(), convert);
606  positionAttribute.compact();
607 }
608 
609 
610 ////////////////////////////////////////
611 
612 
613 template <typename TypedAttribute, typename PointDataTreeT, typename FilterT>
614 inline void
615 convertPointDataGridAttribute( TypedAttribute& attribute,
616  const PointDataTreeT& tree,
617  const std::vector<Index64>& pointOffsets,
618  const Index64 startOffset,
619  const unsigned arrayIndex,
620  const Index stride,
621  const FilterT& filter,
622  const bool inCoreOnly)
623 {
624  using LeafManagerT = typename tree::LeafManager<const PointDataTreeT>;
625 
626  using point_conversion_internal::ConvertPointDataGridAttributeOp;
627 
628  auto iter = tree.cbeginLeaf();
629 
630  if (!iter) return;
631 
632  attribute.expand();
633  LeafManagerT leafManager(tree);
634  ConvertPointDataGridAttributeOp<PointDataTreeT, TypedAttribute, FilterT> convert(
635  attribute, pointOffsets, startOffset, arrayIndex, stride,
636  filter, inCoreOnly);
637  tbb::parallel_for(leafManager.leafRange(), convert);
638  attribute.compact();
639 }
640 
641 
642 ////////////////////////////////////////
643 
644 
645 template <typename Group, typename PointDataTreeT, typename FilterT>
646 inline void
648  const PointDataTreeT& tree,
649  const std::vector<Index64>& pointOffsets,
650  const Index64 startOffset,
651  const AttributeSet::Descriptor::GroupIndex index,
652  const FilterT& filter,
653  const bool inCoreOnly)
654 {
655  using LeafManagerT= typename tree::LeafManager<const PointDataTreeT>;
656 
657  using point_conversion_internal::ConvertPointDataGridGroupOp;
658 
659  auto iter = tree.cbeginLeaf();
660  if (!iter) return;
661 
662  LeafManagerT leafManager(tree);
663  ConvertPointDataGridGroupOp<PointDataTreeT, Group, FilterT> convert(
664  group, pointOffsets, startOffset, index,
665  filter, inCoreOnly);
666  tbb::parallel_for(leafManager.leafRange(), convert);
667 
668  // must call this after modifying point groups in parallel
669 
670  group.finalize();
671 }
672 
673 template<typename PositionWrapper, typename InterrupterT, typename VecT>
674 inline float
675 computeVoxelSize( const PositionWrapper& positions,
676  const uint32_t pointsPerVoxel,
677  const math::Mat4d transform,
678  const Index decimalPlaces,
679  InterrupterT* const interrupter)
680 {
681  using namespace point_conversion_internal;
682 
683  struct Local {
684 
685  static bool voxelSizeFromVolume(const double volume,
686  const size_t estimatedVoxelCount,
687  float& voxelSize)
688  {
689  // dictated by the math::ScaleMap limit
690  static const double minimumVoxelVolume(3e-15);
691  static const double maximumVoxelVolume(std::numeric_limits<float>::max());
692 
693  double voxelVolume = volume / static_cast<double>(estimatedVoxelCount);
694  bool valid = true;
695 
696  if (voxelVolume < minimumVoxelVolume) {
697  voxelVolume = minimumVoxelVolume;
698  valid = false;
699  }
700  else if (voxelVolume > maximumVoxelVolume) {
701  voxelVolume = maximumVoxelVolume;
702  valid = false;
703  }
704 
705  voxelSize = static_cast<float>(math::Pow(voxelVolume, 1.0/3.0));
706  return valid;
707  }
708 
709  static float truncate(const float voxelSize, Index decPlaces)
710  {
711  float truncatedVoxelSize = voxelSize;
712 
713  // attempt to truncate from decPlaces -> 11
714  for (int i = decPlaces; i < 11; i++) {
715  truncatedVoxelSize = static_cast<float>(math::Truncate(double(voxelSize), i));
716  if (truncatedVoxelSize != 0.0f) break;
717  }
718 
719  return truncatedVoxelSize;
720  }
721  };
722 
723  if (pointsPerVoxel == 0) OPENVDB_THROW(ValueError, "Points per voxel cannot be zero.");
724 
725  // constructed with the default voxel size as specified by openvdb interface values
726 
727  float voxelSize(0.1f);
728 
729  const size_t numPoints = positions.size();
730 
731  // return the default voxel size if we have zero or only 1 point
732 
733  if (numPoints <= 1) return voxelSize;
734 
735  size_t targetVoxelCount(numPoints / size_t(pointsPerVoxel));
736  if (targetVoxelCount == 0) targetVoxelCount++;
737 
738  // calculate the world space, transform-oriented bounding box
739 
740  math::Mat4d inverseTransform = transform.inverse();
741  inverseTransform = math::unit(inverseTransform);
742 
743  tbb::blocked_range<size_t> range(0, numPoints);
744  CalculatePositionBounds<PositionWrapper, VecT> calculateBounds(positions, inverseTransform);
745  tbb::parallel_reduce(range, calculateBounds);
746 
747  BBoxd bbox = calculateBounds.getBoundingBox();
748 
749  // return default size if points are coincident
750 
751  if (bbox.min() == bbox.max()) return voxelSize;
752 
753  double volume = bbox.volume();
754 
755  // handle points that are collinear or coplanar by expanding the volume
756 
757  if (math::isApproxZero(volume)) {
758  Vec3d extents = bbox.extents().sorted().reversed();
759  if (math::isApproxZero(extents[1])) {
760  // colinear (maxExtent^3)
761  volume = extents[0]*extents[0]*extents[0];
762  }
763  else {
764  // coplanar (maxExtent*nextMaxExtent^2)
765  volume = extents[0]*extents[1]*extents[1];
766  }
767  }
768 
769  double previousVolume = volume;
770 
771  if (!Local::voxelSizeFromVolume(volume, targetVoxelCount, voxelSize)) {
772  OPENVDB_LOG_DEBUG("Out of range, clamping voxel size.");
773  return voxelSize;
774  }
775 
776  size_t previousVoxelCount(0);
777  size_t voxelCount(1);
778 
779  if (interrupter) interrupter->start("Computing voxel size");
780 
781  while (voxelCount > previousVoxelCount)
782  {
783  math::Transform::Ptr newTransform;
784 
785  if (!math::isIdentity(transform))
786  {
787  // if using a custom transform, pre-scale by coefficients
788  // which define the new voxel size
789 
790  math::Mat4d matrix(transform);
791  matrix.preScale(Vec3d(voxelSize) / math::getScale(matrix));
792  newTransform = math::Transform::createLinearTransform(matrix);
793  }
794  else
795  {
796  newTransform = math::Transform::createLinearTransform(voxelSize);
797  }
798 
799  // create a mask grid of the points from the calculated voxel size
800  // this is the same function call as tools::createPointMask() which has
801  // been duplicated to provide an interrupter
802 
803  MaskGrid::Ptr mask = createGrid<MaskGrid>(false);
804  mask->setTransform(newTransform);
805  tools::PointsToMask<MaskGrid, InterrupterT> pointMaskOp(*mask, interrupter);
806  pointMaskOp.template addPoints<PositionWrapper, VecT>(positions);
807 
808  if (interrupter && util::wasInterrupted(interrupter)) break;
809 
810  previousVoxelCount = voxelCount;
811  voxelCount = mask->activeVoxelCount();
812  volume = math::Pow3(voxelSize) * static_cast<float>(voxelCount);
813 
814  // stop if no change in the volume or the volume has increased
815 
816  if (volume >= previousVolume) break;
817  previousVolume = volume;
818 
819  const float previousVoxelSize = voxelSize;
820 
821  // compute the new voxel size and if invalid return the previous value
822 
823  if (!Local::voxelSizeFromVolume(volume, targetVoxelCount, voxelSize)) {
824  voxelSize = previousVoxelSize;
825  break;
826  }
827 
828  // halt convergence if the voxel size has decreased by less
829  // than 10% in this iteration
830 
831  if (voxelSize / previousVoxelSize > 0.9f) break;
832  }
833 
834  if (interrupter) interrupter->end();
835 
836  // truncate the voxel size for readability and return the value
837 
838  return Local::truncate(voxelSize, decimalPlaces);
839 }
840 
841 
842 ////////////////////////////////////////
843 
844 
845 } // namespace points
846 } // namespace OPENVDB_VERSION_NAME
847 } // namespace openvdb
848 
849 #endif // OPENVDB_POINTS_POINT_CONVERSION_IMPL_HAS_BEEN_INCLUDED
bool isApproxZero(const Type &x)
Return true if x is equal to zero to within the default floating-point comparison tolerance...
Definition: Math.h:349
Definition: Exceptions.h:65
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: PointCountImpl.h:18
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: PointConversionImpl.h:675
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:504
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
Mat4< double > Mat4d
Definition: Mat4.h:1354
Definition: Exceptions.h:59
#define OPENVDB_LOG_DEBUG(message)
In debug builds only, log a debugging message of the form &#39;someVar << "text" << ...&#39;.
Definition: logging.h:266
LeafRange leafRange(size_t grainsize=1) const
Return a TBB-compatible LeafRange.
Definition: LeafManager.h:345
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: PointConversionImpl.h:544
Type Truncate(Type x, unsigned int digits)
Return x truncated to the given number of decimal digits.
Definition: Math.h:870
SharedPtr< Transform > Ptr
Definition: Transform.h:42
Definition: Coord.h:589
double Real
Definition: Types.h:60
static const int Size
Definition: Types.h:246
Definition: Mat.h:165
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: PointConversionImpl.h:647
Vec3< T > sorted() const
Return a vector with the components of this in ascending order.
Definition: Vec3.h:450
Vec3T extents() const
Return the extents of this bounding box, i.e., the length along each axis.
Definition: BBox.h:253
std::pair< Name, Name > NamePair
Definition: AttributeArray.h:39
Vec3< double > Vec3d
Definition: Vec3.h:664
uint64_t Index64
Definition: Types.h:53
Type Pow(Type x, int n)
Return xn.
Definition: Math.h:561
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: PointConversionImpl.h:581
bool isIdentity(const MatType &m)
Determine if a matrix is an identity matrix.
Definition: Mat.h:860
Base class for storing metadata information in a grid.
Definition: Metadata.h:23
Vec3d worldToIndex(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:110
Type Pow3(Type x)
Return x3.
Definition: Math.h:552
Ptr copy() const
Definition: Transform.h:50
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:736
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: PointCountImpl.h:52
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:485
std::vector< Index > IndexArray
Definition: PointMoveImpl.h:88
Definition: Exceptions.h:13
TypedAttributeArray< GroupType, GroupCodec > GroupAttributeArray
Definition: AttributeGroup.h:40
Definition: Transform.h:39
ElementType volume() const
Return the volume enclosed by this bounding box.
Definition: BBox.h:100
const TreeType & tree() const
Return a const reference to tree associated with this manager.
Definition: LeafManager.h:302
Typed class for storing attribute data.
Definition: AttributeArray.h:544
std::string Name
Definition: Name.h:19
const Vec3T & max() const
Return a const reference to the maximum point of this bounding box.
Definition: BBox.h:64
SharedPtr< Grid > Ptr
Definition: Grid.h:573
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: PointConversionImpl.h:615
uint8_t GroupType
Definition: AttributeSet.h:31
Index32 Index
Definition: Types.h:54
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:513
bool isGroup(const AttributeArray &array)
Definition: AttributeGroup.h:63
PointDataGridT::Ptr createPointDataGrid(const PointIndexGridT &pointIndexGrid, const PositionArrayT &positions, const math::Transform &xform, const Metadata *positionDefaultValue=nullptr)
Localises points with position into a PointDataGrid into two stages: allocation of the leaf attribute...
Definition: PointConversionImpl.h:421
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:648
const Vec3T & min() const
Return a const reference to the minimum point of this bounding box.
Definition: BBox.h:62
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...
Vec3< T > reversed() const
Return the vector (z, y, x)
Definition: Vec3.h:460
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:633
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
Makes every voxel of a grid active if it contains a point.
Definition: PointsToMask.h:71
math::BBox< Vec3d > BBoxd
Definition: Types.h:84
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:110
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
void split(ContainerT &out, const std::string &in, const char delim)
Definition: Name.h:43
Write-able version of AttributeHandle.
Definition: AttributeArray.h:903
Point-partitioner compatible STL vector attribute wrapper for convenience.
Definition: PointConversion.h:40
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:212