GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/tools/Clip.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 113 141 80.1%
Functions: 10 60 16.7%
Branches: 48 267 18.0%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @file Clip.h
5 ///
6 /// @brief Functions to clip a grid against a bounding box, a camera frustum,
7 /// or another grid's active voxel topology
8
9 #ifndef OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED
10 #define OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED
11
12 #include <openvdb/Grid.h>
13 #include <openvdb/math/Math.h> // for math::isNegative()
14 #include <openvdb/math/Maps.h> // for math::NonlinearFrustumMap
15 #include <openvdb/tree/LeafManager.h>
16 #include <openvdb/points/PointDataGrid.h>
17 #include "GridTransformer.h" // for tools::resampleToMatch()
18 #include "Prune.h"
19 #include <tbb/blocked_range.h>
20 #include <tbb/parallel_reduce.h>
21 #include <type_traits> // for std::enable_if, std::is_same
22 #include <vector>
23
24
25 namespace openvdb {
26 OPENVDB_USE_VERSION_NAMESPACE
27 namespace OPENVDB_VERSION_NAME {
28 namespace tools {
29
30 /// @brief Clip the given grid against a world-space bounding box
31 /// and return a new grid containing the result.
32 /// @param grid the grid to be clipped
33 /// @param bbox a world-space bounding box
34 /// @param keepInterior if true, discard voxels that lie outside the bounding box;
35 /// if false, discard voxels that lie inside the bounding box
36 /// @warning Clipping a level set will likely produce a grid that is
37 /// no longer a valid level set.
38 template<typename GridType>
39 typename GridType::Ptr
40 clip(const GridType& grid, const BBoxd& bbox, bool keepInterior = true);
41
42 /// @brief Clip the given grid against a frustum and return a new grid containing the result.
43 /// @param grid the grid to be clipped
44 /// @param frustum a frustum map
45 /// @param keepInterior if true, discard voxels that lie outside the frustum;
46 /// if false, discard voxels that lie inside the frustum
47 /// @warning Clipping a level set will likely produce a grid that is
48 /// no longer a valid level set.
49 template<typename GridType>
50 typename GridType::Ptr
51 clip(const GridType& grid, const math::NonlinearFrustumMap& frustum, bool keepInterior = true);
52
53 /// @brief Clip a grid against the active voxels of another grid
54 /// and return a new grid containing the result.
55 /// @param grid the grid to be clipped
56 /// @param mask a grid whose active voxels form a boolean clipping mask
57 /// @param keepInterior if true, discard voxels that do not intersect the mask;
58 /// if false, discard voxels that intersect the mask
59 /// @details The mask grid need not have the same transform as the source grid.
60 /// Also, if the mask grid is a level set, consider using tools::sdfInteriorMask
61 /// to construct a new mask comprising the interior (rather than the narrow band)
62 /// of the level set.
63 /// @warning Clipping a level set will likely produce a grid that is
64 /// no longer a valid level set.
65 template<typename GridType, typename MaskTreeType>
66 typename GridType::Ptr
67 clip(const GridType& grid, const Grid<MaskTreeType>& mask, bool keepInterior = true);
68
69
70 ////////////////////////////////////////
71
72 /// @cond OPENVDB_DOCS_INTERNAL
73
74 namespace clip_internal {
75
76 // Use either MaskGrids or BoolGrids internally.
77 // (MaskGrids have a somewhat lower memory footprint.)
78 using MaskValueType = ValueMask;
79 //using MaskValueType = bool;
80
81
82 template<typename TreeT>
83 class MaskInteriorVoxels
84 {
85 public:
86 using ValueT = typename TreeT::ValueType;
87 using LeafNodeT = typename TreeT::LeafNodeType;
88
89 MaskInteriorVoxels(const TreeT& tree): mAcc(tree) {}
90
91 template<typename LeafNodeType>
92 void operator()(LeafNodeType& leaf, size_t /*leafIndex*/) const
93 {
94 const auto* refLeaf = mAcc.probeConstLeaf(leaf.origin());
95 if (refLeaf) {
96 for (auto iter = leaf.beginValueOff(); iter; ++iter) {
97 const auto pos = iter.pos();
98 leaf.setActiveState(pos, math::isNegative(refLeaf->getValue(pos)));
99 }
100 }
101 }
102
103 private:
104 tree::ValueAccessor<const TreeT> mAcc;
105 };
106
107
108 ////////////////////////////////////////
109
110
111 template<typename TreeT>
112 12 class CopyLeafNodes
113 {
114 public:
115 using MaskTreeT = typename TreeT::template ValueConverter<MaskValueType>::Type;
116 using MaskLeafManagerT = tree::LeafManager<const MaskTreeT>;
117
118 CopyLeafNodes(const TreeT&, const MaskLeafManagerT&);
119
120 void run(bool threaded = true);
121
122 typename TreeT::Ptr tree() const { return mNewTree; }
123
124 CopyLeafNodes(CopyLeafNodes&, tbb::split);
125 void operator()(const tbb::blocked_range<size_t>&);
126 12 void join(const CopyLeafNodes& rhs) { mNewTree->merge(*rhs.mNewTree); }
127
128 private:
129 const MaskTreeT* mClipMask;
130 const TreeT* mTree;
131 const MaskLeafManagerT* mLeafNodes;
132 typename TreeT::Ptr mNewTree;
133 };
134
135
136 template<typename TreeT>
137 12 CopyLeafNodes<TreeT>::CopyLeafNodes(const TreeT& tree, const MaskLeafManagerT& leafNodes)
138 : mTree(&tree)
139 , mLeafNodes(&leafNodes)
140 12 , mNewTree(new TreeT(mTree->background()))
141 {
142 }
143
144
145 template<typename TreeT>
146 24 CopyLeafNodes<TreeT>::CopyLeafNodes(CopyLeafNodes& rhs, tbb::split)
147 24 : mTree(rhs.mTree)
148 24 , mLeafNodes(rhs.mLeafNodes)
149 24 , mNewTree(new TreeT(mTree->background()))
150 {
151 }
152
153
154 template<typename TreeT>
155 void
156 12 CopyLeafNodes<TreeT>::run(bool threaded)
157 {
158
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 if (threaded) tbb::parallel_reduce(mLeafNodes->getRange(), *this);
159 else (*this)(mLeafNodes->getRange());
160 }
161
162
163 template<typename TreeT>
164 void
165 248 CopyLeafNodes<TreeT>::operator()(const tbb::blocked_range<size_t>& range)
166 {
167 tree::ValueAccessor<TreeT> acc(*mNewTree);
168
1/2
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
248 tree::ValueAccessor<const TreeT> refAcc(*mTree);
169
170
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 124 times.
496 for (auto n = range.begin(); n != range.end(); ++n) {
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
248 const auto& maskLeaf = mLeafNodes->leaf(n);
172 const auto& ijk = maskLeaf.origin();
173 const auto* refLeaf = refAcc.probeConstLeaf(ijk);
174
175
1/2
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
248 auto* newLeaf = acc.touchLeaf(ijk);
176
177
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 12 times.
248 if (refLeaf) {
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
224 for (auto it = maskLeaf.cbeginValueOn(); it; ++it) {
179 const auto pos = it.pos();
180 newLeaf->setValueOnly(pos, refLeaf->getValue(pos));
181 newLeaf->setActiveState(pos, refLeaf->isValueOn(pos));
182 }
183 } else {
184 typename TreeT::ValueType value;
185
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
24 bool isActive = refAcc.probeValue(ijk, value);
186
187
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 12 times.
180 for (auto it = maskLeaf.cbeginValueOn(); it; ++it) {
188 const auto pos = it.pos();
189
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
156 newLeaf->setValueOnly(pos, value);
190 156 newLeaf->setActiveState(pos, isActive);
191 }
192 }
193 }
194 }
195
196
197 ////////////////////////////////////////
198
199
200 struct BoolSampler
201 {
202 static const char* name() { return "bin"; }
203 static int radius() { return 2; }
204 static bool mipmap() { return false; }
205 static bool consistent() { return true; }
206
207 template<class TreeT>
208 static bool sample(const TreeT& inTree,
209 const Vec3R& inCoord, typename TreeT::ValueType& result)
210 {
211 return inTree.probeValue(Coord::floor(inCoord), result);
212 }
213 };
214
215
216 ////////////////////////////////////////
217
218
219 // Convert a grid of one type to a grid of another type
220 template<typename FromGridT, typename ToGridT>
221 struct ConvertGrid
222 {
223 using FromGridCPtrT = typename FromGridT::ConstPtr;
224 using ToGridPtrT = typename ToGridT::Ptr;
225 ToGridPtrT operator()(const FromGridCPtrT& grid) { return ToGridPtrT(new ToGridT(*grid)); }
226 };
227
228 // Partial specialization that avoids copying when
229 // the input and output grid types are the same
230 template<typename GridT>
231 struct ConvertGrid<GridT, GridT>
232 {
233 using GridCPtrT = typename GridT::ConstPtr;
234 GridCPtrT operator()(const GridCPtrT& grid) { return grid; }
235 };
236
237
238 ////////////////////////////////////////
239
240
241 // Convert a grid of arbitrary type to a mask grid with the same tree configuration
242 // and return a pointer to the new grid.
243 /// @private
244 template<typename GridT>
245 typename std::enable_if<!std::is_same<MaskValueType, typename GridT::BuildType>::value,
246 typename GridT::template ValueConverter<MaskValueType>::Type::Ptr>::type
247 8 convertToMaskGrid(const GridT& grid)
248 {
249 using MaskGridT = typename GridT::template ValueConverter<MaskValueType>::Type;
250
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 auto mask = MaskGridT::create(/*background=*/false);
251 mask->topologyUnion(grid);
252
2/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
8 mask->setTransform(grid.constTransform().copy());
253 8 return mask;
254 }
255
256 // Overload that avoids any processing if the input grid is already a mask grid
257 /// @private
258 template<typename GridT>
259 typename std::enable_if<std::is_same<MaskValueType, typename GridT::BuildType>::value,
260 typename GridT::ConstPtr>::type
261 1 convertToMaskGrid(const GridT& grid)
262 {
263 1 return grid.copy(); // shallow copy
264 }
265
266
267 ////////////////////////////////////////
268
269
270 /// @private
271 template<typename GridType>
272 typename GridType::Ptr
273 12 doClip(
274 const GridType& grid,
275 const typename GridType::template ValueConverter<MaskValueType>::Type& clipMask,
276 bool keepInterior)
277 {
278 using TreeT = typename GridType::TreeType;
279 using MaskTreeT = typename GridType::TreeType::template ValueConverter<MaskValueType>::Type;
280
281 12 const auto gridClass = grid.getGridClass();
282 const auto& tree = grid.tree();
283
284
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
24 MaskTreeT gridMask(false);
285 gridMask.topologyUnion(tree);
286
287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
12 if (gridClass == GRID_LEVEL_SET) {
288 tree::LeafManager<MaskTreeT> leafNodes(gridMask);
289 leafNodes.foreach(MaskInteriorVoxels<TreeT>(tree));
290
291 tree::ValueAccessor<const TreeT> acc(tree);
292
293 typename MaskTreeT::ValueAllIter iter(gridMask);
294 iter.setMaxDepth(MaskTreeT::ValueAllIter::LEAF_DEPTH - 1);
295
296 for ( ; iter; ++iter) {
297 iter.setActiveState(math::isNegative(acc.getValue(iter.getCoord())));
298 }
299 }
300
301
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
12 if (keepInterior) {
302 gridMask.topologyIntersection(clipMask.constTree());
303 } else {
304 gridMask.topologyDifference(clipMask.constTree());
305 }
306
307
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 auto outGrid = grid.copyWithNewTree();
308 {
309 // Copy voxel values and states.
310
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
24 tree::LeafManager<const MaskTreeT> leafNodes(gridMask);
311
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 CopyLeafNodes<TreeT> maskOp(tree, leafNodes);
312
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 maskOp.run();
313
3/10
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
24 outGrid->setTree(maskOp.tree());
314 }
315 {
316 // Copy tile values and states.
317 tree::ValueAccessor<const TreeT> refAcc(tree);
318 tree::ValueAccessor<const MaskTreeT> maskAcc(gridMask);
319
320
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 typename TreeT::ValueAllIter it(outGrid->tree());
321 12 it.setMaxDepth(TreeT::ValueAllIter::LEAF_DEPTH - 1);
322
2/2
✓ Branch 0 taken 884588 times.
✓ Branch 1 taken 6 times.
1769188 for ( ; it; ++it) {
323 1769176 Coord ijk = it.getCoord();
324
325
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 884588 times.
1769176 if (maskAcc.isValueOn(ijk)) {
326 typename TreeT::ValueType value;
327 bool isActive = refAcc.probeValue(ijk, value);
328
329 it.setValue(value);
330 if (!isActive) it.setValueOff();
331 }
332 }
333 }
334
335
2/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
12 outGrid->setTransform(grid.transform().copy());
336
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
12 if (gridClass != GRID_LEVEL_SET) outGrid->setGridClass(gridClass);
337
338 12 return outGrid;
339 }
340
341 } // namespace clip_internal
342
343 /// @endcond
344
345
346 ////////////////////////////////////////
347
348
349 /// @private
350 template<typename GridType>
351 typename GridType::Ptr
352 2 clip(const GridType& grid, const BBoxd& bbox, bool keepInterior)
353 {
354 using MaskValueT = clip_internal::MaskValueType;
355 using MaskGridT = typename GridType::template ValueConverter<MaskValueT>::Type;
356
357 // Transform the world-space bounding box into the source grid's index space.
358 Vec3d idxMin, idxMax;
359 2 math::calculateBounds(grid.constTransform(), bbox.min(), bbox.max(), idxMin, idxMax);
360 2 CoordBBox region(Coord::floor(idxMin), Coord::floor(idxMax));
361 // Construct a boolean mask grid that is true inside the index-space bounding box
362 // and false everywhere else.
363 4 MaskGridT clipMask(/*background=*/false);
364
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 clipMask.fill(region, /*value=*/true, /*active=*/true);
365
366
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
4 return clip_internal::doClip(grid, clipMask, keepInterior);
367 }
368
369
370 /// @private
371 template<typename SrcGridType, typename ClipTreeType>
372 typename SrcGridType::Ptr
373 5 clip(const SrcGridType& srcGrid, const Grid<ClipTreeType>& clipGrid, bool keepInterior)
374 {
375 using MaskValueT = clip_internal::MaskValueType;
376 using ClipGridType = Grid<ClipTreeType>;
377 using SrcMaskGridType = typename SrcGridType::template ValueConverter<MaskValueT>::Type;
378 using ClipMaskGridType = typename ClipGridType::template ValueConverter<MaskValueT>::Type;
379
380 // Convert the clipping grid to a boolean-valued mask grid with the same tree configuration.
381 5 auto maskGrid = clip_internal::convertToMaskGrid(clipGrid);
382
383 // Resample the mask grid into the source grid's index space.
384 5 if (srcGrid.constTransform() != maskGrid->constTransform()) {
385 auto resampledMask = ClipMaskGridType::create(/*background=*/false);
386 resampledMask->setTransform(srcGrid.constTransform().copy());
387 tools::resampleToMatch<clip_internal::BoolSampler>(*maskGrid, *resampledMask);
388 tools::prune(resampledMask->tree());
389 maskGrid = resampledMask;
390 }
391
392 // Convert the mask grid to a mask grid with the same tree configuration as the source grid.
393 4 auto clipMask = clip_internal::ConvertGrid<
394 /*from=*/ClipMaskGridType, /*to=*/SrcMaskGridType>()(maskGrid);
395
396 // Clip the source grid against the mask grid.
397 10 return clip_internal::doClip(srcGrid, *clipMask, keepInterior);
398 }
399
400
401 /// @private
402 template<typename GridType>
403 typename GridType::Ptr
404 3 clip(const GridType& inGrid, const math::NonlinearFrustumMap& frustumMap, bool keepInterior)
405 {
406 using ValueT = typename GridType::ValueType;
407 using TreeT = typename GridType::TreeType;
408 using LeafT = typename TreeT::LeafNodeType;
409
410 const auto& gridXform = inGrid.transform();
411 3 const auto frustumIndexBBox = frustumMap.getBBox();
412
413 // Return true if index-space point (i,j,k) lies inside the frustum.
414 2114691 auto frustumContainsCoord = [&](const Coord& ijk) -> bool {
415 auto xyz = gridXform.indexToWorld(ijk);
416 2114688 xyz = frustumMap.applyInverseMap(xyz);
417 2114688 return frustumIndexBBox.isInside(xyz);
418 };
419
420 // Return the frustum index-space bounding box of the corners of
421 // the given grid index-space bounding box.
422 1116325 auto toFrustumIndexSpace = [&](const CoordBBox& inBBox) -> BBoxd {
423 65666 const Coord bounds[2] = { inBBox.min(), inBBox.max() };
424 Coord ijk;
425 BBoxd outBBox;
426
2/20
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 525328 times.
✓ Branch 9 taken 65666 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
590994 for (int i = 0; i < 8; ++i) {
427 525328 ijk[0] = bounds[(i & 1) >> 0][0];
428 525328 ijk[1] = bounds[(i & 2) >> 1][1];
429 525328 ijk[2] = bounds[(i & 4) >> 2][2];
430 auto xyz = gridXform.indexToWorld(ijk);
431 525328 xyz = frustumMap.applyInverseMap(xyz);
432 525328 outBBox.expand(xyz);
433 }
434 65666 return outBBox;
435 };
436
437 // Construct an output grid with the same transform and metadata as the input grid.
438 3 auto outGrid = inGrid.copyWithNewTree();
439 3 if (outGrid->getGridClass() == GRID_LEVEL_SET) {
440 // After clipping, a level set grid might no longer be a valid SDF.
441 outGrid->setGridClass(GRID_UNKNOWN);
442 }
443
444 const auto& bg = outGrid->background();
445
446 auto outAcc = outGrid->getAccessor();
447
448 // Copy active and inactive tiles that intersect the clipping region
449 // from the input grid to the output grid.
450 // ("Clipping region" refers to either the interior or the exterior
451 // of the frustum, depending on the value of keepInterior.)
452 auto tileIter = inGrid.beginValueAll();
453 3 tileIter.setMaxDepth(GridType::ValueAllIter::LEAF_DEPTH - 1);
454 3 CoordBBox tileBBox;
455 360387 for ( ; tileIter; ++tileIter) {
456 const bool tileActive = tileIter.isValueOn();
457 const auto& tileValue = tileIter.getValue();
458
459 // Skip background tiles.
460 360384 if (!tileActive && math::isApproxEqual(tileValue, bg)) continue;
461
462 // Transform the tile's bounding box into frustum index space.
463 10 tileIter.getBoundingBox(tileBBox);
464 10 const auto tileFrustumBBox = toFrustumIndexSpace(tileBBox);
465
466 // Determine whether any or all of the tile intersects the clipping region.
467 enum class CopyTile { kNone, kPartial, kFull };
468 auto copyTile = CopyTile::kNone;
469 10 if (keepInterior) {
470 9 if (frustumIndexBBox.isInside(tileFrustumBBox)) {
471 copyTile = CopyTile::kFull;
472 9 } else if (frustumIndexBBox.hasOverlap(tileFrustumBBox)) {
473 copyTile = CopyTile::kPartial;
474 }
475 } else {
476 1 if (!frustumIndexBBox.hasOverlap(tileFrustumBBox)) {
477 copyTile = CopyTile::kFull;
478 1 } else if (!frustumIndexBBox.isInside(tileFrustumBBox)) {
479 copyTile = CopyTile::kPartial;
480 }
481 }
482 switch (copyTile) {
483 case CopyTile::kNone:
484 break;
485 case CopyTile::kFull:
486 // Copy the entire tile.
487 outAcc.addTile(tileIter.getLevel(), tileBBox.min(), tileValue, tileActive);
488 break;
489 10 case CopyTile::kPartial:
490 // Copy only voxels inside the clipping region.
491 131200 for (std::vector<CoordBBox> bboxVec = { tileBBox }; !bboxVec.empty(); ) {
492 // For efficiency, subdivide sufficiently large tiles and discard
493 // subregions based on additional bounding box intersection tests.
494 // The mimimum subregion size is chosen so that cost of the
495 // bounding box test is comparable to testing every voxel.
496 131190 if (bboxVec.back().volume() > 64 && bboxVec.back().is_divisible()) {
497 // Subdivide this region in-place and append the other half to the list.
498 65590 bboxVec.emplace_back(bboxVec.back(), tbb::split{});
499 98348 continue;
500 }
501 65600 auto subBBox = bboxVec.back();
502 bboxVec.pop_back();
503
504 // Discard the subregion if it lies completely outside the clipping region.
505 65600 if (keepInterior) {
506 32832 if (!frustumIndexBBox.hasOverlap(toFrustumIndexSpace(subBBox))) continue;
507 } else {
508 32768 if (frustumIndexBBox.isInside(toFrustumIndexSpace(subBBox))) continue;
509 }
510
511 // Test every voxel within the subregion.
512 2134730 for (const auto& ijk: subBBox) {
513 2101888 if (frustumContainsCoord(ijk) == keepInterior) {
514 2099576 if (tileActive) {
515 outAcc.setValueOn(ijk, tileValue);
516 } else {
517 outAcc.setValueOff(ijk, tileValue);
518 }
519 }
520 }
521 }
522 10 break;
523 }
524 }
525 3 tools::prune(outGrid->tree());
526
527 // Ensure that the output grid has the same leaf node topology as the input grid,
528 // with the exception of leaf nodes that lie completely outside the clipping region.
529 // (This operation is serial.)
530 59 for (auto leafIter = inGrid.constTree().beginLeaf(); leafIter; ++leafIter) {
531 56 const auto leafBBox = leafIter->getNodeBoundingBox();
532 56 const auto leafFrustumBBox = toFrustumIndexSpace(leafBBox);
533 56 if (keepInterior) {
534 56 if (frustumIndexBBox.hasOverlap(leafFrustumBBox)) {
535 13 outAcc.touchLeaf(leafBBox.min());
536 }
537 } else {
538 if (!frustumIndexBBox.hasOverlap(leafFrustumBBox)
539 || !frustumIndexBBox.isInside(leafFrustumBBox))
540 {
541 outAcc.touchLeaf(leafBBox.min());
542 }
543 }
544 }
545
546 // In parallel across output leaf nodes, copy leaf voxels
547 // from the input grid to the output grid.
548 3 tree::LeafManager<TreeT> outLeafNodes{outGrid->tree()};
549 3 outLeafNodes.foreach(
550 50 [&](LeafT& leaf, size_t /*idx*/) {
551 auto inAcc = inGrid.getConstAccessor();
552 ValueT val;
553
2/20
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 12800 times.
✓ Branch 14 taken 25 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
12850 for (auto voxelIter = leaf.beginValueAll(); voxelIter; ++voxelIter) {
554
1/20
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 12800 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
12800 const auto ijk = voxelIter.getCoord();
555
3/40
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✓ Branch 21 taken 12800 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 3894 times.
✓ Branch 24 taken 8906 times.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
12800 if (frustumContainsCoord(ijk) == keepInterior) {
556
1/16
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 3894 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
3894 const bool active = inAcc.probeValue(ijk, val);
557
1/18
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 3894 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
3894 voxelIter.setValue(val);
558
1/18
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 3894 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
3894 voxelIter.setValueOn(active);
559 }
560 }
561 }
562 );
563
564 3 return outGrid;
565 }
566
567
568 ////////////////////////////////////////
569
570
571 // Explicit Template Instantiation
572
573 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
574
575 #ifdef OPENVDB_INSTANTIATE_CLIP
576 #include <openvdb/util/ExplicitInstantiation.h>
577 #endif
578
579 #define _FUNCTION(TreeT) \
580 Grid<TreeT>::Ptr clip(const Grid<TreeT>&, const BBoxd&, bool)
581 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
582 #undef _FUNCTION
583
584 #define _FUNCTION(TreeT) \
585 Grid<TreeT>::Ptr clip(const Grid<TreeT>&, const math::NonlinearFrustumMap&, bool)
586 OPENVDB_ALL_TREE_INSTANTIATE(_FUNCTION)
587 #undef _FUNCTION
588
589 #define _FUNCTION(TreeT) \
590 Grid<TreeT>::Ptr clip_internal::doClip(const Grid<TreeT>&, const MaskGrid&, bool)
591 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
592 #undef _FUNCTION
593
594 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
595
596
597 } // namespace tools
598 } // namespace OPENVDB_VERSION_NAME
599 } // namespace openvdb
600
601 #endif // OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED
602