GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/tools/Filter.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 194 265 73.2%
Functions: 23 68 33.8%
Branches: 196 572 34.3%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
4 /// @author Ken Museth
5 ///
6 /// @file tools/Filter.h
7 ///
8 /// @brief Filtering of VDB volumes. All operations can optionally be masked
9 /// with another grid that acts as an alpha-mask. By default, filtering
10 /// operations do not modify the topology of the input tree and thus do
11 /// not process active tiles. However Filter::setProcessTiles can be
12 /// used to process active tiles, densifying them on demand when necessary.
13
14 #ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
15 #define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
16
17 #include "openvdb/Types.h"
18 #include "openvdb/Grid.h"
19 #include "openvdb/math/Math.h"
20 #include "openvdb/math/Stencils.h"
21 #include "openvdb/math/Transform.h"
22 #include "openvdb/tree/NodeManager.h"
23 #include "openvdb/tree/LeafManager.h"
24 #include "openvdb/util/NullInterrupter.h"
25 #include "openvdb/util/Util.h"
26 #include "openvdb/thread/Threading.h"
27 #include "Interpolation.h"
28
29 #include <tbb/parallel_for.h>
30 #include <tbb/concurrent_vector.h>
31
32 #include <algorithm> // for std::max()
33 #include <functional>
34 #include <type_traits>
35
36 namespace openvdb {
37 OPENVDB_USE_VERSION_NAMESPACE
38 namespace OPENVDB_VERSION_NAME {
39 namespace tools {
40
41 /// @brief Volume filtering (e.g., diffusion) with optional alpha masking
42 template<typename GridT,
43 typename MaskT = typename GridT::template ValueConverter<float>::Type,
44 typename InterruptT = util::NullInterrupter>
45
10/20
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
17 class Filter
46 {
47 public:
48 using GridType = GridT;
49 using MaskType = MaskT;
50 using TreeType = typename GridType::TreeType;
51 using LeafType = typename TreeType::LeafNodeType;
52 using ValueType = typename GridType::ValueType;
53 using AlphaType = typename MaskType::ValueType;
54 using LeafManagerType = typename tree::LeafManager<TreeType>;
55 using RangeType = typename LeafManagerType::LeafRange;
56 using BufferType = typename LeafManagerType::BufferType;
57 static_assert(std::is_floating_point<AlphaType>::value,
58 "openvdb::tools::Filter requires a mask grid with floating-point values");
59
60 /// Constructor
61 /// @param grid Grid to be filtered.
62 /// @param interrupt Optional interrupter.
63 17 Filter(GridT& grid, InterruptT* interrupt = nullptr)
64 : mGrid(&grid)
65 , mTask(nullptr)
66 , mInterrupter(interrupt)
67 , mMask(nullptr)
68 , mGrainSize(1)
69 , mMinMask(0)
70 , mMaxMask(1)
71 , mInvertMask(false)
72
12/24
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 1 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
17 , mTiles(false) {}
73
74 /// @brief Shallow copy constructor called by tbb::parallel_for()
75 /// threads during filtering.
76 /// @param other The other Filter from which to copy.
77 1400 Filter(const Filter& other)
78 1400 : mGrid(other.mGrid)
79 1400 , mTask(other.mTask)
80 1400 , mInterrupter(other.mInterrupter)
81 1400 , mMask(other.mMask)
82 1400 , mGrainSize(other.mGrainSize)
83 1400 , mMinMask(other.mMinMask)
84 1400 , mMaxMask(other.mMaxMask)
85 1400 , mInvertMask(other.mInvertMask)
86 1400 , mTiles(other.mTiles) {}
87
88 /// @return the grain-size used for multi-threading
89 int getGrainSize() const { return mGrainSize; }
90 /// @brief Set the grain-size used for multi-threading.
91 /// @note A grain size of 0 or less disables multi-threading!
92
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 void setGrainSize(int grainsize) { mGrainSize = grainsize; }
93
94 /// @return whether active tiles are being processed
95 25 bool getProcessTiles() const { return mTiles; }
96 /// @brief Set whether active tiles should also be processed.
97 /// @note If true, some tiles may become voxelized
98 /// @warning If using with a mask, ensure that the mask topology matches the
99 /// tile topology of the filter grid as tiles will not respect overlapping
100 /// mask values at tree levels finer than themselves e.g. a leaf level tile
101 /// will only use the corresponding tile ijk value in the mask grid
102
9/18
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
14 void setProcessTiles(bool flag) { mTiles = flag; }
103
104 /// @brief Return the minimum value of the mask to be used for the
105 /// derivation of a smooth alpha value.
106 AlphaType minMask() const { return mMinMask; }
107 /// @brief Return the maximum value of the mask to be used for the
108 /// derivation of a smooth alpha value.
109 AlphaType maxMask() const { return mMaxMask; }
110 /// @brief Define the range for the (optional) scalar mask.
111 /// @param min Minimum value of the range.
112 /// @param max Maximum value of the range.
113 /// @details Mask values outside the range are clamped to zero or one, and
114 /// values inside the range map smoothly to 0->1 (unless the mask is inverted).
115 /// @throw ValueError if @a min is not smaller than @a max.
116 void setMaskRange(AlphaType min, AlphaType max)
117 {
118 if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
119 mMinMask = min;
120 mMaxMask = max;
121 }
122
123 /// @brief Return true if the mask is inverted, i.e. min->max in the
124 /// original mask maps to 1->0 in the inverted alpha mask.
125 bool isMaskInverted() const { return mInvertMask; }
126 /// @brief Invert the optional mask, i.e. min->max in the original
127 /// mask maps to 1->0 in the inverted alpha mask.
128 void invertMask(bool invert=true) { mInvertMask = invert; }
129
130 /// @brief One iteration of a fast separable mean-value (i.e. box) filter.
131 /// @param width The width of the mean-value filter is 2*width+1 voxels.
132 /// @param iterations Number of times the mean-value filter is applied.
133 /// @param mask Optional alpha mask.
134 void mean(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
135
136 /// @brief One iteration of a fast separable Gaussian filter.
137 ///
138 /// @note This is approximated as 4 iterations of a separable mean filter
139 /// which typically leads an approximation that's better than 95%!
140 /// @param width The width of the mean-value filter is 2*width+1 voxels.
141 /// @param iterations Number of times the mean-value filter is applied.
142 /// @param mask Optional alpha mask.
143 void gaussian(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
144
145 /// @brief One iteration of a median-value filter
146 ///
147 /// @note This filter is not separable and is hence relatively slow!
148 /// @param width The width of the mean-value filter is 2*width+1 voxels.
149 /// @param iterations Number of times the mean-value filter is applied.
150 /// @param mask Optional alpha mask.
151 void median(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
152
153 /// Offsets (i.e. adds) a constant value to all active voxels.
154 /// @param offset Offset in the same units as the grid.
155 /// @param mask Optional alpha mask.
156 void offset(ValueType offset, const MaskType* mask = nullptr);
157
158 /// @brief Used internally by tbb::parallel_for()
159 /// @param range Range of LeafNodes over which to multi-thread.
160 ///
161 /// @warning Never call this method directly!
162
1/2
✓ Branch 0 taken 1677 times.
✗ Branch 1 not taken.
3354 void operator()(const RangeType& range) const
163 {
164
1/2
✓ Branch 0 taken 1677 times.
✗ Branch 1 not taken.
3354 if (mTask) mTask(const_cast<Filter*>(this), range);
165 else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc.");
166 }
167
168 private:
169 using LeafT = typename TreeType::LeafNodeType;
170 using VoxelIterT = typename LeafT::ValueOnIter;
171 using VoxelCIterT = typename LeafT::ValueOnCIter;
172 using BufferT = typename tree::LeafManager<TreeType>::BufferType;
173 using LeafIterT = typename RangeType::Iterator;
174 using AlphaMaskT = tools::AlphaMask<GridT, MaskT>;
175
176 void cook(LeafManagerType& leafs);
177
178 template<size_t Axis>
179 struct Avg {
180 2582 Avg(const GridT* grid, Int32 w): acc(grid->tree()), width(w), frac(1.f/float(2*w+1)) {}
181 inline ValueType operator()(Coord xyz);
182 typename GridT::ConstAccessor acc;
183 const Int32 width;
184 const float frac;
185 };
186
187 // Private filter methods called by tbb::parallel_for threads
188 template <typename AvgT>
189 void doBox(const RangeType& r, Int32 w);
190 850 void doBoxX(const RangeType& r, Int32 w) { this->doBox<Avg<0> >(r,w); }
191 876 void doBoxY(const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); }
192 856 void doBoxZ(const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); }
193 void doMedian(const RangeType&, int);
194 void doOffset(const RangeType&, ValueType);
195 /// @return true if the process was interrupted
196 bool wasInterrupted();
197
198 GridType* mGrid;
199 typename std::function<void (Filter*, const RangeType&)> mTask;
200 InterruptT* mInterrupter;
201 const MaskType* mMask;
202 int mGrainSize;
203 AlphaType mMinMask, mMaxMask;
204 bool mInvertMask;
205 bool mTiles;
206 }; // end of Filter class
207
208
209 ////////////////////////////////////////
210
211 /// @cond OPENVDB_DOCS_INTERNAL
212
213 namespace filter_internal {
214
215 template<typename TreeT>
216 13 struct Voxelizer
217 {
218 // NodeManager for processing internal/root node values
219 // @note Should not cache leaf nodes
220 using NodeManagerT = tree::NodeManager<TreeT, TreeT::RootNodeType::LEVEL-1>;
221 using MaskT = typename TreeT::template ValueConverter<ValueMask>::Type;
222
223 13 Voxelizer(TreeT& tree, const bool allNeighbors, const size_t grainSize)
224 : mVoxelTopology()
225 , mManager(nullptr)
226 , mGrainSize(grainSize)
227
2/8
✗ 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 taken 7 times.
✓ Branch 7 taken 2 times.
9 , mOp(tree, mVoxelTopology, allNeighbors ? 26 : 6) {}
228
229 /// @brief Convert tiles to leaf nodes that exist at a particular
230 /// voxel distance away
231 /// @param width distance in voxels to seach for tiles from each leaf
232 /// @return Returns how many search iterations were performed, which
233 /// also represents how many leaf node neighbors may have been
234 /// created. Returns 0 if the tree is already entirely voxelized
235 28 int run(const int width)
236 {
237
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
28 if (!mOp.tree().hasActiveTiles()) return 0;
238 28 this->init();
239 int count = 0;
240
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 14 times.
58 for (int i = 0; i < width; i += int(TreeT::LeafNodeType::DIM), ++count) {
241
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
30 if (i > 0) mManager->rebuild();
242 30 mManager->foreachBottomUp(mOp, mGrainSize > 0, mGrainSize);
243 mOp.tree().topologyUnion(mVoxelTopology);
244 }
245 return count;
246 }
247
248 private:
249
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
28 void init()
250 {
251
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
28 if (mManager) {
252 2 mManager->rebuild();
253 }
254 else {
255 // @note We don't actually need the leaf topology here, just the
256 // internal node structure so that we can generate leaf nodes in parallel
257 26 mVoxelTopology.topologyUnion(mOp.tree());
258
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
26 mManager.reset(new NodeManagerT(mOp.tree()));
259 }
260 }
261
262 struct CreateVoxelMask
263 {
264 using LeafT = typename TreeT::LeafNodeType;
265 using RootT = typename TreeT::RootNodeType;
266
267 13 CreateVoxelMask(TreeT& tree, MaskT& mask, const size_t NN)
268
2/12
✗ 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 taken 4 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 9 times.
13 : mTree(tree), mVoxelTopology(mask), mNeighbors(NN) {}
269
270 55 TreeT& tree() { return mTree; }
271
272 // do nothing for leaf nodes. They shouldn't even be cached as
273 // part of the NodeManager used with this method.
274 void operator()(const LeafT&) const { assert(false); }
275
276 30 void operator()(const RootT& node) const
277 {
278 using ChildT = typename RootT::ChildNodeType;
279 static constexpr Int32 CHILDDIM = Int32(ChildT::DIM);
280 static constexpr Int32 LEAFDIM = Int32(LeafT::DIM);
281 30 const Tester op(mTree, mNeighbors);
282
283 30 auto step =
284 6 [&](const Coord& ijk,
285 const size_t axis1,
286 const size_t axis2,
287 const auto& val)
288 {
289 Coord offset(0);
290 Int32& a = offset[axis1];
291 Int32& b = offset[axis2];
292
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 3072 times.
✓ Branch 3 taken 6 times.
3078 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
293
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1572864 times.
✓ Branch 3 taken 3072 times.
1575936 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
294 1572864 const Coord childijk = ijk + offset;
295
2/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✓ Branch 5 taken 1572842 times.
1572864 if (op.test(childijk, val)) {
296 22 mVoxelTopology.touchLeaf(childijk);
297 }
298 }
299 }
300
301 offset.reset(CHILDDIM-1);
302
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 3072 times.
✓ Branch 3 taken 6 times.
3078 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
303
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1572864 times.
✓ Branch 3 taken 3072 times.
1575936 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
304 1572864 const Coord childijk = ijk + offset;
305
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1572864 times.
1572864 if (op.test(childijk, val)) {
306 mVoxelTopology.touchLeaf(childijk);
307 }
308 }
309 }
310 };
311
312
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
34 for (auto iter = node.cbeginValueOn(); iter; ++iter) {
313
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 const Coord& ijk = iter.getCoord();
314 // @todo step only needs to search if a given direction
315 // depending on the face
316
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 step(ijk, 0, 1, *iter);
317
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 step(ijk, 0, 2, *iter);
318
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 step(ijk, 1, 2, *iter);
319 }
320 }
321
322 template<typename NodeT>
323 66 void operator()(const NodeT& node) const
324 {
325 using ChildT = typename NodeT::ChildNodeType;
326 static constexpr Int32 CHILDDIM = Int32(ChildT::DIM);
327 static constexpr Int32 LEAFDIM = Int32(LeafT::DIM);
328
329 static auto step =
330 98316 [](const Tester& op,
331 const Coord& ijk,
332 const size_t axis1,
333 const size_t axis2,
334 const auto& val,
335 std::vector<Coord>& coords)
336 {
337 Coord offset(0);
338 Int32& a = offset[axis1];
339 Int32& b = offset[axis2];
340
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1573056 times.
✓ Branch 3 taken 98316 times.
1671372 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
341
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 25168896 times.
✓ Branch 3 taken 1573056 times.
26741952 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
342 25168896 const Coord childijk = ijk + offset;
343
2/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 1553 times.
✓ Branch 5 taken 25167343 times.
25168896 if (op.test(childijk, val)) {
344 1553 coords.emplace_back(childijk);
345 }
346 }
347 }
348
349 offset.reset(CHILDDIM-1);
350
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1573056 times.
✓ Branch 3 taken 98316 times.
1671372 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
351
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 25168896 times.
✓ Branch 3 taken 1573056 times.
26741952 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
352 25168896 const Coord childijk = ijk + offset;
353
2/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 1536 times.
✓ Branch 5 taken 25167360 times.
25168896 if (op.test(childijk, val)) {
354 1536 coords.emplace_back(childijk);
355 }
356 }
357 }
358 };
359
360 /// Two types of algorithms here
361 /// 1) For the case where this node is the direct parent of leaf nodes
362 /// 2) For all other node types
363 ///
364 /// In general, given a tile's ijk, search its faces/edges/corners for
365 /// values which differ from its own or leaf level topology. When a
366 /// difference is detected, mask topology is generated which can be used
367 /// with topologyUnion to ensure valid voxel values exist in the source
368 /// grid.
369 ///
370 /// This operator handles all internal node types. For example, for the
371 /// lowest level internal node (which contains leaf nodes as children)
372 /// each tile is at the leaf level (a single tile represents an 8x8x8
373 /// node). CHILDDIM is this case will match the valid of LEAFDIM, as we
374 /// only need to check each tiles immediate neighbors. For higher level
375 /// internal nodes (and the root node) each child tile will have a
376 /// significantly larger CHILDDIM than the grid's LEAFDIM. We
377 /// consistently probe values along the LEAFDIM stride to ensure no
378 /// changes are missed.
379
380 if (CHILDDIM == LEAFDIM) {
381 // If the current node is the parent of leaf nodes, search each
382 // neighbor directly and use a flag buffer to test offsets in
383 // this node which need converting to leaf level topology.
384 // This is faster than the more general method which steps across
385 // faces (unecessary due to CHILDDIM == LEAFDIM) and provides
386 // a simpler way of tracking new topology
387
388 28 std::vector<char> flags(NodeT::NUM_VALUES, char(0));
389
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
28 tbb::parallel_for(tbb::blocked_range<size_t>(0, NodeT::NUM_VALUES),
390 10040 [&](const tbb::blocked_range<size_t>& range) {
391 1851 const Tester op(mTree, mNeighbors);
392
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 57344 times.
✓ Branch 3 taken 1851 times.
59195 for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
393
2/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 8189 times.
✓ Branch 5 taken 49155 times.
57344 if (node.isValueMaskOn(Index(n))) {
394 // if index is a tile, search its neighbors
395 8189 const Coord ijk = node.offsetToGlobalCoord(Index(n));
396
2/8
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 7 taken 8189 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 8189 times.
✗ Branch 11 not taken.
8189 flags[n] = op.test(ijk, node.getValue(ijk));
397 }
398 }
399 });
400
401 // create leaf level topology in this internal node
402 Index idx = 0;
403
2/2
✓ Branch 0 taken 57344 times.
✓ Branch 1 taken 14 times.
114716 for (auto iter = flags.begin(); iter != flags.end(); ++iter, ++idx) {
404
2/4
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 57305 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
114766 if (*iter) mVoxelTopology.touchLeaf(node.offsetToGlobalCoord(idx));
405 }
406 }
407 else {
408 // If this is a higher level internal node, we only need to search its
409 // face/edge/vertex neighbors for values which differ or leaf level
410 // topology. When a difference is detected, store the coordinate which
411 // needs to be voxelized.
412 // @todo investigate better threaded impl
413
414 38 tbb::concurrent_vector<Coord> nodes;
415
2/4
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
38 tbb::parallel_for(tbb::blocked_range<size_t>(0, NodeT::NUM_VALUES),
416 2609 [&](const tbb::blocked_range<size_t>& range)
417 {
418 2606 const Tester op(mTree, mNeighbors);
419 std::vector<Coord> coords;
420
421
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 622592 times.
✓ Branch 3 taken 2606 times.
625198 for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
422
2/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 589820 times.
✓ Branch 5 taken 32772 times.
622592 if (!node.isValueMaskOn(Index(n))) continue;
423
424 32772 const Coord ijk = node.offsetToGlobalCoord(Index(n));
425
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 32772 times.
✗ Branch 5 not taken.
32772 const auto& val = node.getValue(ijk);
426 // @todo step only needs to search if a given direction
427 // depending on the face
428
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 32772 times.
✗ Branch 5 not taken.
32772 step(op, ijk, 0, 1, val, coords);
429
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 32772 times.
✗ Branch 5 not taken.
32772 step(op, ijk, 0, 2, val, coords);
430
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 32772 times.
✗ Branch 5 not taken.
32772 step(op, ijk, 1, 2, val, coords);
431 }
432
433
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2603 times.
2606 if (!coords.empty()) {
434
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 std::copy(coords.begin(), coords.end(),
435 nodes.grow_by(coords.size()));
436 }
437 });
438
439 // create leaf level topology in this internal node
440 // @note nodes may contain duplicate coords
441
2/2
✓ Branch 0 taken 3089 times.
✓ Branch 1 taken 19 times.
6216 for (const auto& coord : nodes) {
442
1/2
✓ Branch 1 taken 3089 times.
✗ Branch 2 not taken.
6178 mVoxelTopology.touchLeaf(coord);
443 }
444 }
445 }
446
447 private:
448 struct Tester
449 {
450 4472 Tester(const TreeT& tree, const size_t NN)
451 4472 : mAcc(tree), mNeighbors(NN) {}
452
453 106983418 inline bool test(const Coord& ijk,
454 const typename TreeT::ValueType& val) const
455 {
456 static constexpr Int32 LEAFDIM = Int32(LeafT::DIM);
457 const Coord* NN = util::COORD_OFFSETS;
458
2/2
✓ Branch 0 taken 352540917 times.
✓ Branch 1 taken 53488559 times.
812058952 for (size_t i = 0; i < mNeighbors; ++i, ++NN) {
459 705081834 Coord neighbor(*NN);
460 705081834 neighbor.x() *= LEAFDIM;
461 705081834 neighbor.y() *= LEAFDIM;
462 705081834 neighbor.z() *= LEAFDIM;
463 neighbor += ijk;
464 // if a leaf exists, assume its buffer is not constant
465
4/4
✓ Branch 1 taken 352537802 times.
✓ Branch 2 taken 3115 times.
✓ Branch 3 taken 35 times.
✓ Branch 4 taken 352537767 times.
1410157438 if (mAcc.getValue(neighbor) != val ||
466 mAcc.probeConstLeaf(neighbor)) {
467 6300 return true;
468 }
469 }
470 return false;
471 }
472 private:
473 const tree::ValueAccessor<const TreeT> mAcc;
474 const size_t mNeighbors;
475 };
476
477 private:
478 TreeT& mTree;
479 MaskT& mVoxelTopology;
480 const size_t mNeighbors;
481 };// CreateVoxelMask
482
483 private:
484 MaskT mVoxelTopology;
485 std::unique_ptr<NodeManagerT> mManager;
486 const size_t mGrainSize;
487 CreateVoxelMask mOp;
488 };
489
490 // Helper function for Filter::Avg::operator()
491 8236032 template<typename T> static inline void accum(T& sum, T addend) { sum += addend; }
492 // Overload for bool ValueType
493 inline void accum(bool& sum, bool addend) { sum = sum || addend; }
494
495 } // namespace filter_internal
496
497 /// @endcond
498
499 ////////////////////////////////////////
500
501
502 template<typename GridT, typename MaskT, typename InterruptT>
503 template<size_t Axis>
504 inline typename GridT::ValueType
505 5025792 Filter<GridT, MaskT, InterruptT>::Avg<Axis>::operator()(Coord xyz)
506 {
507 ValueType sum = zeroVal<ValueType>();
508 5025792 Int32 &i = xyz[Axis], j = i + width;
509
2/2
✓ Branch 0 taken 8236032 times.
✓ Branch 1 taken 2512896 times.
21497856 for (i -= width; i <= j; ++i) filter_internal::accum(sum, acc.getValue(xyz));
510 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
511 5025792 ValueType value = static_cast<ValueType>(sum * frac);
512 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
513 5025792 return value;
514 }
515
516
517 ////////////////////////////////////////
518
519
520 template<typename GridT, typename MaskT, typename InterruptT>
521 void
522 24 Filter<GridT, MaskT, InterruptT>::mean(int width, int iterations, const MaskType* mask)
523 {
524
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
24 if (iterations <= 0) return;
525 24 mMask = mask;
526
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 9 times.
24 const int w = std::max(1, width);
527 24 const bool serial = mGrainSize == 0;
528
529
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
24 if (mInterrupter) mInterrupter->start("Applying mean filter");
530
531 24 std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
532
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
24 if (this->getProcessTiles()) {
533 // if performing multiple iterations, also search edge/vertex
534 // neighbors for difference topology.
535 const bool allNeighbors = iterations > 1;
536 // If processing tiles, create a voxelizer and run a single
537 // width based search for tiles that need to be voxelized
538
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
18 voxelizer.reset(new filter_internal::Voxelizer<TreeType>
539
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
18 (mGrid->tree(), allNeighbors, mGrainSize));
540
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
18 if (!voxelizer->run(w)) voxelizer.reset();
541 }
542
543
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
48 LeafManagerType leafs(mGrid->tree(), 1, serial);
544
545 int iter = 1; // num of leaf level neighbor based searches performed
546 int dist = w; // kernel distance of the current iteration
547
4/6
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 12 times.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
66 for (int i=0; i<iterations && !this->wasInterrupted(); ++i, dist+=w) {
548
3/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
42 if (i > 0 && voxelizer) {
549 // the total influence distance in voxels of this iteration
550 // minus how far we've already accounted for
551 18 const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
552
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
18 if (remain > 0) {
553
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 const int searches = voxelizer->run(remain);
554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
2 if (searches == 0) voxelizer.reset();
555 else leafs.rebuild(serial);
556 2 iter += searches;
557 }
558 }
559
560
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
42 mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
561
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
42 this->cook(leafs);
562 // note that the order of the YZ passes are flipped to maintain backwards-compatibility
563 // with an indexing typo in the original logic
564
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
42 mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
565
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
42 this->cook(leafs);
566
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
42 mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
567
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
42 this->cook(leafs);
568 }
569
570
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
24 if (mInterrupter) mInterrupter->end();
571 }
572
573
574 template<typename GridT, typename MaskT, typename InterruptT>
575 void
576 Filter<GridT, MaskT, InterruptT>::gaussian(int width, int iterations, const MaskType* mask)
577 {
578 if (iterations <= 0) return;
579 mMask = mask;
580 const int w = std::max(1, width);
581 const bool serial = mGrainSize == 0;
582
583 if (mInterrupter) mInterrupter->start("Applying Gaussian filter");
584
585 std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
586 if (this->getProcessTiles()) {
587 // if performing multiple iterations, also search edge/vertex
588 // neighbors for difference topology.
589 const bool allNeighbors = iterations > 1;
590 // If processing tiles, create a voxelizer and run a single
591 // width based search for tiles that need to be voxelized
592 // @note account for sub iteration due to gaussian filter
593 voxelizer.reset(new filter_internal::Voxelizer<TreeType>
594 (mGrid->tree(), allNeighbors, mGrainSize));
595 if (!voxelizer->run(w*4)) voxelizer.reset();
596 }
597
598 LeafManagerType leafs(mGrid->tree(), 1, serial);
599
600 int iter = 1; // num of leaf level neighbor based searches performed
601 int dist = w*4; // kernel distance of the current iteration
602 for (int i=0; i<iterations; ++i, dist+=(w*4)) {
603 if (i > 0 && voxelizer) {
604 // the total influence distance in voxels of this iteration
605 // minus how far we've already accounted for
606 const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
607 if (remain > 0) {
608 const int searches = voxelizer->run(remain);
609 if (searches == 0) voxelizer.reset();
610 else leafs.rebuild(serial);
611 iter += searches;
612 }
613 }
614
615 for (int n=0; n<4 && !this->wasInterrupted(); ++n) {
616 mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
617 this->cook(leafs);
618 // note that the order of the YZ passes are flipped to maintain backwards-compatibility
619 // with an indexing typo in the original logic
620 mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
621 this->cook(leafs);
622 mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
623 this->cook(leafs);
624 }
625 }
626
627 if (mInterrupter) mInterrupter->end();
628 }
629
630
631 template<typename GridT, typename MaskT, typename InterruptT>
632 void
633 14 Filter<GridT, MaskT, InterruptT>::median(int width, int iterations, const MaskType* mask)
634 {
635
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
14 if (iterations <= 0) return;
636 14 mMask = mask;
637
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
14 const int w = std::max(1, width);
638 14 const bool serial = mGrainSize == 0;
639
640
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
14 if (mInterrupter) mInterrupter->start("Applying median filter");
641
642 14 std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
643
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
14 if (this->getProcessTiles()) {
644 // If processing tiles, create a voxelizer and run a single
645 // width based search for tiles that need to be voxelized
646
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 voxelizer.reset(new filter_internal::Voxelizer<TreeType>
647
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 (mGrid->tree(), /*allNeighbors*/true, mGrainSize));
648
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
8 if (!voxelizer->run(w)) voxelizer.reset();
649 }
650
651
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
28 LeafManagerType leafs(mGrid->tree(), 1, serial);
652
653
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
14 mTask = std::bind(&Filter::doMedian, std::placeholders::_1, std::placeholders::_2, w);
654
655 int iter = 1; // num of leaf level neighbor based searches performed
656 int dist = w; // kernel distance of the current iteration
657
4/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
28 for (int i=0; i<iterations && !this->wasInterrupted(); ++i, dist+=w) {
658
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
14 if (i > 0 && voxelizer) {
659 // the total influence distance in voxels of this iteration
660 // minus how far we've already accounted for
661 const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
662 if (remain > 0) {
663 const int searches = voxelizer->run(remain);
664 if (searches == 0) voxelizer.reset();
665 else leafs.rebuild(serial);
666 iter += searches;
667 }
668 }
669
670
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
14 this->cook(leafs);
671 }
672
673
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
14 if (mInterrupter) mInterrupter->end();
674 }
675
676
677 template<typename GridT, typename MaskT, typename InterruptT>
678 void
679 12 Filter<GridT, MaskT, InterruptT>::offset(ValueType value, const MaskType* mask)
680 {
681 12 mMask = mask;
682
683
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
12 if (mInterrupter) mInterrupter->start("Applying offset");
684
685
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
12 if (this->getProcessTiles()) {
686 // Don't process leaf nodes with the node manager - we'll do them
687 // separately to allow for cleaner branching
688 using NodeManagerT = tree::NodeManager<TreeType, TreeType::RootNodeType::LEVEL-1>;
689 8 NodeManagerT manager(mGrid->tree());
690
691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 if (mask) {
692 manager.foreachBottomUp([&](auto& node) {
693 this->wasInterrupted();
694 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
695 typename AlphaMaskT::FloatType a, b;
696 for (auto iter = node.beginValueOn(); iter; ++iter) {
697 if (!alpha(iter.getCoord(), a, b)) continue;
698 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
699 iter.modifyValue([&](ValueType& v) { v += a*value; });
700 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
701 }
702 });
703 }
704 else {
705
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
10 manager.foreachBottomUp([&](auto& node) {
706 5 this->wasInterrupted();
707
5/12
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 2 times.
9 for (auto iter = node.beginValueOn(); iter; ++iter) {
708 2 iter.modifyValue([&](ValueType& v) { v += value; });
709 }
710 });
711 }
712 }
713
714 24 LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0);
715
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 mTask = std::bind(&Filter::doOffset, std::placeholders::_1, std::placeholders::_2, value);
716
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 this->cook(leafs);
717
718
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 if (mInterrupter) mInterrupter->end();
719 }
720
721
722 ////////////////////////////////////////
723
724
725 /// Private method to perform the task (serial or threaded) and
726 /// subsequently swap the leaf buffers.
727 template<typename GridT, typename MaskT, typename InterruptT>
728 void
729 152 Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs)
730 {
731
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 1 times.
152 if (mGrainSize>0) {
732 150 tbb::parallel_for(leafs.leafRange(mGrainSize), *this);
733 } else {
734 2 (*this)(leafs.leafRange());
735 }
736 152 leafs.swapLeafBuffer(1, mGrainSize==0);
737 }
738
739
740 /// One dimensional convolution of a separable box filter
741 template<typename GridT, typename MaskT, typename InterruptT>
742 template <typename AvgT>
743 void
744 2582 Filter<GridT, MaskT, InterruptT>::doBox(const RangeType& range, Int32 w)
745 {
746 2582 this->wasInterrupted();
747 2582 AvgT avg(mGrid, w);
748
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1291 times.
2582 if (mMask) {
749 typename AlphaMaskT::FloatType a, b;
750 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
751 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
752 BufferT& buffer = leafIter.buffer(1);
753 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
754 const Coord xyz = iter.getCoord();
755 if (alpha(xyz, a, b)) {
756 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
757 const ValueType value(b*(*iter) + a*avg(xyz));
758 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
759 buffer.setValue(iter.pos(), value);
760 }
761 }
762 }
763 } else {
764
2/2
✓ Branch 1 taken 4947 times.
✓ Branch 2 taken 1291 times.
12476 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
765 BufferT& buffer = leafIter.buffer(1);
766
2/2
✓ Branch 0 taken 2512896 times.
✓ Branch 1 taken 4947 times.
5035686 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
767
3/8
✓ Branch 1 taken 2512896 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2512896 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2512896 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
5025792 buffer.setValue(iter.pos(), avg(iter.getCoord()));
768 }
769 }
770 }
771 }
772
773
774 /// Performs simple but slow median-value diffusion
775 template<typename GridT, typename MaskT, typename InterruptT>
776 void
777 520 Filter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width)
778 {
779 520 this->wasInterrupted();
780 520 typename math::DenseStencil<GridType> stencil(*mGrid, width);//creates local cache!
781
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 260 times.
520 if (mMask) {
782 typename AlphaMaskT::FloatType a, b;
783 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
784 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
785 BufferT& buffer = leafIter.buffer(1);
786 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
787 if (alpha(iter.getCoord(), a, b)) {
788 stencil.moveTo(iter);
789 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
790 ValueType value(b*(*iter) + a*stencil.median());
791 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
792 buffer.setValue(iter.pos(), value);
793 }
794 }
795 }
796 } else {
797
2/2
✓ Branch 1 taken 1478 times.
✓ Branch 2 taken 260 times.
3476 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
798 BufferT& buffer = leafIter.buffer(1);
799
2/2
✓ Branch 0 taken 756736 times.
✓ Branch 1 taken 1478 times.
1516428 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
800 stencil.moveTo(iter);
801
2/4
✓ Branch 1 taken 756736 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 756736 times.
✗ Branch 5 not taken.
1513472 buffer.setValue(iter.pos(), stencil.median());
802 }
803 }
804 }
805 }
806
807
808 /// Offsets the values by a constant
809 template<typename GridT, typename MaskT, typename InterruptT>
810 void
811 252 Filter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset)
812 {
813 252 this->wasInterrupted();
814
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
252 if (mMask) {
815 typename AlphaMaskT::FloatType a, b;
816 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
817 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
818 for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
819 if (alpha(iter.getCoord(), a, b)) {
820 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
821 ValueType value(*iter + a*offset);
822 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
823 iter.setValue(value);
824 }
825 }
826 }
827 } else {
828
2/2
✓ Branch 1 taken 250 times.
✓ Branch 2 taken 126 times.
752 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
829
2/2
✓ Branch 0 taken 128000 times.
✓ Branch 1 taken 250 times.
256500 for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
830 256000 iter.setValue(*iter + offset);
831 }
832 }
833 }
834 }
835
836
837 template<typename GridT, typename MaskT, typename InterruptT>
838 inline bool
839 1710 Filter<GridT, MaskT, InterruptT>::wasInterrupted()
840 {
841 1710 if (util::wasInterrupted(mInterrupter)) {
842 thread::cancelGroupExecution();
843 return true;
844 }
845 return false;
846 }
847
848
849 ////////////////////////////////////////
850
851
852 // Explicit Template Instantiation
853
854 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
855
856 #ifdef OPENVDB_INSTANTIATE_FILTER
857 #include <openvdb/util/ExplicitInstantiation.h>
858 #endif
859
860 OPENVDB_INSTANTIATE_CLASS Filter<FloatGrid, FloatGrid, util::NullInterrupter>;
861 OPENVDB_INSTANTIATE_CLASS Filter<DoubleGrid, FloatGrid, util::NullInterrupter>;
862
863 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
864
865
866 } // namespace tools
867 } // namespace OPENVDB_VERSION_NAME
868 } // namespace openvdb
869
870 #endif // OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
871