1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 ///
4 /// @file Diagnostics.h
5 ///
6 /// @author Ken Museth
7 ///
8 /// @brief Various diagnostic tools to identify potential issues with
9 /// for example narrow-band level sets or fog volumes
10 ///
11 #ifndef OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED
12 #define OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED
13
14 #include "openvdb/Grid.h"
15 #include "openvdb/math/Math.h"
16 #include "openvdb/math/Vec3.h"
17 #include "openvdb/math/Stencils.h"
18 #include "openvdb/math/Operators.h"
19 #include "openvdb/tree/LeafManager.h"
20 #include "openvdb/thread/Threading.h"
21 #include <openvdb/openvdb.h>
22
23 #include <tbb/blocked_range.h>
24 #include <tbb/parallel_reduce.h>
25
26 #include <cmath> // for std::isnan(), std::isfinite()
27 #include <set>
28 #include <sstream>
29 #include <string>
30 #include <type_traits>
31 #include <vector>
32
33 namespace openvdb {
34 OPENVDB_USE_VERSION_NAMESPACE
35 namespace OPENVDB_VERSION_NAME {
36 namespace tools {
37
38 ////////////////////////////////////////////////////////////////////////////////
39
40 /// @brief Perform checks on a grid to see if it is a valid symmetric,
41 /// narrow-band level set.
42 ///
43 /// @param grid Grid to be checked
44 /// @param number Number of the checks to be performed (see below)
45 /// @return string with a message indicating the nature of the
46 /// issue. If no issue is detected the return string is empty.
47 ///
48 /// @details @a number refers to the following ordered list of
49 /// checks - always starting from the top.
50 /// Fast checks
51 /// 1: value type is floating point
52 /// 2: has level set class type
53 /// 3: has uniform scale
54 /// 4: background value is positive and n*dx
55 ///
56 /// Slower checks
57 /// 5: no active tiles
58 /// 6: all the values are finite, i.e not NaN or infinite
59 /// 7: active values in range between +-background
60 /// 8: abs of inactive values = background, i.e. assuming a symmetric
61 /// narrow band!
62 ///
63 /// Relatively slow check (however multithreaded)
64 /// 9: norm gradient is close to one, i.e. satisfied the Eikonal equation.
65 template<class GridType>
66 std::string
67 checkLevelSet(const GridType& grid, size_t number=9);
68
69 ////////////////////////////////////////////////////////////////////////////////
70
71 /// @brief Perform checks on a grid to see if it is a valid fog volume.
72 ///
73 /// @param grid Grid to be checked
74 /// @param number Number of the checks to be performed (see below)
75 /// @return string with a message indicating the nature of the
76 /// issue. If no issue is detected the return string is empty.
77 ///
78 /// @details @a number refers to the following ordered list of
79 /// checks - always starting from the top.
80 /// Fast checks
81 /// 1: value type is floating point
82 /// 2: has FOG volume class type
83 /// 3: background value is zero
84 ///
85 /// Slower checks
86 /// 4: all the values are finite, i.e not NaN or infinite
87 /// 5: inactive values are zero
88 /// 6: active values are in the range [0,1]
89 template<class GridType>
90 std::string
91 checkFogVolume(const GridType& grid, size_t number=6);
92
93 ////////////////////////////////////////////////////////////////////////////////
94
95 /// @brief Threaded method to find unique inactive values.
96 ///
97 /// @param grid A VDB volume.
98 /// @param values List of unique inactive values, returned by this method.
99 /// @param numValues Number of values to look for.
100 /// @return @c false if the @a grid has more than @a numValues inactive values.
101 template<class GridType>
102 bool
103 uniqueInactiveValues(const GridType& grid,
104 std::vector<typename GridType::ValueType>& values, size_t numValues);
105
106
107 ////////////////////////////////////////////////////////////////////////////////
108
109 /// @brief Checks NaN values
110 template<typename GridT, typename TreeIterT = typename GridT::ValueOnCIter>
111 struct CheckNan
112 {
113 using ElementType = typename VecTraits<typename GridT::ValueType>::ElementType;
114 using TileIterT = TreeIterT;
115 using VoxelIterT = typename tree::IterTraits<
116 typename TreeIterT::NodeT, typename TreeIterT::ValueIterT>::template
117 NodeConverter<typename GridT::TreeType::LeafNodeType>::Type;
118
119 /// @brief Default constructor
CheckNanCheckNan120 CheckNan() {}
121
122 /// Return true if the scalar value is NaN
operatorCheckNan123 inline bool operator()(const ElementType& v) const { return std::isnan(v); }
124
125 /// @brief This allows for vector values to be checked component-wise
126 template<typename T>
127 inline typename std::enable_if<VecTraits<T>::IsVec, bool>::type
operatorCheckNan128 operator()(const T& v) const
129 {
130 for (int i=0; i<VecTraits<T>::Size; ++i) if ((*this)(v[i])) return true;//should unroll
131 return false;
132 }
133
134 /// @brief Return true if the tile at the iterator location is NaN
operatorCheckNan135 bool operator()(const TreeIterT &iter) const { return (*this)(*iter); }
136
137 /// @brief Return true if the voxel at the iterator location is NaN
operatorCheckNan138 bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); }
139
140 /// @brief Return a string describing a failed check.
strCheckNan141 std::string str() const { return "NaN"; }
142
143 };// CheckNan
144
145 ////////////////////////////////////////////////////////////////////////////////
146
147 /// @brief Checks for infinite values, e.g. 1/0 or -1/0
148 template <typename GridT,
149 typename TreeIterT = typename GridT::ValueOnCIter>
150 struct CheckInf
151 {
152 using ElementType = typename VecTraits<typename GridT::ValueType>::ElementType;
153 using TileIterT = TreeIterT;
154 using VoxelIterT = typename tree::IterTraits<typename TreeIterT::NodeT,
155 typename TreeIterT::ValueIterT> ::template NodeConverter<
156 typename GridT::TreeType::LeafNodeType>::Type;
157
158 /// @brief Default constructor
CheckInfCheckInf159 CheckInf() {}
160
161 /// Return true if the value is infinite
operatorCheckInf162 inline bool operator()(const ElementType& v) const { return std::isinf(v); }
163
164 /// Return true if any of the vector components are infinite.
165 template<typename T>
166 inline typename std::enable_if<VecTraits<T>::IsVec, bool>::type
operatorCheckInf167 operator()(const T& v) const
168 {
169 for (int i=0; i<VecTraits<T>::Size; ++i) if ((*this)(v[i])) return true;
170 return false;
171 }
172
173 /// @brief Return true if the tile at the iterator location is infinite
operatorCheckInf174 bool operator()(const TreeIterT &iter) const { return (*this)(*iter); }
175
176 /// @brief Return true if the tile at the iterator location is infinite
operatorCheckInf177 bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); }
178
179 /// @brief Return a string describing a failed check.
strCheckInf180 std::string str() const { return "infinite"; }
181 };// CheckInf
182
183 ////////////////////////////////////////////////////////////////////////////////
184
185 /// @brief Checks for both NaN and inf values, i.e. any value that is not finite.
186 template <typename GridT,
187 typename TreeIterT = typename GridT::ValueOnCIter>
188 struct CheckFinite
189 {
190 using ElementType = typename VecTraits<typename GridT::ValueType>::ElementType;
191 using TileIterT = TreeIterT;
192 using VoxelIterT = typename tree::IterTraits<typename TreeIterT::NodeT,
193 typename TreeIterT::ValueIterT> ::template NodeConverter<
194 typename GridT::TreeType::LeafNodeType>::Type;
195
196 /// @brief Default constructor
CheckFiniteCheckFinite197 CheckFinite() {}
198
199 /// Return true if the value is NOT finite, i.e. it's NaN or infinite
operatorCheckFinite200 inline bool operator()(const ElementType& v) const { return !std::isfinite(v); }
201
202 /// Return true if any of the vector components are NaN or infinite.
203 template<typename T>
204 inline typename std::enable_if<VecTraits<T>::IsVec, bool>::type
operatorCheckFinite205 operator()(const T& v) const {
206 for (int i=0; i<VecTraits<T>::Size; ++i) if ((*this)(v[i])) return true;
207 return false;
208 }
209
210 /// @brief Return true if the tile at the iterator location is NaN or infinite.
operatorCheckFinite211 bool operator()(const TreeIterT &iter) const { return (*this)(*iter); }
212
213 /// @brief Return true if the tile at the iterator location is NaN or infinite.
operatorCheckFinite214 bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); }
215
216 /// @brief Return a string describing a failed check.
strCheckFinite217 std::string str() const { return "not finite"; }
218 };// CheckFinite
219
220 ////////////////////////////////////////////////////////////////////////////////
221
222 /// @brief Check that the magnitude of a value, a, is close to a fixed
223 /// magnitude, b, given a fixed tolerance c. That is | |a| - |b| | <= c
224 template <typename GridT,
225 typename TreeIterT = typename GridT::ValueOffCIter>
226 struct CheckMagnitude
227 {
228 using ElementType = typename VecTraits<typename GridT::ValueType>::ElementType;
229 using TileIterT = TreeIterT;
230 using VoxelIterT = typename tree::IterTraits<typename TreeIterT::NodeT,
231 typename TreeIterT::ValueIterT> ::template NodeConverter<
232 typename GridT::TreeType::LeafNodeType>::Type;
233
234 /// @brief Default constructor
235 CheckMagnitude(const ElementType& a,
236 const ElementType& t = math::Tolerance<ElementType>::value())
absValCheckMagnitude237 : absVal(math::Abs(a)), tolVal(math::Abs(t))
238 {
239 }
240
241 /// Return true if the magnitude of the value is not approximately
242 /// equal to totVal.
operatorCheckMagnitude243 inline bool operator()(const ElementType& v) const
244 {
245 return math::Abs(math::Abs(v) - absVal) > tolVal;
246 }
247
248 /// Return true if any of the vector components are infinite.
249 template<typename T>
250 inline typename std::enable_if<VecTraits<T>::IsVec, bool>::type
operatorCheckMagnitude251 operator()(const T& v) const
252 {
253 for (int i=0; i<VecTraits<T>::Size; ++i) if ((*this)(v[i])) return true;
254 return false;
255 }
256
257 /// @brief Return true if the tile at the iterator location is infinite
operatorCheckMagnitude258 bool operator()(const TreeIterT &iter) const { return (*this)(*iter); }
259
260 /// @brief Return true if the tile at the iterator location is infinite
operatorCheckMagnitude261 bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); }
262
263 /// @brief Return a string describing a failed check.
strCheckMagnitude264 std::string str() const
265 {
266 std::ostringstream ss;
267 ss << "not equal to +/-"<<absVal<<" with a tolerance of "<<tolVal;
268 return ss.str();
269 }
270
271 const ElementType absVal, tolVal;
272 };// CheckMagnitude
273
274 ////////////////////////////////////////////////////////////////////////////////
275
276 /// @brief Checks a value against a range
277 template <typename GridT,
278 bool MinInclusive = true,//is min part of the range?
279 bool MaxInclusive = true,//is max part of the range?
280 typename TreeIterT = typename GridT::ValueOnCIter>
281 struct CheckRange
282 {
283 using ElementType = typename VecTraits<typename GridT::ValueType>::ElementType;
284 using TileIterT = TreeIterT;
285 using VoxelIterT = typename tree::IterTraits<typename TreeIterT::NodeT,
286 typename TreeIterT::ValueIterT> ::template NodeConverter<
287 typename GridT::TreeType::LeafNodeType>::Type;
288
289 // @brief Constructor taking a range to be tested against.
CheckRangeCheckRange290 CheckRange(const ElementType& _min, const ElementType& _max) : minVal(_min), maxVal(_max)
291 {
292 if (minVal > maxVal) {
293 OPENVDB_THROW(ValueError, "CheckRange: Invalid range (min > max)");
294 }
295 }
296
297 /// Return true if the value is smaller than min or larger than max.
operatorCheckRange298 inline bool operator()(const ElementType& v) const
299 {
300 return (MinInclusive ? v<minVal : v<=minVal) ||
301 (MaxInclusive ? v>maxVal : v>=maxVal);
302 }
303
304 /// Return true if any of the vector components are out of range.
305 template<typename T>
306 inline typename std::enable_if<VecTraits<T>::IsVec, bool>::type
operatorCheckRange307 operator()(const T& v) const {
308 for (int i=0; i<VecTraits<T>::Size; ++i) if ((*this)(v[i])) return true;
309 return false;
310 }
311
312 /// @brief Return true if the voxel at the iterator location is out of range.
operatorCheckRange313 bool operator()(const TreeIterT &iter) const { return (*this)(*iter); }
314
315 /// @brief Return true if the tile at the iterator location is out of range.
operatorCheckRange316 bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); }
317
318 /// @brief Return a string describing a failed check.
strCheckRange319 std::string str() const
320 {
321 std::ostringstream ss;
322 ss << "outside the value range " << (MinInclusive ? "[" : "]")
323 << minVal << "," << maxVal << (MaxInclusive ? "]" : "[");
324 return ss.str();
325 }
326
327 const ElementType minVal, maxVal;
328 };// CheckRange
329
330 ////////////////////////////////////////////////////////////////////////////////
331
332 /// @brief Checks a value against a minimum
333 template <typename GridT,
334 typename TreeIterT = typename GridT::ValueOnCIter>
335 struct CheckMin
336 {
337 using ElementType = typename VecTraits<typename GridT::ValueType>::ElementType;
338 using TileIterT = TreeIterT;
339 using VoxelIterT = typename tree::IterTraits<typename TreeIterT::NodeT,
340 typename TreeIterT::ValueIterT> ::template NodeConverter<
341 typename GridT::TreeType::LeafNodeType>::Type;
342
343 // @brief Constructor taking a minimum to be tested against.
CheckMinCheckMin344 CheckMin(const ElementType& _min) : minVal(_min) {}
345
346 /// Return true if the value is smaller than min.
operatorCheckMin347 inline bool operator()(const ElementType& v) const { return v<minVal; }
348
349 /// Return true if any of the vector components are smaller than min.
350 template<typename T>
351 inline typename std::enable_if<VecTraits<T>::IsVec, bool>::type
operatorCheckMin352 operator()(const T& v) const {
353 for (int i=0; i<VecTraits<T>::Size; ++i) if ((*this)(v[i])) return true;
354 return false;
355 }
356
357 /// @brief Return true if the voxel at the iterator location is smaller than min.
operatorCheckMin358 bool operator()(const TreeIterT &iter) const { return (*this)(*iter); }
359
360 /// @brief Return true if the tile at the iterator location is smaller than min.
operatorCheckMin361 bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); }
362
363 /// @brief Return a string describing a failed check.
strCheckMin364 std::string str() const
365 {
366 std::ostringstream ss;
367 ss << "smaller than "<<minVal;
368 return ss.str();
369 }
370
371 const ElementType minVal;
372 };// CheckMin
373
374 ////////////////////////////////////////////////////////////////////////////////
375
376 /// @brief Checks a value against a maximum
377 template <typename GridT,
378 typename TreeIterT = typename GridT::ValueOnCIter>
379 struct CheckMax
380 {
381 using ElementType = typename VecTraits<typename GridT::ValueType>::ElementType;
382 using TileIterT = TreeIterT;
383 using VoxelIterT = typename tree::IterTraits<typename TreeIterT::NodeT,
384 typename TreeIterT::ValueIterT> ::template NodeConverter<
385 typename GridT::TreeType::LeafNodeType>::Type;
386
387 /// @brief Constructor taking a maximum to be tested against.
CheckMaxCheckMax388 CheckMax(const ElementType& _max) : maxVal(_max) {}
389
390 /// Return true if the value is larger than max.
operatorCheckMax391 inline bool operator()(const ElementType& v) const { return v>maxVal; }
392
393 /// Return true if any of the vector components are larger than max.
394 template<typename T>
395 inline typename std::enable_if<VecTraits<T>::IsVec, bool>::type
operatorCheckMax396 operator()(const T& v) const {
397 for (int i=0; i<VecTraits<T>::Size; ++i) if ((*this)(v[i])) return true;
398 return false;
399 }
400
401 /// @brief Return true if the tile at the iterator location is larger than max.
operatorCheckMax402 bool operator()(const TreeIterT &iter) const { return (*this)(*iter); }
403
404 /// @brief Return true if the voxel at the iterator location is larger than max.
operatorCheckMax405 bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); }
406
407 /// @brief Return a string describing a failed check.
strCheckMax408 std::string str() const
409 {
410 std::ostringstream ss;
411 ss << "larger than "<<maxVal;
412 return ss.str();
413 }
414
415 const ElementType maxVal;
416 };// CheckMax
417
418 ////////////////////////////////////////////////////////////////////////////////
419
420 /// @brief Checks the norm of the gradient against a range, i.e.,
421 /// |∇Φ| ∈ [min, max]
422 ///
423 /// @note Internally the test is performed as
424 /// |∇Φ|² ∈ [min², max²] for optimization reasons.
425 template<typename GridT,
426 typename TreeIterT = typename GridT::ValueOnCIter,
427 math::BiasedGradientScheme GradScheme = math::FIRST_BIAS>//math::WENO5_BIAS>
428 struct CheckNormGrad
429 {
430 using ValueType = typename GridT::ValueType;
431 static_assert(std::is_floating_point<ValueType>::value,
432 "openvdb::tools::CheckNormGrad requires a scalar, floating-point grid");
433 using TileIterT = TreeIterT;
434 using VoxelIterT = typename tree::IterTraits<typename TreeIterT::NodeT,
435 typename TreeIterT::ValueIterT> ::template NodeConverter<
436 typename GridT::TreeType::LeafNodeType>::Type;
437 using AccT = typename GridT::ConstAccessor;
438
439 /// @brief Constructor taking a grid and a range to be tested against.
CheckNormGradCheckNormGrad440 CheckNormGrad(const GridT& grid, const ValueType& _min, const ValueType& _max)
441 : acc(grid.getConstAccessor())
442 , invdx2(ValueType(1.0/math::Pow2(grid.voxelSize()[0])))
443 , minVal2(_min*_min)
444 , maxVal2(_max*_max)
445 {
446 if ( !grid.hasUniformVoxels() ) {
447 OPENVDB_THROW(ValueError, "CheckNormGrad: The transform must have uniform scale");
448 }
449 if (_min > _max) {
450 OPENVDB_THROW(ValueError, "CheckNormGrad: Invalid range (min > max)");
451 }
452 }
453
CheckNormGradCheckNormGrad454 CheckNormGrad(const CheckNormGrad& other)
455 : acc(other.acc.tree())
456 , invdx2(other.invdx2)
457 , minVal2(other.minVal2)
458 , maxVal2(other.maxVal2)
459 {
460 }
461
462 /// Return true if the value is smaller than min or larger than max.
operatorCheckNormGrad463 inline bool operator()(const ValueType& v) const { return v<minVal2 || v>maxVal2; }
464
465 /// @brief Return true if zero is outside the range.
466 /// @note We assume that the norm of the gradient of a tile is always zero.
operatorCheckNormGrad467 inline bool operator()(const TreeIterT&) const { return (*this)(ValueType(0)); }
468
469 /// @brief Return true if the norm of the gradient at a voxel
470 /// location of the iterator is out of range.
operatorCheckNormGrad471 inline bool operator()(const VoxelIterT &iter) const
472 {
473 const Coord ijk = iter.getCoord();
474 return (*this)(invdx2 * math::ISGradientNormSqrd<GradScheme>::result(acc, ijk));
475 }
476
477 /// @brief Return a string describing a failed check.
strCheckNormGrad478 std::string str() const
479 {
480 std::ostringstream ss;
481 ss << "outside the range of NormGrad ["<<math::Sqrt(minVal2)<<","<<math::Sqrt(maxVal2)<<"]";
482 return ss.str();
483 }
484
485 AccT acc;
486 const ValueType invdx2, minVal2, maxVal2;
487 };// CheckNormGrad
488
489 ////////////////////////////////////////////////////////////////////////////////
490
491 /// @brief Checks the norm of the gradient at zero-crossing voxels against a range
492 /// @details CheckEikonal differs from CheckNormGrad in that it only
493 /// checks the norm of the gradient at voxel locations where the
494 /// FD-stencil crosses the zero isosurface!
495 template<typename GridT,
496 typename TreeIterT = typename GridT::ValueOnCIter,
497 typename StencilT = math::WenoStencil<GridT> >//math::GradStencil<GridT>
498 struct CheckEikonal
499 {
500 using ValueType = typename GridT::ValueType;
501 static_assert(std::is_floating_point<ValueType>::value,
502 "openvdb::tools::CheckEikonal requires a scalar, floating-point grid");
503 using TileIterT = TreeIterT;
504 using VoxelIterT = typename tree::IterTraits<typename TreeIterT::NodeT,
505 typename TreeIterT::ValueIterT> ::template NodeConverter<
506 typename GridT::TreeType::LeafNodeType>::Type;
507
508 /// @brief Constructor taking a grid and a range to be tested against.
CheckEikonalCheckEikonal509 CheckEikonal(const GridT& grid, const ValueType& _min, const ValueType& _max)
510 : stencil(grid), minVal(_min), maxVal(_max)
511 {
512 if ( !grid.hasUniformVoxels() ) {
513 OPENVDB_THROW(ValueError, "CheckEikonal: The transform must have uniform scale");
514 }
515 if (minVal > maxVal) {
516 OPENVDB_THROW(ValueError, "CheckEikonal: Invalid range (min > max)");
517 }
518 }
519
CheckEikonalCheckEikonal520 CheckEikonal(const CheckEikonal& other)
521 : stencil(other.stencil.grid()), minVal(other.minVal), maxVal(other.maxVal)
522 {
523 }
524
525 /// Return true if the value is smaller than min or larger than max.
operatorCheckEikonal526 inline bool operator()(const ValueType& v) const { return v<minVal || v>maxVal; }
527
528 /// @brief Return true if zero is outside the range.
529 /// @note We assume that the norm of the gradient of a tile is always zero.
operatorCheckEikonal530 inline bool operator()(const TreeIterT&) const { return (*this)(ValueType(0)); }
531
532 /// @brief Return true if the norm of the gradient at a
533 /// zero-crossing voxel location of the iterator is out of range.
operatorCheckEikonal534 inline bool operator()(const VoxelIterT &iter) const
535 {
536 stencil.moveTo(iter);
537 if (!stencil.zeroCrossing()) return false;
538 return (*this)(stencil.normSqGrad());
539 }
540
541 /// @brief Return a string describing a failed check.
strCheckEikonal542 std::string str() const
543 {
544 std::ostringstream ss;
545 ss << "outside the range of NormGrad ["<<minVal<<","<<maxVal<<"]";
546 return ss.str();
547 }
548
549 mutable StencilT stencil;
550 const ValueType minVal, maxVal;
551 };// CheckEikonal
552
553 ////////////////////////////////////////////////////////////////////////////////
554
555 /// @brief Checks the divergence against a range
556 template<typename GridT,
557 typename TreeIterT = typename GridT::ValueOnCIter,
558 math::DScheme DiffScheme = math::CD_2ND>
559 struct CheckDivergence
560 {
561 using ValueType = typename GridT::ValueType;
562 using ElementType = typename VecTraits<ValueType>::ElementType;
563 static_assert(std::is_floating_point<ElementType>::value,
564 "openvdb::tools::CheckDivergence requires a floating-point vector grid");
565 using TileIterT = TreeIterT;
566 using VoxelIterT = typename tree::IterTraits<typename TreeIterT::NodeT,
567 typename TreeIterT::ValueIterT>::template NodeConverter<
568 typename GridT::TreeType::LeafNodeType>::Type;
569 using AccT = typename GridT::ConstAccessor;
570
571 /// @brief Constructor taking a grid and a range to be tested against.
CheckDivergenceCheckDivergence572 CheckDivergence(const GridT& grid,
573 const ValueType& _min,
574 const ValueType& _max)
575 : acc(grid.getConstAccessor())
576 , invdx(ValueType(1.0/grid.voxelSize()[0]))
577 , minVal(_min)
578 , maxVal(_max)
579 {
580 if ( !grid.hasUniformVoxels() ) {
581 OPENVDB_THROW(ValueError, "CheckDivergence: The transform must have uniform scale");
582 }
583 if (minVal > maxVal) {
584 OPENVDB_THROW(ValueError, "CheckDivergence: Invalid range (min > max)");
585 }
586 }
587 /// Return true if the value is smaller than min or larger than max.
operatorCheckDivergence588 inline bool operator()(const ElementType& v) const { return v<minVal || v>maxVal; }
589
590 /// @brief Return true if zero is outside the range.
591 /// @note We assume that the divergence of a tile is always zero.
operatorCheckDivergence592 inline bool operator()(const TreeIterT&) const { return (*this)(ElementType(0)); }
593
594 /// @brief Return true if the divergence at a voxel location of
595 /// the iterator is out of range.
operatorCheckDivergence596 inline bool operator()(const VoxelIterT &iter) const
597 {
598 const Coord ijk = iter.getCoord();
599 return (*this)(invdx * math::ISDivergence<DiffScheme>::result(acc, ijk));
600 }
601
602 /// @brief Return a string describing a failed check.
strCheckDivergence603 std::string str() const
604 {
605 std::ostringstream ss;
606 ss << "outside the range of divergence ["<<minVal<<","<<maxVal<<"]";
607 return ss.str();
608 }
609
610 AccT acc;
611 const ValueType invdx, minVal, maxVal;
612 };// CheckDivergence
613
614 ////////////////////////////////////////////////////////////////////////////////
615
616 /// @brief Performs multithreaded diagnostics of a grid
617 /// @note More documentation will be added soon!
618 template <typename GridT>
619 class Diagnose
620 {
621 public:
622 using MaskType = typename GridT::template ValueConverter<bool>::Type;
623
Diagnose(const GridT & grid)624 Diagnose(const GridT& grid) : mGrid(&grid), mMask(new MaskType()), mCount(0)
625 {
626 mMask->setTransform(grid.transformPtr()->copy());
627 }
628
629 template <typename CheckT>
630 std::string check(const CheckT& check,
631 bool updateMask = false,
632 bool checkVoxels = true,
633 bool checkTiles = true,
634 bool checkBackground = true)
635 {
636 typename MaskType::TreeType* mask = updateMask ? &(mMask->tree()) : nullptr;
637 CheckValues<CheckT> cc(mask, mGrid, check);
638 std::ostringstream ss;
639 if (checkBackground) ss << cc.checkBackground();
640 if (checkTiles) ss << cc.checkTiles();
641 if (checkVoxels) ss << cc.checkVoxels();
642 mCount += cc.mCount;
643 return ss.str();
644 }
645
646 //@{
647 /// @brief Return a boolean mask of all the values
648 /// (i.e. tiles and/or voxels) that have failed one or
649 /// more checks.
mask()650 typename MaskType::ConstPtr mask() const { return mMask; }
mask()651 typename MaskType::Ptr mask() { return mMask; }
652 //@}
653
654 /// @brief Return the number of values (i.e. background, tiles or
655 /// voxels) that have failed one or more checks.
valueCount()656 Index64 valueCount() const { return mMask->activeVoxelCount(); }
657
658 /// @brief Return total number of failed checks
659 /// @note If only one check was performed and the mask was updated
660 /// failureCount equals valueCount.
failureCount()661 Index64 failureCount() const { return mCount; }
662
663 /// @brief Return a const reference to the grid
grid()664 const GridT& grid() const { return *mGrid; }
665
666 /// @brief Clear the mask and error counter
clear()667 void clear() { mMask = new MaskType(); mCount = 0; }
668
669 private:
670 // disallow copy construction and copy by assignment!
671 Diagnose(const Diagnose&);// not implemented
672 Diagnose& operator=(const Diagnose&);// not implemented
673
674 const GridT* mGrid;
675 typename MaskType::Ptr mMask;
676 Index64 mCount;
677
678 /// @brief Private class that performs the multithreaded checks
679 template <typename CheckT>
680 struct CheckValues
681 {
682 using MaskT = typename MaskType::TreeType;
683 using LeafT = typename GridT::TreeType::LeafNodeType;
684 using LeafManagerT = typename tree::LeafManager<const typename GridT::TreeType>;
685 const bool mOwnsMask;
686 MaskT* mMask;
687 const GridT* mGrid;
688 const CheckT mCheck;
689 Index64 mCount;
690
CheckValuesCheckValues691 CheckValues(MaskT* mask, const GridT* grid, const CheckT& check)
692 : mOwnsMask(false)
693 , mMask(mask)
694 , mGrid(grid)
695 , mCheck(check)
696 , mCount(0)
697 {
698 }
CheckValuesCheckValues699 CheckValues(CheckValues& other, tbb::split)
700 : mOwnsMask(true)
701 , mMask(other.mMask ? new MaskT() : nullptr)
702 , mGrid(other.mGrid)
703 , mCheck(other.mCheck)
704 , mCount(0)
705 {
706 }
~CheckValuesCheckValues707 ~CheckValues() { if (mOwnsMask) delete mMask; }
708
checkBackgroundCheckValues709 std::string checkBackground()
710 {
711 std::ostringstream ss;
712 if (mCheck(mGrid->background())) {
713 ++mCount;
714 ss << "Background is " + mCheck.str() << std::endl;
715 }
716 return ss.str();
717 }
718
checkTilesCheckValues719 std::string checkTiles()
720 {
721 std::ostringstream ss;
722 const Index64 n = mCount;
723 typename CheckT::TileIterT i(mGrid->tree());
724 for (i.setMaxDepth(GridT::TreeType::RootNodeType::LEVEL - 1); i; ++i) {
725 if (mCheck(i)) {
726 ++mCount;
727 if (mMask) mMask->fill(i.getBoundingBox(), true, true);
728 }
729 }
730 if (const Index64 m = mCount - n) {
731 ss << m << " tile" << (m==1 ? " is " : "s are ") + mCheck.str() << std::endl;
732 }
733 return ss.str();
734 }
735
checkVoxelsCheckValues736 std::string checkVoxels()
737 {
738 std::ostringstream ss;
739 LeafManagerT leafs(mGrid->tree());
740 const Index64 n = mCount;
741 tbb::parallel_reduce(leafs.leafRange(), *this);
742 if (const Index64 m = mCount - n) {
743 ss << m << " voxel" << (m==1 ? " is " : "s are ") + mCheck.str() << std::endl;
744 }
745 return ss.str();
746 }
747
operatorCheckValues748 void operator()(const typename LeafManagerT::LeafRange& r)
749 {
750 using VoxelIterT = typename CheckT::VoxelIterT;
751 if (mMask) {
752 for (typename LeafManagerT::LeafRange::Iterator i=r.begin(); i; ++i) {
753 typename MaskT::LeafNodeType* maskLeaf = nullptr;
754 for (VoxelIterT j = tree::IterTraits<LeafT, VoxelIterT>::begin(*i); j; ++j) {
755 if (mCheck(j)) {
756 ++mCount;
757 if (maskLeaf == nullptr) maskLeaf = mMask->touchLeaf(j.getCoord());
758 maskLeaf->setValueOn(j.pos(), true);
759 }
760 }
761 }
762 } else {
763 for (typename LeafManagerT::LeafRange::Iterator i=r.begin(); i; ++i) {
764 for (VoxelIterT j = tree::IterTraits<LeafT, VoxelIterT>::begin(*i); j; ++j) {
765 if (mCheck(j)) ++mCount;
766 }
767 }
768 }
769 }
joinCheckValues770 void join(const CheckValues& other)
771 {
772 if (mMask) mMask->merge(*(other.mMask), openvdb::MERGE_ACTIVE_STATES_AND_NODES);
773 mCount += other.mCount;
774 }
775 };//End of private class CheckValues
776
777 };// End of public class Diagnose
778
779
780 ////////////////////////////////////////////////////////////////////////////////
781
782 /// @brief Class that performs various types of checks on narrow-band level sets.
783 ///
784 /// @note The most common usage is to simply call CheckLevelSet::check()
785 template<class GridType>
786 class CheckLevelSet
787 {
788 public:
789 using ValueType = typename GridType::ValueType;
790 using MaskType = typename GridType::template ValueConverter<bool>::Type;
791
CheckLevelSet(const GridType & grid)792 CheckLevelSet(const GridType& grid) : mDiagnose(grid) {}
793
794 //@{
795 /// @brief Return a boolean mask of all the values
796 /// (i.e. tiles and/or voxels) that have failed one or
797 /// more checks.
mask()798 typename MaskType::ConstPtr mask() const { return mDiagnose.mask(); }
mask()799 typename MaskType::Ptr mask() { return mDiagnose.mask(); }
800 //@}
801
802 /// @brief Return the number of values (i.e. background, tiles or
803 /// voxels) that have failed one or more checks.
valueCount()804 Index64 valueCount() const { return mDiagnose.valueCount(); }
805
806 /// @brief Return total number of failed checks
807 /// @note If only one check was performed and the mask was updated
808 /// failureCount equals valueCount.
failureCount()809 Index64 failureCount() const { return mDiagnose.failureCount(); }
810
811 /// @brief Return a const reference to the grid
grid()812 const GridType& grid() const { return mDiagnose.grid(); }
813
814 /// @brief Clear the mask and error counter
clear()815 void clear() { mDiagnose.clear(); }
816
817 /// @brief Return a nonempty message if the grid's value type is a floating point.
818 ///
819 /// @note No run-time overhead
checkValueType()820 static std::string checkValueType()
821 {
822 static const bool test = std::is_floating_point<ValueType>::value;
823 return test ? "" : "Value type is not floating point\n";
824 }
825
826 /// @brief Return message if the grid's class is a level set.
827 ///
828 /// @note Small run-time overhead
checkClassType()829 std::string checkClassType() const
830 {
831 const bool test = mDiagnose.grid().getGridClass() == GRID_LEVEL_SET;
832 return test ? "" : "Class type is not \"GRID_LEVEL_SET\"\n";
833 }
834
835 /// @brief Return a nonempty message if the grid's transform does not have uniform scaling.
836 ///
837 /// @note Small run-time overhead
checkTransform()838 std::string checkTransform() const
839 {
840 return mDiagnose.grid().hasUniformVoxels() ? "" : "Does not have uniform voxels\n";
841 }
842
843 /// @brief Return a nonempty message if the background value is larger than or
844 /// equal to the halfWidth*voxelSize.
845 ///
846 /// @note Small run-time overhead
847 std::string checkBackground(Real halfWidth = LEVEL_SET_HALF_WIDTH) const
848 {
849 const Real w = mDiagnose.grid().background() / mDiagnose.grid().voxelSize()[0];
850 if (w < halfWidth) {
851 std::ostringstream ss;
852 ss << "The background value ("<< mDiagnose.grid().background()<<") is less than "
853 << halfWidth << " voxel units\n";
854 return ss.str();
855 }
856 return "";
857 }
858
859 /// @brief Return a nonempty message if the grid has no active tile values.
860 ///
861 /// @note Medium run-time overhead
checkTiles()862 std::string checkTiles() const
863 {
864 const bool test = mDiagnose.grid().tree().hasActiveTiles();
865 return test ? "Has active tile values\n" : "";
866 }
867
868 /// @brief Return a nonempty message if any of the values are not finite. i.e. NaN or inf.
869 ///
870 /// @note Medium run-time overhead
871 std::string checkFinite(bool updateMask = false)
872 {
873 CheckFinite<GridType,typename GridType::ValueAllCIter> c;
874 return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/true);
875 }
876
877 /// @brief Return a nonempty message if the active voxel values are out-of-range.
878 ///
879 /// @note Medium run-time overhead
880 std::string checkRange(bool updateMask = false)
881 {
882 const ValueType& background = mDiagnose.grid().background();
883 CheckRange<GridType> c(-background, background);
884 return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/false, /*background*/false);
885 }
886
887 /// @brief Return a nonempty message if the the inactive values do not have a
888 /// magnitude equal to the background value.
889 ///
890 /// @note Medium run-time overhead
891 std::string checkInactiveValues(bool updateMask = false)
892 {
893 const ValueType& background = mDiagnose.grid().background();
894 CheckMagnitude<GridType, typename GridType::ValueOffCIter> c(background);
895 return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/false);
896 }
897
898 /// @brief Return a nonempty message if the norm of the gradient of the
899 /// active voxels is out of the range minV to maxV.
900 ///
901 /// @note Significant run-time overhead
902 std::string checkEikonal(bool updateMask = false, ValueType minV = 0.5, ValueType maxV = 1.5)
903 {
904 CheckEikonal<GridType> c(mDiagnose.grid(), minV, maxV);
905 return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/false, /*background*/false);
906 }
907
908 /// @brief Return a nonempty message if an error or issue is detected. Only
909 /// runs tests with a number lower than or equal to n, where:
910 ///
911 /// Fast checks
912 /// 1: value type is floating point
913 /// 2: has level set class type
914 /// 3: has uniform scale
915 /// 4: background value is positive and n*dx
916 ///
917 /// Slower checks
918 /// 5: no active tiles
919 /// 6: all the values are finite, i.e not NaN or infinite
920 /// 7: active values in range between +-background
921 /// 8: abs of inactive values = background, i.e. assuming a symmetric narrow band!
922 ///
923 /// Relatively slow check (however multi-threaded)
924 /// 9: norm of gradient at zero-crossings is one, i.e. satisfied the Eikonal equation.
925 std::string check(size_t n=9, bool updateMask = false)
926 {
927 std::string str = this->checkValueType();
928 if (str.empty() && n>1) str = this->checkClassType();
929 if (str.empty() && n>2) str = this->checkTransform();
930 if (str.empty() && n>3) str = this->checkBackground();
931 if (str.empty() && n>4) str = this->checkTiles();
932 if (str.empty() && n>5) str = this->checkFinite(updateMask);
933 if (str.empty() && n>6) str = this->checkRange(updateMask);
934 if (str.empty() && n>7) str = this->checkInactiveValues(updateMask);
935 if (str.empty() && n>8) str = this->checkEikonal(updateMask);
936 return str;
937 }
938
939 private:
940 // disallow copy construction and copy by assignment!
941 CheckLevelSet(const CheckLevelSet&);// not implemented
942 CheckLevelSet& operator=(const CheckLevelSet&);// not implemented
943
944 // Member data
945 Diagnose<GridType> mDiagnose;
946 };// CheckLevelSet
947
948 template<class GridType>
949 std::string
checkLevelSet(const GridType & grid,size_t n)950 checkLevelSet(const GridType& grid, size_t n)
951 {
952 CheckLevelSet<GridType> c(grid);
953 return c.check(n, false);
954 }
955
956 ////////////////////////////////////////////////////////////////////////////////
957
958 /// @brief Class that performs various types of checks on fog volumes.
959 ///
960 /// @note The most common usage is to simply call CheckFogVolume::check()
961 template<class GridType>
962 class CheckFogVolume
963 {
964 public:
965 using ValueType = typename GridType::ValueType;
966 using MaskType = typename GridType::template ValueConverter<bool>::Type;
967
CheckFogVolume(const GridType & grid)968 CheckFogVolume(const GridType& grid) : mDiagnose(grid) {}
969
970 //@{
971 /// @brief Return a boolean mask of all the values
972 /// (i.e. tiles and/or voxels) that have failed one or
973 /// more checks.
mask()974 typename MaskType::ConstPtr mask() const { return mDiagnose.mask(); }
mask()975 typename MaskType::Ptr mask() { return mDiagnose.mask(); }
976 //@}
977
978 /// @brief Return the number of values (i.e. background, tiles or
979 /// voxels) that have failed one or more checks.
valueCount()980 Index64 valueCount() const { return mDiagnose.valueCount(); }
981
982 /// @brief Return total number of failed checks
983 /// @note If only one check was performed and the mask was updated
984 /// failureCount equals valueCount.
failureCount()985 Index64 failureCount() const { return mDiagnose.failureCount(); }
986
987 /// @brief Return a const reference to the grid
grid()988 const GridType& grid() const { return mDiagnose.grid(); }
989
990 /// @brief Clear the mask and error counter
clear()991 void clear() { mDiagnose.clear(); }
992
993 /// @brief Return a nonempty message if the grid's value type is a floating point.
994 ///
995 /// @note No run-time overhead
checkValueType()996 static std::string checkValueType()
997 {
998 static const bool test = std::is_floating_point<ValueType>::value;
999 return test ? "" : "Value type is not floating point";
1000 }
1001
1002 /// @brief Return a nonempty message if the grid's class is a level set.
1003 ///
1004 /// @note Small run-time overhead
checkClassType()1005 std::string checkClassType() const
1006 {
1007 const bool test = mDiagnose.grid().getGridClass() == GRID_FOG_VOLUME;
1008 return test ? "" : "Class type is not \"GRID_LEVEL_SET\"";
1009 }
1010
1011 /// @brief Return a nonempty message if the background value is not zero.
1012 ///
1013 /// @note Small run-time overhead
checkBackground()1014 std::string checkBackground() const
1015 {
1016 if (!math::isApproxZero(mDiagnose.grid().background())) {
1017 std::ostringstream ss;
1018 ss << "The background value ("<< mDiagnose.grid().background()<<") is not zero";
1019 return ss.str();
1020 }
1021 return "";
1022 }
1023
1024 /// @brief Return a nonempty message if any of the values are not finite. i.e. NaN or inf.
1025 ///
1026 /// @note Medium run-time overhead
1027 std::string checkFinite(bool updateMask = false)
1028 {
1029 CheckFinite<GridType,typename GridType::ValueAllCIter> c;
1030 return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/true);
1031 }
1032
1033 /// @brief Return a nonempty message if any of the inactive values are not zero.
1034 ///
1035 /// @note Medium run-time overhead
1036 std::string checkInactiveValues(bool updateMask = false)
1037 {
1038 CheckMagnitude<GridType, typename GridType::ValueOffCIter> c(0);
1039 return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/true);
1040 }
1041
1042 /// @brief Return a nonempty message if the active voxel values
1043 /// are out-of-range, i.e. not in the range [0,1].
1044 ///
1045 /// @note Medium run-time overhead
1046 std::string checkRange(bool updateMask = false)
1047 {
1048 CheckRange<GridType> c(0, 1);
1049 return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/false);
1050 }
1051
1052 /// @brief Return a nonempty message if an error or issue is detected. Only
1053 /// runs tests with a number lower than or equal to n, where:
1054 ///
1055 /// Fast checks
1056 /// 1: value type is floating point
1057 /// 2: has FOG volume class type
1058 /// 3: background value is zero
1059 ///
1060 /// Slower checks
1061 /// 4: all the values are finite, i.e not NaN or infinite
1062 /// 5: inactive values are zero
1063 /// 6: active values are in the range [0,1]
1064 std::string check(size_t n=6, bool updateMask = false)
1065 {
1066 std::string str = this->checkValueType();
1067 if (str.empty() && n>1) str = this->checkClassType();
1068 if (str.empty() && n>2) str = this->checkBackground();
1069 if (str.empty() && n>3) str = this->checkFinite(updateMask);
1070 if (str.empty() && n>4) str = this->checkInactiveValues(updateMask);
1071 if (str.empty() && n>5) str = this->checkRange(updateMask);
1072 return str;
1073 }
1074
1075 private:
1076 // disallow copy construction and copy by assignment!
1077 CheckFogVolume(const CheckFogVolume&);// not implemented
1078 CheckFogVolume& operator=(const CheckFogVolume&);// not implemented
1079
1080 // Member data
1081 Diagnose<GridType> mDiagnose;
1082 };// CheckFogVolume
1083
1084 template<class GridType>
1085 std::string
checkFogVolume(const GridType & grid,size_t n)1086 checkFogVolume(const GridType& grid, size_t n)
1087 {
1088 CheckFogVolume<GridType> c(grid);
1089 return c.check(n, false);
1090 }
1091
1092
1093 ////////////////////////////////////////////////////////////////////////////////
1094
1095 // Internal utility objects and implementation details
1096
1097 /// @cond OPENVDB_DOCS_INTERNAL
1098
1099 namespace diagnostics_internal {
1100
1101
1102 template<typename TreeType>
1103 class InactiveVoxelValues
1104 {
1105 public:
1106 using LeafArray = tree::LeafManager<TreeType>;
1107 using ValueType = typename TreeType::ValueType;
1108 using SetType = std::set<ValueType>;
1109
1110 InactiveVoxelValues(LeafArray&, size_t numValues);
1111
1112 void runParallel();
1113 void runSerial();
1114
1115 void getInactiveValues(SetType&) const;
1116
1117 inline InactiveVoxelValues(const InactiveVoxelValues<TreeType>&, tbb::split);
1118 inline void operator()(const tbb::blocked_range<size_t>&);
1119 inline void join(const InactiveVoxelValues<TreeType>&);
1120
1121 private:
1122 LeafArray& mLeafArray;
1123 SetType mInactiveValues;
1124 size_t mNumValues;
1125 };// InactiveVoxelValues
1126
1127 template<typename TreeType>
InactiveVoxelValues(LeafArray & leafs,size_t numValues)1128 InactiveVoxelValues<TreeType>::InactiveVoxelValues(LeafArray& leafs, size_t numValues)
1129 : mLeafArray(leafs)
1130 , mInactiveValues()
1131 , mNumValues(numValues)
1132 {
1133 }
1134
1135 template <typename TreeType>
1136 inline
InactiveVoxelValues(const InactiveVoxelValues<TreeType> & rhs,tbb::split)1137 InactiveVoxelValues<TreeType>::InactiveVoxelValues(
1138 const InactiveVoxelValues<TreeType>& rhs, tbb::split)
1139 : mLeafArray(rhs.mLeafArray)
1140 , mInactiveValues()
1141 , mNumValues(rhs.mNumValues)
1142 {
1143 }
1144
1145 template<typename TreeType>
1146 void
runParallel()1147 InactiveVoxelValues<TreeType>::runParallel()
1148 {
1149 tbb::parallel_reduce(mLeafArray.getRange(), *this);
1150 }
1151
1152
1153 template<typename TreeType>
1154 void
runSerial()1155 InactiveVoxelValues<TreeType>::runSerial()
1156 {
1157 (*this)(mLeafArray.getRange());
1158 }
1159
1160
1161 template<typename TreeType>
1162 inline void
operator()1163 InactiveVoxelValues<TreeType>::operator()(const tbb::blocked_range<size_t>& range)
1164 {
1165 typename TreeType::LeafNodeType::ValueOffCIter iter;
1166
1167 for (size_t n = range.begin(); n < range.end() && !thread::isGroupExecutionCancelled(); ++n) {
1168 for (iter = mLeafArray.leaf(n).cbeginValueOff(); iter; ++iter) {
1169 mInactiveValues.insert(iter.getValue());
1170 }
1171
1172 if (mInactiveValues.size() > mNumValues) {
1173 thread::cancelGroupExecution();
1174 }
1175 }
1176 }
1177
1178 template<typename TreeType>
1179 inline void
join(const InactiveVoxelValues<TreeType> & rhs)1180 InactiveVoxelValues<TreeType>::join(const InactiveVoxelValues<TreeType>& rhs)
1181 {
1182 mInactiveValues.insert(rhs.mInactiveValues.begin(), rhs.mInactiveValues.end());
1183 }
1184
1185 template<typename TreeType>
1186 inline void
getInactiveValues(SetType & values)1187 InactiveVoxelValues<TreeType>::getInactiveValues(SetType& values) const
1188 {
1189 values.insert(mInactiveValues.begin(), mInactiveValues.end());
1190 }
1191
1192
1193 ////////////////////////////////////////
1194
1195
1196 template<typename TreeType>
1197 class InactiveTileValues
1198 {
1199 public:
1200 using IterRange = tree::IteratorRange<typename TreeType::ValueOffCIter>;
1201 using ValueType = typename TreeType::ValueType;
1202 using SetType = std::set<ValueType>;
1203
1204 InactiveTileValues(size_t numValues);
1205
1206 void runParallel(IterRange&);
1207 void runSerial(IterRange&);
1208
1209 void getInactiveValues(SetType&) const;
1210
1211 inline InactiveTileValues(const InactiveTileValues<TreeType>&, tbb::split);
1212 inline void operator()(IterRange&);
1213 inline void join(const InactiveTileValues<TreeType>&);
1214
1215 private:
1216 SetType mInactiveValues;
1217 size_t mNumValues;
1218 };
1219
1220
1221 template<typename TreeType>
InactiveTileValues(size_t numValues)1222 InactiveTileValues<TreeType>::InactiveTileValues(size_t numValues)
1223 : mInactiveValues()
1224 , mNumValues(numValues)
1225 {
1226 }
1227
1228 template <typename TreeType>
1229 inline
InactiveTileValues(const InactiveTileValues<TreeType> & rhs,tbb::split)1230 InactiveTileValues<TreeType>::InactiveTileValues(
1231 const InactiveTileValues<TreeType>& rhs, tbb::split)
1232 : mInactiveValues()
1233 , mNumValues(rhs.mNumValues)
1234 {
1235 }
1236
1237 template<typename TreeType>
1238 void
runParallel(IterRange & range)1239 InactiveTileValues<TreeType>::runParallel(IterRange& range)
1240 {
1241 tbb::parallel_reduce(range, *this);
1242 }
1243
1244
1245 template<typename TreeType>
1246 void
runSerial(IterRange & range)1247 InactiveTileValues<TreeType>::runSerial(IterRange& range)
1248 {
1249 (*this)(range);
1250 }
1251
1252
1253 template<typename TreeType>
1254 inline void
operator()1255 InactiveTileValues<TreeType>::operator()(IterRange& range)
1256 {
1257 for (; range && !thread::isGroupExecutionCancelled(); ++range) {
1258 typename TreeType::ValueOffCIter iter = range.iterator();
1259 for (; iter; ++iter) {
1260 mInactiveValues.insert(iter.getValue());
1261 }
1262
1263 if (mInactiveValues.size() > mNumValues) {
1264 thread::cancelGroupExecution();
1265 }
1266 }
1267 }
1268
1269 template<typename TreeType>
1270 inline void
join(const InactiveTileValues<TreeType> & rhs)1271 InactiveTileValues<TreeType>::join(const InactiveTileValues<TreeType>& rhs)
1272 {
1273 mInactiveValues.insert(rhs.mInactiveValues.begin(), rhs.mInactiveValues.end());
1274 }
1275
1276 template<typename TreeType>
1277 inline void
getInactiveValues(SetType & values)1278 InactiveTileValues<TreeType>::getInactiveValues(SetType& values) const
1279 {
1280 values.insert(mInactiveValues.begin(), mInactiveValues.end());
1281 }
1282
1283 } // namespace diagnostics_internal
1284
1285 /// @endcond
1286
1287 ////////////////////////////////////////
1288
1289
1290 template<class GridType>
1291 bool
uniqueInactiveValues(const GridType & grid,std::vector<typename GridType::ValueType> & values,size_t numValues)1292 uniqueInactiveValues(const GridType& grid,
1293 std::vector<typename GridType::ValueType>& values, size_t numValues)
1294 {
1295 using TreeType = typename GridType::TreeType;
1296 using ValueType = typename GridType::ValueType;
1297 using SetType = std::set<ValueType>;
1298
1299 SetType uniqueValues;
1300
1301 { // Check inactive voxels
1302 TreeType& tree = const_cast<TreeType&>(grid.tree());
1303 tree::LeafManager<TreeType> leafs(tree);
1304 diagnostics_internal::InactiveVoxelValues<TreeType> voxelOp(leafs, numValues);
1305 voxelOp.runParallel();
1306 voxelOp.getInactiveValues(uniqueValues);
1307 }
1308
1309 // Check inactive tiles
1310 if (uniqueValues.size() <= numValues) {
1311 typename TreeType::ValueOffCIter iter(grid.tree());
1312 iter.setMaxDepth(TreeType::ValueAllIter::LEAF_DEPTH - 1);
1313 diagnostics_internal::InactiveTileValues<TreeType> tileOp(numValues);
1314
1315 tree::IteratorRange<typename TreeType::ValueOffCIter> range(iter);
1316 tileOp.runParallel(range);
1317
1318 tileOp.getInactiveValues(uniqueValues);
1319 }
1320
1321 values.clear();
1322 values.reserve(uniqueValues.size());
1323
1324 typename SetType::iterator it = uniqueValues.begin();
1325 for ( ; it != uniqueValues.end(); ++it) {
1326 values.push_back(*it);
1327 }
1328
1329 return values.size() <= numValues;
1330 }
1331
1332
1333 ////////////////////////////////////////
1334
1335
1336 // Explicit Template Instantiation
1337
1338 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
1339
1340 #ifdef OPENVDB_INSTANTIATE_DIAGNOSTICS
1341 #include <openvdb/util/ExplicitInstantiation.h>
1342 #endif
1343
1344 #define _FUNCTION(TreeT) \
1345 std::string checkLevelSet(const Grid<TreeT>&, size_t)
1346 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1347 #undef _FUNCTION
1348
1349 #define _FUNCTION(TreeT) \
1350 std::string checkFogVolume(const Grid<TreeT>&, size_t)
1351 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
1352 #undef _FUNCTION
1353
1354 #define _FUNCTION(TreeT) \
1355 bool uniqueInactiveValues(const Grid<TreeT>&, std::vector<TreeT::ValueType>&, size_t)
1356 OPENVDB_VOLUME_TREE_INSTANTIATE(_FUNCTION)
1357 #undef _FUNCTION
1358
1359 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
1360
1361
1362 } // namespace tools
1363 } // namespace OPENVDB_VERSION_NAME
1364 } // namespace openvdb
1365
1366 #endif // OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED
1367