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 /// |&nabla;&Phi;| &isin; [min, max]
422 ///
423 /// @note Internally the test is performed as
424 /// |&nabla;&Phi;|&sup2; &isin; [min&sup2;, max&sup2;] 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