OpenVDB  12.0.0
PointMaskImpl.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-2.0
3 
4 /// @file PointMaskImpl.h
5 ///
6 /// @author Dan Bailey
7 ///
8 
9 #ifndef OPENVDB_POINTS_POINT_MASK_IMPL_HAS_BEEN_INCLUDED
10 #define OPENVDB_POINTS_POINT_MASK_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_mask_internal {
20 
21 template <typename LeafT>
22 void voxelSum(LeafT& leaf, const Index offset, const typename LeafT::ValueType& value)
23 {
24  leaf.modifyValue(offset, tools::valxform::SumOp<typename LeafT::ValueType>(value));
25 }
26 
27 // overload PointDataLeaf access to use setOffsetOn(), as modifyValue()
28 // is intentionally disabled to avoid accidental usage
29 
30 template <typename T, Index Log2Dim>
31 void voxelSum(PointDataLeafNode<T, Log2Dim>& leaf, const Index offset,
32  const typename PointDataLeafNode<T, Log2Dim>::ValueType& value)
33 {
34  leaf.setOffsetOn(offset, leaf.getValue(offset) + value);
35 }
36 
37 
38 /// @brief Combines multiple grids into one by stealing leaf nodes and summing voxel values
39 /// This class is designed to work with thread local storage containers such as tbb::combinable
40 template<typename GridT>
41 struct GridCombinerOp
42 {
43  using CombinableT = typename tbb::combinable<GridT>;
44 
45  using TreeT = typename GridT::TreeType;
46  using LeafT = typename TreeT::LeafNodeType;
47  using ValueType = typename TreeT::ValueType;
48  using SumOp = tools::valxform::SumOp<typename TreeT::ValueType>;
49 
50  GridCombinerOp(GridT& grid)
51  : mTree(grid.tree()) {}
52 
53  void operator()(const GridT& grid)
54  {
55  for (auto leaf = grid.tree().beginLeaf(); leaf; ++leaf) {
56  auto* newLeaf = mTree.probeLeaf(leaf->origin());
57  if (!newLeaf) {
58  // if the leaf doesn't yet exist in the new tree, steal it
59  auto& tree = const_cast<GridT&>(grid).tree();
60  mTree.addLeaf(tree.template stealNode<LeafT>(leaf->origin(),
61  zeroVal<ValueType>(), false));
62  }
63  else {
64  // otherwise increment existing values
65  for (auto iter = leaf->cbeginValueOn(); iter; ++iter) {
66  voxelSum(*newLeaf, iter.offset(), ValueType(*iter));
67  }
68  }
69  }
70  }
71 
72 private:
73  TreeT& mTree;
74 }; // struct GridCombinerOp
75 
76 
77 /// @brief Compute scalar grid from PointDataGrid while evaluating the point filter
78 template <typename TreeT, typename PointDataTreeT, typename FilterT>
79 struct PointsToScalarOp
80 {
81  using LeafT = typename TreeT::LeafNodeType;
82  using ValueT = typename LeafT::ValueType;
83  // This method is also used by PointCount so ValueT may not be bool
84  static constexpr bool IsBool =
85  std::is_same<ValueT, bool>::value;
86 
87  PointsToScalarOp(const PointDataTreeT& tree,
88  const FilterT& filter)
89  : mPointDataAccessor(tree)
90  , mFilter(filter) {}
91 
92  void operator()(LeafT& leaf, size_t /*idx*/) const
93  {
94  // assumes matching topology
95  const auto* const pointLeaf =
96  mPointDataAccessor.probeConstLeaf(leaf.origin());
97  OPENVDB_ASSERT(pointLeaf);
98 
99  for (auto value = leaf.beginValueOn(); value; ++value) {
100  const auto iter = pointLeaf->beginIndexVoxel(value.getCoord(), mFilter);
101  if (IsBool) {
102  if (!iter) value.setValueOn(false);
103  }
104  else {
105  const Index64 count = points::iterCount(iter);
106  if (count > Index64(0)) value.setValue(ValueT(count));
107  else value.setValueOn(false);
108  }
109  }
110  }
111 
112 private:
113  const tree::ValueAccessor<const PointDataTreeT> mPointDataAccessor;
114  const FilterT& mFilter;
115 }; // struct PointsToScalarOp
116 
117 
118 /// @brief Compute scalar grid from PointDataGrid using a different transform
119 /// and while evaluating the point filter
120 template <typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
121 struct PointsToTransformedScalarOp
122 {
123  using PointDataLeafT = typename PointDataGridT::TreeType::LeafNodeType;
124  using ValueT = typename GridT::TreeType::ValueType;
125  using HandleT = AttributeHandle<Vec3f>;
126  using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
127 
128  PointsToTransformedScalarOp(const math::Transform& targetTransform,
129  const math::Transform& sourceTransform,
130  const FilterT& filter,
131  const DeformerT& deformer,
132  CombinableT& combinable)
133  : mTargetTransform(targetTransform)
134  , mSourceTransform(sourceTransform)
135  , mFilter(filter)
136  , mDeformer(deformer)
137  , mCombinable(combinable) { }
138 
139  void operator()(const PointDataLeafT& leaf, size_t idx) const
140  {
141  DeformerT deformer(mDeformer);
142 
143  auto& grid = mCombinable.local();
144  auto& countTree = grid.tree();
145  tree::ValueAccessor<typename GridT::TreeType> accessor(countTree);
146 
147  deformer.reset(leaf, idx);
148 
149  auto handle = HandleT::create(leaf.constAttributeArray("P"));
150 
151  for (auto iter = leaf.beginIndexOn(mFilter); iter; iter++) {
152 
153  // extract index-space position
154 
155  Vec3d position = handle->get(*iter) + iter.getCoord().asVec3d();
156 
157  // if deformer is designed to be used in index-space, perform deformation prior
158  // to transforming position to world-space, otherwise perform deformation afterwards
159 
160  if (DeformerTraits<DeformerT>::IndexSpace) {
161  deformer.template apply<decltype(iter)>(position, iter);
162  position = mSourceTransform.indexToWorld(position);
163  }
164  else {
165  position = mSourceTransform.indexToWorld(position);
166  deformer.template apply<decltype(iter)>(position, iter);
167  }
168 
169  // determine coord of target grid
170 
171  const Coord ijk = mTargetTransform.worldToIndexCellCentered(position);
172 
173  // increment count in target voxel
174 
175  auto* newLeaf = accessor.touchLeaf(ijk);
176  OPENVDB_ASSERT(newLeaf);
177  voxelSum(*newLeaf, newLeaf->coordToOffset(ijk), ValueT(1));
178  }
179  }
180 
181 private:
182  const openvdb::math::Transform& mTargetTransform;
183  const openvdb::math::Transform& mSourceTransform;
184  const FilterT& mFilter;
185  const DeformerT& mDeformer;
186  CombinableT& mCombinable;
187 }; // struct PointsToTransformedScalarOp
188 
189 
190 template<typename TreeT, typename PointDataTreeT, typename FilterT>
191 inline typename TreeT::Ptr convertPointsToScalar(
192  const PointDataTreeT& points,
193  const FilterT& filter,
194  bool threaded = true)
195 {
196  using point_mask_internal::PointsToScalarOp;
197 
198  using ValueT = typename TreeT::ValueType;
199 
200  // copy the topology from the points tree
201 
202  typename TreeT::Ptr tree(new TreeT(/*background=*/false));
203  tree->topologyUnion(points);
204 
205  // early exit if no leaves
206 
207  if (points.leafCount() == 0) return tree;
208 
209  // early exit if mask and no group logic
210 
211  if (std::is_same<ValueT, bool>::value && filter.state() == index::ALL) return tree;
212 
213  // evaluate point group filters to produce a subset of the generated mask
214 
215  tree::LeafManager<TreeT> leafManager(*tree);
216 
217  if (filter.state() == index::ALL) {
218  NullFilter nullFilter;
219  PointsToScalarOp<TreeT, PointDataTreeT, NullFilter> pointsToScalarOp(
220  points, nullFilter);
221  leafManager.foreach(pointsToScalarOp, threaded);
222  } else {
223  // build mask from points in parallel only where filter evaluates to true
224  PointsToScalarOp<TreeT, PointDataTreeT, FilterT> pointsToScalarOp(
225  points, filter);
226  leafManager.foreach(pointsToScalarOp, threaded);
227  }
228 
229  return tree;
230 }
231 
232 
233 template<typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
234 inline typename GridT::Ptr convertPointsToScalar(
235  PointDataGridT& points,
236  const math::Transform& transform,
237  const FilterT& filter,
238  const DeformerT& deformer,
239  bool threaded = true)
240 {
241  using point_mask_internal::PointsToTransformedScalarOp;
242  using point_mask_internal::GridCombinerOp;
243 
244  using CombinerOpT = GridCombinerOp<GridT>;
245  using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
246 
247  typename GridT::Ptr grid = GridT::create();
248  grid->setTransform(transform.copy());
249 
250  // use the simpler method if the requested transform matches the existing one
251 
252  const math::Transform& pointsTransform = points.constTransform();
253 
254  if (transform == pointsTransform && std::is_same<NullDeformer, DeformerT>()) {
255  using TreeT = typename GridT::TreeType;
256  typename TreeT::Ptr tree =
257  convertPointsToScalar<TreeT>(points.tree(), filter, threaded);
258  grid->setTree(tree);
259  return grid;
260  }
261 
262  // early exit if no leaves
263 
264  if (points.constTree().leafCount() == 0) return grid;
265 
266  // compute mask grids in parallel using new transform
267 
268  CombinableT combiner;
269 
270  tree::LeafManager<typename PointDataGridT::TreeType> leafManager(points.tree());
271 
272  if (filter.state() == index::ALL) {
273  NullFilter nullFilter;
274  PointsToTransformedScalarOp<GridT, PointDataGridT, NullFilter, DeformerT> pointsToScalarOp(
275  transform, pointsTransform, nullFilter, deformer, combiner);
276  leafManager.foreach(pointsToScalarOp, threaded);
277  } else {
278  PointsToTransformedScalarOp<GridT, PointDataGridT, FilterT, DeformerT> pointsToScalarOp(
279  transform, pointsTransform, filter, deformer, combiner);
280  leafManager.foreach(pointsToScalarOp, threaded);
281  }
282 
283  // combine the mask grids into one
284 
285  CombinerOpT combineOp(*grid);
286  combiner.combine_each(combineOp);
287 
288  return grid;
289 }
290 
291 
292 } // namespace point_mask_internal
293 
294 /// @endcond
295 
296 ////////////////////////////////////////
297 
298 
299 template <typename PointDataTreeT, typename MaskTreeT, typename FilterT>
300 inline typename std::enable_if<std::is_base_of<TreeBase, PointDataTreeT>::value &&
301  std::is_same<typename MaskTreeT::ValueType, bool>::value, typename MaskTreeT::Ptr>::type
302 convertPointsToMask(const PointDataTreeT& tree,
303  const FilterT& filter,
304  bool threaded)
305 {
306  return point_mask_internal::convertPointsToScalar<MaskTreeT>(
307  tree, filter, threaded);
308 }
309 
310 
311 template<typename PointDataGridT, typename MaskGridT, typename FilterT>
312 inline typename std::enable_if<std::is_base_of<GridBase, PointDataGridT>::value &&
313  std::is_same<typename MaskGridT::ValueType, bool>::value, typename MaskGridT::Ptr>::type
315  const PointDataGridT& points,
316  const FilterT& filter,
317  bool threaded)
318 {
319  using PointDataTreeT = typename PointDataGridT::TreeType;
320  using MaskTreeT = typename MaskGridT::TreeType;
321 
322  typename MaskTreeT::Ptr tree =
323  convertPointsToMask<PointDataTreeT, MaskTreeT, FilterT>
324  (points.tree(), filter, threaded);
325 
326  typename MaskGridT::Ptr grid(new MaskGridT(tree));
327  grid->setTransform(points.transform().copy());
328  return grid;
329 }
330 
331 
332 template<typename PointDataGridT, typename MaskT, typename FilterT>
333 inline typename std::enable_if<std::is_same<typename MaskT::ValueType, bool>::value,
334  typename MaskT::Ptr>::type
336  const PointDataGridT& points,
337  const openvdb::math::Transform& transform,
338  const FilterT& filter,
339  bool threaded)
340 {
341  // This is safe because the PointDataGrid can only be modified by the deformer
343  auto& nonConstPoints = const_cast<typename AdapterT::NonConstGridType&>(points);
344 
345  NullDeformer deformer;
346  return point_mask_internal::convertPointsToScalar<MaskT>(
347  nonConstPoints, transform, filter, deformer, threaded);
348 }
349 
350 
351 ////////////////////////////////////////
352 
353 
354 } // namespace points
355 } // namespace OPENVDB_VERSION_NAME
356 } // namespace openvdb
357 
358 #endif // OPENVDB_POINTS_POINT_MASK_IMPL_HAS_BEEN_INCLUDED
uint64_t Index64
Definition: Types.h:53
Vec3< double > Vec3d
Definition: Vec3.h:665
Definition: IndexIterator.h:44
Index64 iterCount(const IterT &iter)
Count up the number of times the iterator can iterate.
Definition: IndexIterator.h:315
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
This adapter allows code that is templated on a Tree type to accept either a Tree type or a Grid type...
Definition: Grid.h:1059
OutGridT XformOp bool threaded
Definition: ValueTransformer.h:140
Definition: Exceptions.h:13
No-op deformer (adheres to the deformer interface documented in PointMove.h)
Definition: PointMask.h:74
std::enable_if< std::is_base_of< TreeBase, PointDataTreeT >::value &&std::is_same< typename MaskTreeT::ValueType, bool >::value, typename MaskTreeT::Ptr >::type convertPointsToMask(const PointDataTreeT &tree, const FilterT &filter=NullFilter(), bool threaded=true)
Extract a Mask Tree from a Point Data Tree.
Definition: PointMaskImpl.h:302
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:218