OpenVDB  9.0.1
PointMask.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file points/PointMask.h
5 ///
6 /// @author Dan Bailey
7 ///
8 /// @brief Methods for extracting masks from VDB Point grids.
9 
10 #ifndef OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED
11 #define OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED
12 
13 #include <openvdb/openvdb.h>
14 #include <openvdb/tools/ValueTransformer.h> // valxform::SumOp
15 
16 #include "PointDataGrid.h"
17 #include "IndexFilter.h"
18 
19 #include <tbb/combinable.h>
20 
21 #include <type_traits>
22 #include <vector>
23 
24 
25 namespace openvdb {
27 namespace OPENVDB_VERSION_NAME {
28 namespace points {
29 
30 
31 /// @brief Extract a Mask Grid from a Point Data Grid
32 /// @param grid the PointDataGrid to extract the mask from.
33 /// @param filter an optional index filter
34 /// @param threaded enable or disable threading (threading is enabled by default)
35 /// @note this method is only available for Bool Grids and Mask Grids
36 template <typename PointDataGridT,
37  typename MaskT = typename PointDataGridT::template ValueConverter<bool>::Type,
38  typename FilterT = NullFilter>
40  typename MaskT::Ptr>::type
41 convertPointsToMask(const PointDataGridT& grid,
42  const FilterT& filter = NullFilter(),
43  bool threaded = true);
44 
45 
46 /// @brief Extract a Mask Grid from a Point Data Grid using a new transform
47 /// @param grid the PointDataGrid to extract the mask from.
48 /// @param transform target transform for the mask.
49 /// @param filter an optional index filter
50 /// @param threaded enable or disable threading (threading is enabled by default)
51 /// @note this method is only available for Bool Grids and Mask Grids
52 template <typename PointDataGridT,
53  typename MaskT = typename PointDataGridT::template ValueConverter<bool>::Type,
54  typename FilterT = NullFilter>
55 inline typename std::enable_if<std::is_same<typename MaskT::ValueType, bool>::value,
56  typename MaskT::Ptr>::type
57 convertPointsToMask(const PointDataGridT& grid,
58  const openvdb::math::Transform& transform,
59  const FilterT& filter = NullFilter(),
60  bool threaded = true);
61 
62 
63 /// @brief No-op deformer (adheres to the deformer interface documented in PointMove.h)
65 {
66  template <typename LeafT>
67  void reset(LeafT&, size_t /*idx*/ = 0) { }
68 
69  template <typename IterT>
70  void apply(Vec3d&, IterT&) const { }
71 };
72 
73 /// @brief Deformer Traits for optionally configuring deformers to be applied
74 /// in index-space. The default is world-space.
75 template <typename DeformerT>
77 {
78  static const bool IndexSpace = false;
79 };
80 
81 
82 ////////////////////////////////////////
83 
84 /// @cond OPENVDB_DOCS_INTERNAL
85 
86 namespace point_mask_internal {
87 
88 template <typename LeafT>
89 void voxelSum(LeafT& leaf, const Index offset, const typename LeafT::ValueType& value)
90 {
91  leaf.modifyValue(offset, tools::valxform::SumOp<typename LeafT::ValueType>(value));
92 }
93 
94 // overload PointDataLeaf access to use setOffsetOn(), as modifyValue()
95 // is intentionally disabled to avoid accidental usage
96 
97 template <typename T, Index Log2Dim>
98 void voxelSum(PointDataLeafNode<T, Log2Dim>& leaf, const Index offset,
99  const typename PointDataLeafNode<T, Log2Dim>::ValueType& value)
100 {
101  leaf.setOffsetOn(offset, leaf.getValue(offset) + value);
102 }
103 
104 
105 /// @brief Combines multiple grids into one by stealing leaf nodes and summing voxel values
106 /// This class is designed to work with thread local storage containers such as tbb::combinable
107 template<typename GridT>
108 struct GridCombinerOp
109 {
110  using CombinableT = typename tbb::combinable<GridT>;
111 
112  using TreeT = typename GridT::TreeType;
113  using LeafT = typename TreeT::LeafNodeType;
114  using ValueType = typename TreeT::ValueType;
116 
117  GridCombinerOp(GridT& grid)
118  : mTree(grid.tree()) {}
119 
120  void operator()(const GridT& grid)
121  {
122  for (auto leaf = grid.tree().beginLeaf(); leaf; ++leaf) {
123  auto* newLeaf = mTree.probeLeaf(leaf->origin());
124  if (!newLeaf) {
125  // if the leaf doesn't yet exist in the new tree, steal it
126  auto& tree = const_cast<GridT&>(grid).tree();
127  mTree.addLeaf(tree.template stealNode<LeafT>(leaf->origin(),
128  zeroVal<ValueType>(), false));
129  }
130  else {
131  // otherwise increment existing values
132  for (auto iter = leaf->cbeginValueOn(); iter; ++iter) {
133  voxelSum(*newLeaf, iter.offset(), ValueType(*iter));
134  }
135  }
136  }
137  }
138 
139 private:
140  TreeT& mTree;
141 }; // struct GridCombinerOp
142 
143 
144 /// @brief Compute scalar grid from PointDataGrid while evaluating the point filter
145 template <typename GridT, typename PointDataGridT, typename FilterT>
146 struct PointsToScalarOp
147 {
148  using LeafT = typename GridT::TreeType::LeafNodeType;
149  using ValueT = typename LeafT::ValueType;
150 
151  PointsToScalarOp( const PointDataGridT& grid,
152  const FilterT& filter)
153  : mPointDataAccessor(grid.getConstAccessor())
154  , mFilter(filter) { }
155 
156  void operator()(LeafT& leaf, size_t /*idx*/) const {
157 
158  const auto* const pointLeaf =
159  mPointDataAccessor.probeConstLeaf(leaf.origin());
160 
161  // assumes matching topology
162  assert(pointLeaf);
163 
164  for (auto value = leaf.beginValueOn(); value; ++value) {
165  const Index64 count = points::iterCount(
166  pointLeaf->beginIndexVoxel(value.getCoord(), mFilter));
167  if (count > Index64(0)) {
168  value.setValue(ValueT(count));
169  } else {
170  // disable any empty voxels
171  value.setValueOn(false);
172  }
173  }
174  }
175 
176 private:
177  const typename PointDataGridT::ConstAccessor mPointDataAccessor;
178  const FilterT& mFilter;
179 }; // struct PointsToScalarOp
180 
181 
182 /// @brief Compute scalar grid from PointDataGrid using a different transform
183 /// and while evaluating the point filter
184 template <typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
185 struct PointsToTransformedScalarOp
186 {
187  using PointDataLeafT = typename PointDataGridT::TreeType::LeafNodeType;
188  using ValueT = typename GridT::TreeType::ValueType;
189  using HandleT = AttributeHandle<Vec3f>;
190  using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
191 
192  PointsToTransformedScalarOp(const math::Transform& targetTransform,
193  const math::Transform& sourceTransform,
194  const FilterT& filter,
195  const DeformerT& deformer,
196  CombinableT& combinable)
197  : mTargetTransform(targetTransform)
198  , mSourceTransform(sourceTransform)
199  , mFilter(filter)
200  , mDeformer(deformer)
201  , mCombinable(combinable) { }
202 
203  void operator()(const PointDataLeafT& leaf, size_t idx) const
204  {
205  DeformerT deformer(mDeformer);
206 
207  auto& grid = mCombinable.local();
208  auto& countTree = grid.tree();
210 
211  deformer.reset(leaf, idx);
212 
213  auto handle = HandleT::create(leaf.constAttributeArray("P"));
214 
215  for (auto iter = leaf.beginIndexOn(mFilter); iter; iter++) {
216 
217  // extract index-space position
218 
219  Vec3d position = handle->get(*iter) + iter.getCoord().asVec3d();
220 
221  // if deformer is designed to be used in index-space, perform deformation prior
222  // to transforming position to world-space, otherwise perform deformation afterwards
223 
225  deformer.template apply<decltype(iter)>(position, iter);
226  position = mSourceTransform.indexToWorld(position);
227  }
228  else {
229  position = mSourceTransform.indexToWorld(position);
230  deformer.template apply<decltype(iter)>(position, iter);
231  }
232 
233  // determine coord of target grid
234 
235  const Coord ijk = mTargetTransform.worldToIndexCellCentered(position);
236 
237  // increment count in target voxel
238 
239  auto* newLeaf = accessor.touchLeaf(ijk);
240  assert(newLeaf);
241  voxelSum(*newLeaf, newLeaf->coordToOffset(ijk), ValueT(1));
242  }
243  }
244 
245 private:
246  const openvdb::math::Transform& mTargetTransform;
247  const openvdb::math::Transform& mSourceTransform;
248  const FilterT& mFilter;
249  const DeformerT& mDeformer;
250  CombinableT& mCombinable;
251 }; // struct PointsToTransformedScalarOp
252 
253 
254 template<typename GridT, typename PointDataGridT, typename FilterT>
255 inline typename GridT::Ptr convertPointsToScalar(
256  const PointDataGridT& points,
257  const FilterT& filter,
258  bool threaded = true)
259 {
260  using point_mask_internal::PointsToScalarOp;
261 
262  using GridTreeT = typename GridT::TreeType;
263  using ValueT = typename GridTreeT::ValueType;
264 
265  // copy the topology from the points grid
266 
267  typename GridTreeT::Ptr tree(new GridTreeT(points.constTree(),
268  false, openvdb::TopologyCopy()));
269  typename GridT::Ptr grid = GridT::create(tree);
270  grid->setTransform(points.transform().copy());
271 
272  // early exit if no leaves
273 
274  if (points.constTree().leafCount() == 0) return grid;
275 
276  // early exit if mask and no group logic
277 
278  if (std::is_same<ValueT, bool>::value && filter.state() == index::ALL) return grid;
279 
280  // evaluate point group filters to produce a subset of the generated mask
281 
282  tree::LeafManager<GridTreeT> leafManager(*tree);
283 
284  if (filter.state() == index::ALL) {
285  NullFilter nullFilter;
286  PointsToScalarOp<GridT, PointDataGridT, NullFilter> pointsToScalarOp(
287  points, nullFilter);
288  leafManager.foreach(pointsToScalarOp, threaded);
289  } else {
290  // build mask from points in parallel only where filter evaluates to true
291  PointsToScalarOp<GridT, PointDataGridT, FilterT> pointsToScalarOp(
292  points, filter);
293  leafManager.foreach(pointsToScalarOp, threaded);
294  }
295 
296  return grid;
297 }
298 
299 
300 template<typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
301 inline typename GridT::Ptr convertPointsToScalar(
302  PointDataGridT& points,
303  const openvdb::math::Transform& transform,
304  const FilterT& filter,
305  const DeformerT& deformer,
306  bool threaded = true)
307 {
308  using point_mask_internal::PointsToTransformedScalarOp;
309  using point_mask_internal::GridCombinerOp;
310 
311  using CombinerOpT = GridCombinerOp<GridT>;
312  using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
313 
314  // use the simpler method if the requested transform matches the existing one
315 
316  const openvdb::math::Transform& pointsTransform = points.constTransform();
317 
318  if (transform == pointsTransform && std::is_same<NullDeformer, DeformerT>()) {
319  return convertPointsToScalar<GridT>(points, filter, threaded);
320  }
321 
322  typename GridT::Ptr grid = GridT::create();
323  grid->setTransform(transform.copy());
324 
325  // early exit if no leaves
326 
327  if (points.constTree().leafCount() == 0) return grid;
328 
329  // compute mask grids in parallel using new transform
330 
331  CombinableT combiner;
332 
333  tree::LeafManager<typename PointDataGridT::TreeType> leafManager(points.tree());
334 
335  if (filter.state() == index::ALL) {
336  NullFilter nullFilter;
337  PointsToTransformedScalarOp<GridT, PointDataGridT, NullFilter, DeformerT> pointsToScalarOp(
338  transform, pointsTransform, nullFilter, deformer, combiner);
339  leafManager.foreach(pointsToScalarOp, threaded);
340  } else {
341  PointsToTransformedScalarOp<GridT, PointDataGridT, FilterT, DeformerT> pointsToScalarOp(
342  transform, pointsTransform, filter, deformer, combiner);
343  leafManager.foreach(pointsToScalarOp, threaded);
344  }
345 
346  // combine the mask grids into one
347 
348  CombinerOpT combineOp(*grid);
349  combiner.combine_each(combineOp);
350 
351  return grid;
352 }
353 
354 
355 } // namespace point_mask_internal
356 
357 /// @endcond
358 
359 ////////////////////////////////////////
360 
361 
362 template<typename PointDataGridT, typename MaskT, typename FilterT>
363 inline typename std::enable_if<std::is_same<typename MaskT::ValueType, bool>::value,
364  typename MaskT::Ptr>::type
366  const PointDataGridT& points,
367  const FilterT& filter,
368  bool threaded)
369 {
370  return point_mask_internal::convertPointsToScalar<MaskT>(
371  points, filter, threaded);
372 }
373 
374 
375 template<typename PointDataGridT, typename MaskT, typename FilterT>
376 inline typename std::enable_if<std::is_same<typename MaskT::ValueType, bool>::value,
377  typename MaskT::Ptr>::type
379  const PointDataGridT& points,
380  const openvdb::math::Transform& transform,
381  const FilterT& filter,
382  bool threaded)
383 {
384  // This is safe because the PointDataGrid can only be modified by the deformer
386  auto& nonConstPoints = const_cast<typename AdapterT::NonConstGridType&>(points);
387 
388  NullDeformer deformer;
389  return point_mask_internal::convertPointsToScalar<MaskT>(
390  nonConstPoints, transform, filter, deformer, threaded);
391 }
392 
393 
394 ////////////////////////////////////////
395 
396 
397 } // namespace points
398 } // namespace OPENVDB_VERSION_NAME
399 } // namespace openvdb
400 
401 #endif // OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED
No-op deformer (adheres to the deformer interface documented in PointMove.h)
Definition: PointMask.h:64
A no-op filter that can be used when iterating over all indices.
Definition: IndexIterator.h:50
void setValueOn(const Coord &xyz)
Definition: PointDataGrid.h:541
void foreach(const LeafOp &op, bool threaded=true, size_t grainSize=1)
Threaded method that applies a user-supplied functor to each leaf node in the LeafManager.
Definition: LeafManager.h:483
const ValueType & getValue(const Coord &xyz) const
Return the value of the voxel at the given coordinates.
Definition: LeafNode.h:1086
Definition: ValueTransformer.h:251
Definition: Transform.h:39
Deformer Traits for optionally configuring deformers to be applied in index-space. The default is world-space.
Definition: PointMask.h:76
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:644
std::enable_if< std::is_same< typename MaskT::ValueType, bool >::value, typename MaskT::Ptr >::type convertPointsToMask(const PointDataGridT &grid, const openvdb::math::Transform &transform, const FilterT &filter=NullFilter(), bool threaded=true)
Extract a Mask Grid from a Point Data Grid using a new transform.
Definition: PointMask.h:378
void reset(LeafT &, size_t=0)
Definition: PointMask.h:67
Index filters primarily designed to be used with a FilterIndexIter.
void setValue(const Coord &, const ValueType &)
Definition: PointDataGrid.h:547
ValueOnCIter cbeginValueOn() const
Definition: PointDataGrid.h:701
T ValueType
Definition: PointDataGrid.h:245
uint64_t Index64
Definition: Types.h:53
Definition: Exceptions.h:13
ValueT value
Definition: GridBuilder.h:1287
void setOffsetOn(Index offset, const ValueType &val)
Definition: PointDataGrid.h:1104
Definition: PointDataGrid.h:181
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
ValueOnCIter beginValueOn() const
Definition: PointDataGrid.h:702
LeafNodeT * touchLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains voxel (x, y, z). If no such node exists, create one, but preserve the values and active states of all voxels.
Definition: ValueAccessor.h:348
Index64 iterCount(const IterT &iter)
Count up the number of times the iterator can iterate.
Definition: IndexIterator.h:314
Definition: AttributeArray.h:810
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
Definition: ValueAccessor.h:182
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
This adapter allows code that is templated on a Tree type to accept either a Tree type or a Grid type...
Definition: Grid.h:1070
const Coord & origin() const
Return the grid index coordinates of this node&#39;s local origin.
Definition: LeafNode.h:172
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202
void apply(Vec3d &, IterT &) const
Definition: PointMask.h:70