OpenVDB  12.0.0
PointSampleImpl.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-2.0
3 
4 /// @author Nick Avramoussis, Francisco Gochez, Dan Bailey
5 ///
6 /// @file PointSampleImpl.h
7 ///
8 
9 #ifndef OPENVDB_POINTS_POINT_SAMPLE_IMPL_HAS_BEEN_INCLUDED
10 #define OPENVDB_POINTS_POINT_SAMPLE_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_sample_internal {
20 
21 
22 template<typename FromType, typename ToType>
23 struct CompatibleTypes { enum { value = std::is_constructible<ToType, FromType>::value }; };
24 
25 // Specializations for types that can be converted from source grid to target attribute
26 template<typename T> struct CompatibleTypes<
27  T, T> { enum { value = true }; };
28 template<typename T> struct CompatibleTypes<
29  T, math::Vec2<T>> { enum { value = true }; };
30 template<typename T> struct CompatibleTypes<
31  T, math::Vec3<T>> { enum { value = true }; };
32 template<typename T> struct CompatibleTypes<
33  T, math::Vec4<T>> { enum { value = true }; };
34 template<typename T> struct CompatibleTypes<
35  math::Vec2<T>, math::Vec2<T>> { enum { value = true }; };
36 template<typename T> struct CompatibleTypes<
37  math::Vec3<T>, math::Vec3<T>> { enum { value = true }; };
38 template<typename T> struct CompatibleTypes<
39  math::Vec4<T>, math::Vec4<T>> { enum { value = true }; };
40 template<typename T0, typename T1> struct CompatibleTypes<
41  math::Vec2<T0>, math::Vec2<T1>> { enum { value = CompatibleTypes<T0, T1>::value }; };
42 template<typename T0, typename T1> struct CompatibleTypes<
43  math::Vec3<T0>, math::Vec3<T1>> { enum { value = CompatibleTypes<T0, T1>::value }; };
44 template<typename T0, typename T1> struct CompatibleTypes<
45  math::Vec4<T0>, math::Vec4<T1>> { enum { value = CompatibleTypes<T0, T1>::value }; };
46 template<typename T> struct CompatibleTypes<
47  ValueMask, T> { enum { value = CompatibleTypes<bool, T>::value }; };
48 
49 
50 // Ability to access the Order and Staggered template parameter from tools::Sampler<Order, Staggered>
51 template <typename T> struct SamplerTraits {
52  static const size_t Order = 0;
53  static const bool Staggered = false;
54 };
55 template <size_t T0, bool T1> struct SamplerTraits<tools::Sampler<T0, T1>> {
56  static const size_t Order = T0;
57  static const bool Staggered = T1;
58 };
59 
60 
61 // default sampling is incompatible, so throw an error
62 template <typename ValueT, typename SamplerT, typename AccessorT, bool Round, bool Compatible = false>
63 struct SampleWithRoundingOp
64 {
65  static inline void sample(ValueT&, const AccessorT&, const Vec3d&)
66  {
67  std::ostringstream ostr;
68  ostr << "Cannot sample a " << typeNameAsString<typename AccessorT::ValueType>()
69  << " grid on to a " << typeNameAsString<ValueT>() << " attribute";
70  OPENVDB_THROW(TypeError, ostr.str());
71  }
72 };
73 // partial specialization to handle sampling and rounding of compatible conversion
74 template <typename ValueT, typename SamplerT, typename AccessorT>
75 struct SampleWithRoundingOp<ValueT, SamplerT, AccessorT, /*Round=*/true, /*Compatible=*/true>
76 {
77  static inline void sample(ValueT& value, const AccessorT& accessor, const Vec3d& position)
78  {
79  value = ValueT(math::Round(SamplerT::sample(accessor, position)));
80  }
81 };
82 // partial specialization to handle sampling and simple casting of compatible conversion
83 template <typename ValueT, typename SamplerT, typename AccessorT>
84 struct SampleWithRoundingOp<ValueT, SamplerT, AccessorT, /*Round=*/false, /*Compatible=*/true>
85 {
86  static inline void sample(ValueT& value, const AccessorT& accessor, const Vec3d& position)
87  {
88  value = ValueT(SamplerT::sample(accessor, position));
89  }
90 };
91 
92 
93 template <typename PointDataGridT, typename SamplerT, typename FilterT, typename InterrupterT>
94 class PointDataSampler
95 {
96 public:
97  PointDataSampler(size_t order,
98  PointDataGridT& points,
99  const SamplerT& sampler,
100  const FilterT& filter,
101  InterrupterT* const interrupter,
102  const bool threaded)
103  : mOrder(order)
104  , mPoints(points)
105  , mSampler(sampler)
106  , mFilter(filter)
107  , mInterrupter(interrupter)
108  , mThreaded(threaded) { }
109 
110 private:
111  // No-op transformation
112  struct AlignedTransform
113  {
114  inline Vec3d transform(const Vec3d& position) const { return position; }
115  }; // struct AlignedTransform
116 
117  // Re-sample world-space position from source to target transforms
118  struct NonAlignedTransform
119  {
120  NonAlignedTransform(const math::Transform& source, const math::Transform& target)
121  : mSource(source)
122  , mTarget(target) { }
123 
124  inline Vec3d transform(const Vec3d& position) const
125  {
126  return mSource.worldToIndex(mTarget.indexToWorld(position));
127  }
128 
129  private:
130  const math::Transform& mSource;
131  const math::Transform& mTarget;
132  }; // struct NonAlignedTransform
133 
134  // A simple convenience wrapper that contains the source grid accessor and the sampler
135  template <typename ValueT, typename SourceGridT, typename GridSamplerT>
136  struct SamplerWrapper
137  {
138  using ValueType = ValueT;
139  using SourceValueType = typename SourceGridT::ValueType;
140  using SourceAccessorT = typename SourceGridT::ConstAccessor;
141 
142  // can only sample from a bool or mask grid using a PointSampler
143  static const bool SourceIsBool = std::is_same<SourceValueType, bool>::value ||
144  std::is_same<SourceValueType, ValueMask>::value;
145  static const bool OrderIsZero = SamplerTraits<GridSamplerT>::Order == 0;
146  static const bool IsValid = !SourceIsBool || OrderIsZero;
147 
148  SamplerWrapper(const SourceGridT& sourceGrid, const SamplerT& sampler)
149  : mAccessor(sourceGrid.getConstAccessor())
150  , mSampler(sampler) { }
151 
152  // note that creating a new accessor from the underlying tree is faster than
153  // copying an existing accessor
154  SamplerWrapper(const SamplerWrapper& other)
155  : mAccessor(other.mAccessor.tree())
156  , mSampler(other.mSampler) { }
157 
158  template <bool IsValidT = IsValid>
159  inline typename std::enable_if<IsValidT, ValueT>::type
160  sample(const Vec3d& position) const {
161  return mSampler.template sample<ValueT, GridSamplerT, SourceAccessorT>(
162  mAccessor, position);
163  }
164 
165  template <bool IsValidT = IsValid>
166  inline typename std::enable_if<!IsValidT, ValueT>::type
167  sample(const Vec3d& /*position*/) const {
168  OPENVDB_THROW(RuntimeError, "Cannot sample bool grid with BoxSampler or QuadraticSampler.");
169  }
170 
171  private:
172  SourceAccessorT mAccessor;
173  const SamplerT& mSampler;
174  }; // struct SamplerWrapper
175 
176  template <typename SamplerWrapperT, typename TransformerT>
177  inline void doSample(const SamplerWrapperT& sampleWrapper, const Index targetIndex,
178  const TransformerT& transformer)
179  {
180  using PointDataTreeT = typename PointDataGridT::TreeType;
181  using LeafT = typename PointDataTreeT::LeafNodeType;
182  using LeafManagerT = typename tree::LeafManager<PointDataTreeT>;
183 
184  const auto& filter(mFilter);
185  const auto& interrupter(mInterrupter);
186 
187  auto sampleLambda = [targetIndex, &sampleWrapper, &transformer, &filter, &interrupter](
188  LeafT& leaf, size_t /*idx*/)
189  {
190  using TargetHandleT = AttributeWriteHandle<typename SamplerWrapperT::ValueType>;
191 
192  if (util::wasInterrupted(interrupter)) {
193  thread::cancelGroupExecution();
194  return;
195  }
196 
197  SamplerWrapperT newSampleWrapper(sampleWrapper);
198  auto positionHandle = AttributeHandle<Vec3f>::create(leaf.constAttributeArray("P"));
199  auto targetHandle = TargetHandleT::create(leaf.attributeArray(targetIndex));
200  for (auto iter = leaf.beginIndexOn(filter); iter; ++iter) {
201  const Vec3d position = transformer.transform(
202  positionHandle->get(*iter) + iter.getCoord().asVec3d());
203  targetHandle->set(*iter, newSampleWrapper.sample(position));
204  }
205  };
206 
207  LeafManagerT leafManager(mPoints.tree());
208 
209  if (mInterrupter) mInterrupter->start();
210 
211  leafManager.foreach(sampleLambda, mThreaded);
212 
213  if (mInterrupter) mInterrupter->end();
214  }
215 
216  template <typename SourceGridT, typename SamplerWrapperT>
217  inline void resolveTransform(const SourceGridT& sourceGrid, const SamplerWrapperT& sampleWrapper,
218  const Index targetIndex)
219  {
220  const auto& sourceTransform = sourceGrid.constTransform();
221  const auto& pointsTransform = mPoints.constTransform();
222 
223  if (sourceTransform == pointsTransform) {
224  AlignedTransform transformer;
225  doSample(sampleWrapper, targetIndex, transformer);
226  } else {
227  NonAlignedTransform transformer(sourceTransform, pointsTransform);
228  doSample(sampleWrapper, targetIndex, transformer);
229  }
230  }
231 
232  template <typename SourceGridT, typename TargetValueT, size_t Order>
233  inline void resolveStaggered(const SourceGridT& sourceGrid, const Index targetIndex)
234  {
235  using SamplerWrapperT = SamplerWrapper<TargetValueT, SourceGridT, tools::Sampler<Order, false>>;
236  using StaggeredSamplerWrapperT = SamplerWrapper<TargetValueT, SourceGridT, tools::Sampler<Order, true>>;
237 
238  using SourceValueType = typename SourceGridT::ValueType;
239  if (VecTraits<SourceValueType>::Size == 3 && sourceGrid.getGridClass() == GRID_STAGGERED) {
240  StaggeredSamplerWrapperT sampleWrapper(sourceGrid, mSampler);
241  resolveTransform(sourceGrid, sampleWrapper, targetIndex);
242  } else {
243  SamplerWrapperT sampleWrapper(sourceGrid, mSampler);
244  resolveTransform(sourceGrid, sampleWrapper, targetIndex);
245  }
246  }
247 
248 public:
249  template <typename SourceGridT, typename TargetValueT = typename SourceGridT::ValueType>
250  inline void sample(const SourceGridT& sourceGrid, Index targetIndex)
251  {
252  using SourceValueType = typename SourceGridT::ValueType;
253  static const bool SourceIsMask = std::is_same<SourceValueType, bool>::value ||
254  std::is_same<SourceValueType, ValueMask>::value;
255 
256  if (SourceIsMask || mOrder == 0) {
257  resolveStaggered<SourceGridT, TargetValueT, 0>(sourceGrid, targetIndex);
258  } else if (mOrder == 1) {
259  resolveStaggered<SourceGridT, TargetValueT, 1>(sourceGrid, targetIndex);
260  } else if (mOrder == 2) {
261  resolveStaggered<SourceGridT, TargetValueT, 2>(sourceGrid, targetIndex);
262  }
263  }
264 
265 private:
266  size_t mOrder;
267  PointDataGridT& mPoints;
268  const SamplerT& mSampler;
269  const FilterT& mFilter;
270  InterrupterT* const mInterrupter;
271  const bool mThreaded;
272 }; // class PointDataSampler
273 
274 
275 template <typename PointDataGridT, typename ValueT>
276 struct AppendAttributeOp
277 {
278  static void append(PointDataGridT& points, const Name& attribute)
279  {
280  appendAttribute<ValueT>(points.tree(), attribute);
281  }
282 };
283 // partial specialization to disable attempts to append attribute type of DummySampleType
284 template <typename PointDataGridT>
285 struct AppendAttributeOp<PointDataGridT, DummySampleType>
286 {
287  static void append(PointDataGridT&, const Name&) { }
288 };
289 
290 } // namespace point_sample_internal
291 
292 /// @endcond
293 
294 ////////////////////////////////////////
295 
296 
297 template<typename ValueT, typename SamplerT, typename AccessorT>
298 ValueT SampleWithRounding::sample(const AccessorT& accessor, const Vec3d& position) const
299 {
300  using namespace point_sample_internal;
301  using SourceValueT = typename AccessorT::ValueType;
302  static const bool staggered = SamplerTraits<SamplerT>::Staggered;
303  static const bool compatible = CompatibleTypes</*from=*/SourceValueT, /*to=*/ValueT>::value &&
304  (!staggered || (staggered && VecTraits<SourceValueT>::Size == 3));
305  static const bool round = std::is_floating_point<SourceValueT>::value &&
306  std::is_integral<ValueT>::value;
307  ValueT value;
308  SampleWithRoundingOp<ValueT, SamplerT, AccessorT, round, compatible>::sample(
309  value, accessor, position);
310  return value;
311 }
312 
313 
314 ////////////////////////////////////////
315 
316 
317 template<typename PointDataGridT, typename SourceGridT, typename TargetValueT,
318  typename SamplerT, typename FilterT, typename InterrupterT>
319 inline void sampleGrid( size_t order,
320  PointDataGridT& points,
321  const SourceGridT& sourceGrid,
322  const Name& targetAttribute,
323  const FilterT& filter,
324  const SamplerT& sampler,
325  InterrupterT* const interrupter,
326  const bool threaded)
327 {
328  using point_sample_internal::AppendAttributeOp;
329  using point_sample_internal::PointDataSampler;
330 
331  // use the name of the grid if no target attribute name supplied
332  Name attribute(targetAttribute);
333  if (targetAttribute.empty()) {
334  attribute = sourceGrid.getName();
335  }
336 
337  // we do not allow sampling onto the "P" attribute
338  if (attribute == "P") {
339  OPENVDB_THROW(RuntimeError, "Cannot sample onto the \"P\" attribute");
340  }
341 
342  auto leaf = points.tree().cbeginLeaf();
343  if (!leaf) return;
344 
345  PointDataSampler<PointDataGridT, SamplerT, FilterT, InterrupterT> pointDataSampler(
346  order, points, sampler, filter, interrupter, threaded);
347 
348  const auto& descriptor = leaf->attributeSet().descriptor();
349  size_t targetIndex = descriptor.find(attribute);
350  const bool attributeExists = targetIndex != AttributeSet::INVALID_POS;
351 
352  if (std::is_same<TargetValueT, DummySampleType>::value) {
353  if (!attributeExists) {
354  // append attribute of source grid value type
355  appendAttribute<typename SourceGridT::ValueType>(points.tree(), attribute);
356  targetIndex = leaf->attributeSet().descriptor().find(attribute);
357  OPENVDB_ASSERT(targetIndex != AttributeSet::INVALID_POS);
358 
359  // sample using same type as source grid
360  pointDataSampler.template sample<SourceGridT>(sourceGrid, Index(targetIndex));
361  } else {
362  auto targetIdx = static_cast<Index>(targetIndex);
363  // attempt to explicitly sample using type of existing attribute
364  const Name& targetType = descriptor.valueType(targetIndex);
365  if (targetType == typeNameAsString<Vec3f>()) {
366  pointDataSampler.template sample<SourceGridT, Vec3f>(sourceGrid, targetIdx);
367  } else if (targetType == typeNameAsString<Vec3d>()) {
368  pointDataSampler.template sample<SourceGridT, Vec3d>(sourceGrid, targetIdx);
369  } else if (targetType == typeNameAsString<Vec3i>()) {
370  pointDataSampler.template sample<SourceGridT, Vec3i>(sourceGrid, targetIdx);
371  } else if (targetType == typeNameAsString<int8_t>()) {
372  pointDataSampler.template sample<SourceGridT, int8_t>(sourceGrid, targetIdx);
373  } else if (targetType == typeNameAsString<int16_t>()) {
374  pointDataSampler.template sample<SourceGridT, int16_t>(sourceGrid, targetIdx);
375  } else if (targetType == typeNameAsString<int32_t>()) {
376  pointDataSampler.template sample<SourceGridT, int32_t>(sourceGrid, targetIdx);
377  } else if (targetType == typeNameAsString<int64_t>()) {
378  pointDataSampler.template sample<SourceGridT, int64_t>(sourceGrid, targetIdx);
379  } else if (targetType == typeNameAsString<float>()) {
380  pointDataSampler.template sample<SourceGridT, float>(sourceGrid, targetIdx);
381  } else if (targetType == typeNameAsString<double>()) {
382  pointDataSampler.template sample<SourceGridT, double>(sourceGrid, targetIdx);
383  } else if (targetType == typeNameAsString<bool>()) {
384  pointDataSampler.template sample<SourceGridT, bool>(sourceGrid, targetIdx);
385  } else {
386  std::ostringstream ostr;
387  ostr << "Cannot sample attribute of type - " << targetType;
388  OPENVDB_THROW(TypeError, ostr.str());
389  }
390  }
391  } else {
392  if (!attributeExists) {
393  // append attribute of target value type
394  // (point_sample_internal wrapper disables the ability to use DummySampleType)
395  AppendAttributeOp<PointDataGridT, TargetValueT>::append(points, attribute);
396  targetIndex = leaf->attributeSet().descriptor().find(attribute);
397  OPENVDB_ASSERT(targetIndex != AttributeSet::INVALID_POS);
398  }
399  else {
400  const Name targetType = typeNameAsString<TargetValueT>();
401  const Name attributeType = descriptor.valueType(targetIndex);
402  if (targetType != attributeType) {
403  std::ostringstream ostr;
404  ostr << "Requested attribute type " << targetType << " for sampling "
405  << " does not match existing attribute type " << attributeType;
406  OPENVDB_THROW(TypeError, ostr.str());
407  }
408  }
409 
410  // sample using target value type
411  pointDataSampler.template sample<SourceGridT, TargetValueT>(
412  sourceGrid, static_cast<Index>(targetIndex));
413  }
414 }
415 
416 template<typename PointDataGridT, typename SourceGridT, typename FilterT, typename InterrupterT>
417 inline void pointSample(PointDataGridT& points,
418  const SourceGridT& sourceGrid,
419  const Name& targetAttribute,
420  const FilterT& filter,
421  InterrupterT* const interrupter)
422 {
423  SampleWithRounding sampler;
424  sampleGrid(/*order=*/0, points, sourceGrid, targetAttribute, filter, sampler, interrupter);
425 }
426 
427 template<typename PointDataGridT, typename SourceGridT, typename FilterT, typename InterrupterT>
428 inline void boxSample( PointDataGridT& points,
429  const SourceGridT& sourceGrid,
430  const Name& targetAttribute,
431  const FilterT& filter,
432  InterrupterT* const interrupter)
433 {
434  SampleWithRounding sampler;
435  sampleGrid(/*order=*/1, points, sourceGrid, targetAttribute, filter, sampler, interrupter);
436 }
437 
438 template<typename PointDataGridT, typename SourceGridT, typename FilterT, typename InterrupterT>
439 inline void quadraticSample(PointDataGridT& points,
440  const SourceGridT& sourceGrid,
441  const Name& targetAttribute,
442  const FilterT& filter,
443  InterrupterT* const interrupter)
444 {
445  SampleWithRounding sampler;
446  sampleGrid(/*order=*/2, points, sourceGrid, targetAttribute, filter, sampler, interrupter);
447 }
448 
449 
450 ////////////////////////////////////////
451 
452 
453 } // namespace points
454 } // namespace OPENVDB_VERSION_NAME
455 } // namespace openvdb
456 
457 #endif // OPENVDB_POINTS_POINT_SAMPLE_IMPL_HAS_BEEN_INCLUDED
const char * typeNameAsString< double >()
Definition: Types.h:521
void sampleGrid(size_t order, PointDataGridT &points, const SourceGridT &sourceGrid, const Name &targetAttribute, const FilterT &filter=NullFilter(), const SamplerT &sampler=SampleWithRounding(), InterrupterT *const interrupter=nullptr, const bool threaded=true)
Performs sampling and conversion from a VDB grid onto a VDB Points attribute.
Definition: PointSampleImpl.h:319
const char * typeNameAsString< Vec3i >()
Definition: Types.h:534
void quadraticSample(PointDataGridT &points, const SourceGridT &sourceGrid, const Name &targetAttribute="", const FilterT &filter=NullFilter(), InterrupterT *const interrupter=nullptr)
Performs tri-quadratic sampling from a VDB grid onto a VDB Points attribute.
Definition: PointSampleImpl.h:439
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
void boxSample(PointDataGridT &points, const SourceGridT &sourceGrid, const Name &targetAttribute="", const FilterT &filter=NullFilter(), InterrupterT *const interrupter=nullptr)
Performs tri-linear sampling from a VDB grid onto a VDB Points attribute.
Definition: PointSampleImpl.h:428
const char * typeNameAsString< int32_t >()
Definition: Types.h:526
const char * typeNameAsString< int64_t >()
Definition: Types.h:528
static const int Size
Definition: Types.h:246
Index32 Index
Definition: Types.h:54
Vec3< double > Vec3d
Definition: Vec3.h:665
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
const char * typeNameAsString< int8_t >()
Definition: Types.h:522
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
OutGridT XformOp bool threaded
Definition: ValueTransformer.h:140
Definition: Exceptions.h:13
Definition: PointSample.h:85
const char * typeNameAsString< bool >()
Definition: Types.h:517
const char * typeNameAsString< float >()
Definition: Types.h:520
Definition: Exceptions.h:64
Definition: Types.h:243
std::string Name
Definition: Name.h:19
const char * typeNameAsString< int16_t >()
Definition: Types.h:524
float Round(float x)
Return x rounded to the nearest integer.
Definition: Math.h:819
Definition: Types.h:457
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
const char * typeNameAsString< Vec3d >()
Definition: Types.h:536
const char * typeNameAsString< Vec3f >()
Definition: Types.h:535
void pointSample(PointDataGridT &points, const SourceGridT &sourceGrid, const Name &targetAttribute="", const FilterT &filter=NullFilter(), InterrupterT *const interrupter=nullptr)
Performs closest point sampling from a VDB grid onto a VDB Points attribute.
Definition: PointSampleImpl.h:417
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218
Definition: Exceptions.h:63