Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | // | ||
4 | /// @author Ken Museth | ||
5 | /// | ||
6 | /// @file LevelSetMeasure.h | ||
7 | |||
8 | #ifndef OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED | ||
9 | #define OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED | ||
10 | |||
11 | #include "openvdb/Types.h" | ||
12 | #include "openvdb/Grid.h" | ||
13 | #include "openvdb/tree/LeafManager.h" | ||
14 | #include "openvdb/tree/ValueAccessor.h" | ||
15 | #include "openvdb/math/Math.h" | ||
16 | #include "openvdb/math/FiniteDifference.h" | ||
17 | #include "openvdb/math/Operators.h" | ||
18 | #include "openvdb/math/Stencils.h" | ||
19 | #include "openvdb/util/NullInterrupter.h" | ||
20 | #include "openvdb/thread/Threading.h" | ||
21 | #include <openvdb/openvdb.h> | ||
22 | |||
23 | #include <tbb/parallel_for.h> | ||
24 | #include <tbb/parallel_sort.h> | ||
25 | #include <tbb/parallel_invoke.h> | ||
26 | |||
27 | #include <type_traits> | ||
28 | |||
29 | namespace openvdb { | ||
30 | OPENVDB_USE_VERSION_NAMESPACE | ||
31 | namespace OPENVDB_VERSION_NAME { | ||
32 | namespace tools { | ||
33 | |||
34 | /// @brief Return the surface area of a narrow-band level set. | ||
35 | /// | ||
36 | /// @param grid a scalar, floating-point grid with one or more disjoint, | ||
37 | /// closed level set surfaces | ||
38 | /// @param useWorldSpace if true the area is computed in | ||
39 | /// world space units, else in voxel units. | ||
40 | /// | ||
41 | /// @throw TypeError if @a grid is not scalar or not floating-point or not a level set or empty. | ||
42 | template<class GridType> | ||
43 | Real | ||
44 | levelSetArea(const GridType& grid, bool useWorldSpace = true); | ||
45 | |||
46 | /// @brief Return the volume of a narrow-band level set surface. | ||
47 | /// | ||
48 | /// @param grid a scalar, floating-point grid with one or more disjoint, | ||
49 | /// closed level set surfaces | ||
50 | /// @param useWorldSpace if true the volume is computed in | ||
51 | /// world space units, else in voxel units. | ||
52 | /// | ||
53 | /// @throw TypeError if @a grid is not scalar or not floating-point or not a level set or empty. | ||
54 | template<class GridType> | ||
55 | Real | ||
56 | levelSetVolume(const GridType& grid, bool useWorldSpace = true); | ||
57 | |||
58 | /// @brief Return the Euler Characteristics of a narrow-band level set surface (possibly disconnected). | ||
59 | /// | ||
60 | /// @param grid a scalar, floating-point grid with one or more disjoint, | ||
61 | /// closed level set surfaces | ||
62 | /// | ||
63 | /// @throw TypeError if @a grid is not scalar or not floating-point or not a level set or empty. | ||
64 | template<class GridType> | ||
65 | int | ||
66 | levelSetEulerCharacteristic(const GridType& grid); | ||
67 | |||
68 | /// @brief Return the genus of a narrow-band level set surface. | ||
69 | /// | ||
70 | /// @param grid a scalar, floating-point grid with one or more disjoint, | ||
71 | /// closed level set surfaces | ||
72 | /// @warning The genus is only well defined for a single connected surface | ||
73 | /// | ||
74 | /// @throw TypeError if @a grid is not scalar or not floating-point or not a level set or empty. | ||
75 | template<class GridType> | ||
76 | int | ||
77 | levelSetGenus(const GridType& grid); | ||
78 | |||
79 | //////////////////////////////////////////////////////////////////////////////////////// | ||
80 | |||
81 | /// @brief Smeared-out and continuous Dirac Delta function. | ||
82 | template<typename RealT> | ||
83 | class DiracDelta | ||
84 | { | ||
85 | public: | ||
86 | // eps is the half-width of the dirac delta function in units of phi | ||
87 | DiracDelta(RealT eps) : mC(0.5/eps), mD(2*math::pi<RealT>()*mC), mE(eps) {} | ||
88 | // values of the dirac delta function are in units of one over the units of phi | ||
89 |
4/8✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2441734 times.
✓ Branch 5 taken 2445978 times.
✓ Branch 6 taken 9640106 times.
✓ Branch 7 taken 9475644 times.
|
24003462 | inline RealT operator()(RealT phi) const { return math::Abs(phi) > mE ? 0 : mC*(1+cos(mD*phi)); } |
90 | private: | ||
91 | const RealT mC, mD, mE; | ||
92 | };// DiracDelta functor | ||
93 | |||
94 | |||
95 | /// @brief Multi-threaded computation of surface area, volume and | ||
96 | /// average mean-curvature for narrow band level sets. | ||
97 | /// | ||
98 | /// @details To reduce the risk of round-off errors (primarily due to | ||
99 | /// catastrophic cancellation) and guarantee determinism during | ||
100 | /// multi-threading this class is implemented using parallel_for, and | ||
101 | /// delayed reduction of a sorted list. | ||
102 | template<typename GridT, typename InterruptT = util::NullInterrupter> | ||
103 | class LevelSetMeasure | ||
104 | { | ||
105 | public: | ||
106 | using GridType = GridT; | ||
107 | using TreeType = typename GridType::TreeType; | ||
108 | using ValueType = typename TreeType::ValueType; | ||
109 | using ManagerType = typename tree::LeafManager<const TreeType>; | ||
110 | |||
111 | static_assert(std::is_floating_point<ValueType>::value, | ||
112 | "level set measure is supported only for scalar, floating-point grids"); | ||
113 | |||
114 | /// @brief Main constructor from a grid | ||
115 | /// @param grid The level set to be measured. | ||
116 | /// @param interrupt Optional interrupter. | ||
117 | /// @throw RuntimeError if the grid is not a level set or if it's empty. | ||
118 | LevelSetMeasure(const GridType& grid, InterruptT* interrupt = nullptr); | ||
119 | |||
120 | /// @brief Re-initialize using the specified grid. | ||
121 | /// @param grid The level set to be measured. | ||
122 | /// @throw RuntimeError if the grid is not a level set or if it's empty. | ||
123 | void init(const GridType& grid); | ||
124 | |||
125 | /// @brief Destructor | ||
126 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
64 | virtual ~LevelSetMeasure() {} |
127 | |||
128 | /// @return the grain-size used for multi-threading | ||
129 | ✗ | int getGrainSize() const { return mGrainSize; } | |
130 | |||
131 | /// @brief Set the grain-size used for multi-threading. | ||
132 | /// @note A grain size of 0 or less disables multi-threading! | ||
133 | ✗ | void setGrainSize(int grainsize) { mGrainSize = grainsize; } | |
134 | |||
135 | /// @brief Compute the surface area of the level set. | ||
136 | /// @param useWorldUnits Specifies if the result is in world or voxel units. | ||
137 | /// @note Performs internal caching so only the initial call incurs actual computation. | ||
138 | Real area(bool useWorldUnits = true); | ||
139 | |||
140 | /// @brief Compute the volume of the level set surface. | ||
141 | /// @param useWorldUnits Specifies if the result is in world or voxel units. | ||
142 | /// @note Performs internal caching so only the initial call incurs actual computation. | ||
143 | Real volume(bool useWorldUnits = true); | ||
144 | |||
145 | /// @brief Compute the total mean curvature of the level set surface. | ||
146 | /// @param useWorldUnits Specifies if the result is in world or voxel units. | ||
147 | /// @note Performs internal caching so only the initial call incurs actual computation. | ||
148 | Real totMeanCurvature(bool useWorldUnits = true); | ||
149 | |||
150 | /// @brief Compute the total gaussian curvature of the level set surface. | ||
151 | /// @param useWorldUnits Specifies if the result is in world or voxel units. | ||
152 | /// @note Performs internal caching so only the initial call incurs actual computation. | ||
153 | Real totGaussianCurvature(bool useWorldUnits = true); | ||
154 | |||
155 | /// @brief Compute the average mean curvature of the level set surface. | ||
156 | /// @param useWorldUnits Specifies if the result is in world or voxel units. | ||
157 | /// @note Performs internal caching so only the initial call incurs actual computation. | ||
158 | ✗ | Real avgMeanCurvature(bool useWorldUnits = true) {return this->totMeanCurvature(useWorldUnits) / this->area(useWorldUnits);} | |
159 | |||
160 | /// @brief Compute the average gaussian curvature of the level set surface. | ||
161 | /// @param useWorldUnits Specifies if the result is in world or voxel units. | ||
162 | /// @note Performs internal caching so only the initial call incurs actual computation. | ||
163 | ✗ | Real avgGaussianCurvature(bool useWorldUnits = true) {return this->totGaussianCurvature(useWorldUnits) / this->area(useWorldUnits); } | |
164 | |||
165 | /// @brief Compute the Euler characteristic of the level set surface. | ||
166 | /// @note Performs internal caching so only the initial call incurs actual computation. | ||
167 | int eulerCharacteristic(); | ||
168 | |||
169 | /// @brief Compute the genus of the level set surface. | ||
170 | /// @warning The genus is only well defined for a single connected surface. | ||
171 | /// @note Performs internal caching so only the initial call incurs actual computation. | ||
172 |
6/12✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
|
8 | int genus() { return 1 - this->eulerCharacteristic()/2;} |
173 | |||
174 | private: | ||
175 | |||
176 | using LeafT = typename TreeType::LeafNodeType; | ||
177 | using VoxelCIterT = typename LeafT::ValueOnCIter; | ||
178 | using LeafRange = typename ManagerType::LeafRange; | ||
179 | using LeafIterT = typename LeafRange::Iterator; | ||
180 | using ManagerPtr = std::unique_ptr<ManagerType>; | ||
181 | using BufferPtr = std::unique_ptr<double[]>; | ||
182 | |||
183 | // disallow copy construction and copy by assignment! | ||
184 | LevelSetMeasure(const LevelSetMeasure&);// not implemented | ||
185 | LevelSetMeasure& operator=(const LevelSetMeasure&);// not implemented | ||
186 | |||
187 | const GridType *mGrid; | ||
188 | ManagerPtr mLeafs; | ||
189 | BufferPtr mBuffer; | ||
190 | InterruptT *mInterrupter; | ||
191 | double mDx, mArea, mVolume, mTotMeanCurvature, mTotGausCurvature; | ||
192 | int mGrainSize; | ||
193 | bool mUpdateArea, mUpdateCurvature; | ||
194 | |||
195 | // @brief Return false if the process was interrupted | ||
196 | bool checkInterrupter(); | ||
197 | |||
198 | ✗ | struct MeasureArea | |
199 | { | ||
200 | 8 | MeasureArea(LevelSetMeasure* parent) : mParent(parent), mStencil(*mParent->mGrid) | |
201 | { | ||
202 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
8 | if (parent->mInterrupter) parent->mInterrupter->start("Measuring area and volume of level set"); |
203 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
8 | if (parent->mGrainSize>0) { |
204 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | tbb::parallel_for(parent->mLeafs->leafRange(parent->mGrainSize), *this); |
205 | } else { | ||
206 | ✗ | (*this)(parent->mLeafs->leafRange()); | |
207 | } | ||
208 |
1/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
12 | tbb::parallel_invoke([&](){parent->mArea = parent->reduce(0);}, |
209 | 4 | [&](){parent->mVolume = parent->reduce(1)/3.0;}); | |
210 | 8 | parent->mUpdateArea = false; | |
211 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
8 | if (parent->mInterrupter) parent->mInterrupter->end(); |
212 | } | ||
213 | 110 | MeasureArea(const MeasureArea& other) : mParent(other.mParent), mStencil(*mParent->mGrid) {} | |
214 | void operator()(const LeafRange& range) const; | ||
215 | LevelSetMeasure* mParent; | ||
216 | mutable math::GradStencil<GridT, false> mStencil; | ||
217 | };// MeasureArea | ||
218 | |||
219 | ✗ | struct MeasureCurvatures | |
220 | { | ||
221 | 32 | MeasureCurvatures(LevelSetMeasure* parent) : mParent(parent), mStencil(*mParent->mGrid) | |
222 | { | ||
223 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
32 | if (parent->mInterrupter) parent->mInterrupter->start("Measuring curvatures of level set"); |
224 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
32 | if (parent->mGrainSize>0) { |
225 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | tbb::parallel_for(parent->mLeafs->leafRange(parent->mGrainSize), *this); |
226 | } else { | ||
227 | ✗ | (*this)(parent->mLeafs->leafRange()); | |
228 | } | ||
229 |
1/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
48 | tbb::parallel_invoke([&](){parent->mTotMeanCurvature = parent->reduce(0);}, |
230 | 16 | [&](){parent->mTotGausCurvature = parent->reduce(1);}); | |
231 | 32 | parent->mUpdateCurvature = false; | |
232 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
32 | if (parent->mInterrupter) parent->mInterrupter->end(); |
233 | } | ||
234 | 441 | MeasureCurvatures(const MeasureCurvatures& other) : mParent(other.mParent), mStencil(*mParent->mGrid) {} | |
235 | void operator()(const LeafRange& range) const; | ||
236 | LevelSetMeasure* mParent; | ||
237 | mutable math::CurvatureStencil<GridT, false> mStencil; | ||
238 | };// MeasureCurvatures | ||
239 | |||
240 | 80 | double reduce(int offset) | |
241 | { | ||
242 | 80 | double *first = mBuffer.get() + offset*mLeafs->leafCount(), *last = first + mLeafs->leafCount(); | |
243 | tbb::parallel_sort(first, last);// mitigates catastrophic cancellation | ||
244 | Real sum = 0.0; | ||
245 |
2/2✓ Branch 0 taken 256018 times.
✓ Branch 1 taken 40 times.
|
512116 | while(first != last) sum += *first++; |
246 | 80 | return sum; | |
247 | } | ||
248 | |||
249 | }; // end of LevelSetMeasure class | ||
250 | |||
251 | |||
252 | template<typename GridT, typename InterruptT> | ||
253 | inline | ||
254 | 38 | LevelSetMeasure<GridT, InterruptT>::LevelSetMeasure(const GridType& grid, InterruptT* interrupt) | |
255 | : mInterrupter(interrupt) | ||
256 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 3 times.
|
44 | , mGrainSize(1) |
257 | { | ||
258 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 3 times.
|
38 | this->init(grid); |
259 | } | ||
260 | |||
261 | template<typename GridT, typename InterruptT> | ||
262 | inline void | ||
263 | 42 | LevelSetMeasure<GridT, InterruptT>::init(const GridType& grid) | |
264 | { | ||
265 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
|
42 | if (!grid.hasUniformVoxels()) { |
266 | ✗ | OPENVDB_THROW(RuntimeError, | |
267 | "The transform must have uniform scale for the LevelSetMeasure to function"); | ||
268 | } | ||
269 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
|
42 | if (grid.getGridClass() != GRID_LEVEL_SET) { |
270 | ✗ | OPENVDB_THROW(RuntimeError, | |
271 | "LevelSetMeasure only supports level sets;" | ||
272 | " try setting the grid class to \"level set\""); | ||
273 | } | ||
274 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 18 times.
|
42 | if (grid.empty()) { |
275 |
2/6✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
24 | OPENVDB_THROW(RuntimeError, |
276 | "LevelSetMeasure does not support empty grids;"); | ||
277 | } | ||
278 | 36 | mGrid = &grid; | |
279 | 36 | mDx = grid.voxelSize()[0]; | |
280 | 36 | mLeafs = std::make_unique<ManagerType>(mGrid->tree()); | |
281 | 36 | mBuffer = std::make_unique<double[]>(2*mLeafs->leafCount()); | |
282 | 36 | mUpdateArea = mUpdateCurvature = true; | |
283 | } | ||
284 | |||
285 | template<typename GridT, typename InterruptT> | ||
286 | inline Real | ||
287 | 36 | LevelSetMeasure<GridT, InterruptT>::area(bool useWorldUnits) | |
288 | { | ||
289 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 15 times.
|
36 | if (mUpdateArea) {MeasureArea m(this);}; |
290 | 36 | double area = mArea; | |
291 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
|
36 | if (useWorldUnits) area *= math::Pow2(mDx); |
292 | 36 | return area; | |
293 | } | ||
294 | |||
295 | template<typename GridT, typename InterruptT> | ||
296 | inline Real | ||
297 | 16 | LevelSetMeasure<GridT, InterruptT>::volume(bool useWorldUnits) | |
298 | { | ||
299 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
|
16 | if (mUpdateArea) {MeasureArea m(this);}; |
300 | 16 | double volume = mVolume; | |
301 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
16 | if (useWorldUnits) volume *= math::Pow3(mDx) ; |
302 | 16 | return volume; | |
303 | } | ||
304 | |||
305 | template<typename GridT, typename InterruptT> | ||
306 | inline Real | ||
307 | ✗ | LevelSetMeasure<GridT, InterruptT>::totMeanCurvature(bool useWorldUnits) | |
308 | { | ||
309 | ✗ | if (mUpdateCurvature) {MeasureCurvatures m(this);}; | |
310 | ✗ | return mTotMeanCurvature * (useWorldUnits ? mDx : 1); | |
311 | } | ||
312 | |||
313 | template<typename GridT, typename InterruptT> | ||
314 | inline Real | ||
315 | 28 | LevelSetMeasure<GridT, InterruptT>::totGaussianCurvature(bool) | |
316 | { | ||
317 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
28 | if (mUpdateCurvature) {MeasureCurvatures m(this);}; |
318 | 28 | return mTotGausCurvature; | |
319 | } | ||
320 | |||
321 | template<typename GridT, typename InterruptT> | ||
322 | inline int | ||
323 | 28 | LevelSetMeasure<GridT, InterruptT>::eulerCharacteristic() | |
324 | { | ||
325 | 28 | const Real x = this->totGaussianCurvature(true) / (2.0*math::pi<Real>()); | |
326 | 28 | return int(math::Round( x )); | |
327 | } | ||
328 | |||
329 | ///////////////////////// PRIVATE METHODS ////////////////////// | ||
330 | |||
331 | template<typename GridT, typename InterruptT> | ||
332 | inline bool | ||
333 | 5354 | LevelSetMeasure<GridT, InterruptT>::checkInterrupter() | |
334 | { | ||
335 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2677 times.
|
5354 | if (util::wasInterrupted(mInterrupter)) { |
336 | ✗ | thread::cancelGroupExecution(); | |
337 | ✗ | return false; | |
338 | } | ||
339 | return true; | ||
340 | } | ||
341 | |||
342 | template<typename GridT, typename InterruptT> | ||
343 | inline void | ||
344 | 1070 | LevelSetMeasure<GridT, InterruptT>:: | |
345 | MeasureArea::operator()(const LeafRange& range) const | ||
346 | { | ||
347 | using Vec3T = math::Vec3<ValueType>; | ||
348 | // computations are performed in index space where dV = 1 | ||
349 | 1070 | mParent->checkInterrupter(); | |
350 | 1070 | const Real invDx = 1.0/mParent->mDx; | |
351 | const DiracDelta<Real> DD(1.5);// dirac delta function is 3 voxel units wide | ||
352 | const size_t leafCount = mParent->mLeafs->leafCount(); | ||
353 |
2/2✓ Branch 1 taken 26406 times.
✓ Branch 2 taken 535 times.
|
53882 | for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { |
354 | Real sumA = 0, sumV = 0;//reduce risk of catastrophic cancellation | ||
355 |
2/2✓ Branch 0 taken 4887712 times.
✓ Branch 1 taken 26406 times.
|
9828236 | for (VoxelCIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { |
356 |
2/2✓ Branch 0 taken 2441734 times.
✓ Branch 1 taken 2445978 times.
|
9775424 | const Real dd = DD(invDx * (*voxelIter)); |
357 |
2/2✓ Branch 0 taken 2441734 times.
✓ Branch 1 taken 2445978 times.
|
9775424 | if (dd > 0.0) { |
358 | 4883468 | mStencil.moveTo(voxelIter); | |
359 | const Coord& p = mStencil.getCenterCoord();// in voxel units | ||
360 | 4883468 | const Vec3T g = mStencil.gradient();// in world units | |
361 | 4883468 | sumA += dd*g.length();// \delta(\phi)*|\nabla\phi| | |
362 | 4883468 | sumV += dd*(g[0]*Real(p[0]) + g[1]*Real(p[1]) + g[2]*Real(p[2]));// \delta(\phi)\vec{x}\cdot\nabla\phi | |
363 | } | ||
364 | } | ||
365 | 52812 | double* ptr = mParent->mBuffer.get() + leafIter.pos(); | |
366 | 52812 | *ptr = sumA; | |
367 | 52812 | ptr += leafCount; | |
368 | 52812 | *ptr = sumV; | |
369 | } | ||
370 | } | ||
371 | |||
372 | template<typename GridT, typename InterruptT> | ||
373 | inline void | ||
374 | 4284 | LevelSetMeasure<GridT, InterruptT>:: | |
375 | MeasureCurvatures::operator()(const LeafRange& range) const | ||
376 | { | ||
377 | using Vec3T = math::Vec3<ValueType>; | ||
378 | // computations are performed in index space where dV = 1 | ||
379 | 4284 | mParent->checkInterrupter(); | |
380 | 4284 | const Real dx = mParent->mDx, dx2=dx*dx, invDx = 1.0/dx; | |
381 | const DiracDelta<Real> DD(1.5);// dirac delta function is 3 voxel units wide | ||
382 | ValueType mean, gauss; | ||
383 | const size_t leafCount = mParent->mLeafs->leafCount(); | ||
384 |
2/2✓ Branch 1 taken 101603 times.
✓ Branch 2 taken 2142 times.
|
207490 | for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { |
385 | Real sumM = 0, sumG = 0;//reduce risk of catastrophic cancellation | ||
386 |
2/2✓ Branch 0 taken 19115750 times.
✓ Branch 1 taken 101603 times.
|
38434706 | for (VoxelCIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { |
387 |
2/2✓ Branch 0 taken 9640106 times.
✓ Branch 1 taken 9475644 times.
|
38231500 | const Real dd = DD(invDx * (*voxelIter)); |
388 |
2/2✓ Branch 0 taken 9640106 times.
✓ Branch 1 taken 9475644 times.
|
38231500 | if (dd > 0.0) { |
389 | 19280212 | mStencil.moveTo(voxelIter); | |
390 | 19280212 | const Vec3T g = mStencil.gradient(); | |
391 | 19280212 | const Real dA = dd*g.length();// \delta(\phi)*\delta(\phi) | |
392 | 19280212 | mStencil.curvatures(mean, gauss); | |
393 | 19280212 | sumM += dA*mean*dx;// \delta(\phi)*\delta(\phi)*MeanCurvature | |
394 | 19280212 | sumG += dA*gauss*dx2;// \delta(\phi)*\delta(\phi)*GaussCurvature | |
395 | } | ||
396 | } | ||
397 | 203206 | double* ptr = mParent->mBuffer.get() + leafIter.pos(); | |
398 | 203206 | *ptr = sumM; | |
399 | 203206 | ptr += leafCount; | |
400 | 203206 | *ptr = sumG; | |
401 | } | ||
402 | } | ||
403 | |||
404 | //////////////////////////////////////// | ||
405 | |||
406 | //{ | ||
407 | /// @cond OPENVDB_DOCS_INTERNAL | ||
408 | |||
409 | template<class GridT> | ||
410 | inline | ||
411 | typename std::enable_if<std::is_floating_point<typename GridT::ValueType>::value, Real>::type | ||
412 | 2 | doLevelSetArea(const GridT& grid, bool useWorldUnits) | |
413 | { | ||
414 | 4 | LevelSetMeasure<GridT> m(grid); | |
415 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
4 | return m.area(useWorldUnits); |
416 | } | ||
417 | |||
418 | template<class GridT> | ||
419 | inline | ||
420 | typename std::enable_if<!std::is_floating_point<typename GridT::ValueType>::value, Real>::type | ||
421 | doLevelSetArea(const GridT&, bool) | ||
422 | { | ||
423 | OPENVDB_THROW(TypeError, | ||
424 | "level set area is supported only for scalar, floating-point grids"); | ||
425 | } | ||
426 | |||
427 | /// @endcond | ||
428 | //} | ||
429 | |||
430 | template<class GridT> | ||
431 | Real | ||
432 | 2 | levelSetArea(const GridT& grid, bool useWorldUnits) | |
433 | { | ||
434 | 2 | return doLevelSetArea<GridT>(grid, useWorldUnits); | |
435 | } | ||
436 | |||
437 | //////////////////////////////////////// | ||
438 | |||
439 | //{ | ||
440 | /// @cond OPENVDB_DOCS_INTERNAL | ||
441 | |||
442 | template<class GridT> | ||
443 | inline | ||
444 | typename std::enable_if<std::is_floating_point<typename GridT::ValueType>::value, Real>::type | ||
445 | 2 | doLevelSetVolume(const GridT& grid, bool useWorldUnits) | |
446 | { | ||
447 | 4 | LevelSetMeasure<GridT> m(grid); | |
448 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
4 | return m.volume(useWorldUnits); |
449 | } | ||
450 | |||
451 | template<class GridT> | ||
452 | inline | ||
453 | typename std::enable_if<!std::is_floating_point<typename GridT::ValueType>::value, Real>::type | ||
454 | doLevelSetVolume(const GridT&, bool) | ||
455 | { | ||
456 | OPENVDB_THROW(TypeError, | ||
457 | "level set volume is supported only for scalar, floating-point grids"); | ||
458 | } | ||
459 | |||
460 | /// @endcond | ||
461 | //} | ||
462 | |||
463 | template<class GridT> | ||
464 | Real | ||
465 | 2 | levelSetVolume(const GridT& grid, bool useWorldUnits) | |
466 | { | ||
467 | 2 | return doLevelSetVolume<GridT>(grid, useWorldUnits); | |
468 | } | ||
469 | |||
470 | //////////////////////////////////////// | ||
471 | |||
472 | //{ | ||
473 | /// @cond OPENVDB_DOCS_INTERNAL | ||
474 | |||
475 | template<class GridT> | ||
476 | inline | ||
477 | typename std::enable_if<std::is_floating_point<typename GridT::ValueType>::value, int>::type | ||
478 | 18 | doLevelSetEulerCharacteristic(const GridT& grid) | |
479 | { | ||
480 | 36 | LevelSetMeasure<GridT> m(grid); | |
481 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
36 | return m.eulerCharacteristic(); |
482 | } | ||
483 | |||
484 | template<class GridT> | ||
485 | inline | ||
486 | typename std::enable_if<!std::is_floating_point<typename GridT::ValueType>::value, int>::type | ||
487 | doLevelSetEulerCharacteristic(const GridT&) | ||
488 | { | ||
489 | OPENVDB_THROW(TypeError, | ||
490 | "level set euler characteristic is supported only for scalar, floating-point grids"); | ||
491 | } | ||
492 | |||
493 | /// @endcond | ||
494 | //} | ||
495 | |||
496 | |||
497 | template<class GridT> | ||
498 | int | ||
499 | 18 | levelSetEulerCharacteristic(const GridT& grid) | |
500 | { | ||
501 | 18 | return doLevelSetEulerCharacteristic(grid); | |
502 | } | ||
503 | |||
504 | //////////////////////////////////////// | ||
505 | |||
506 | //{ | ||
507 | /// @cond OPENVDB_DOCS_INTERNAL | ||
508 | |||
509 | template<class GridT> | ||
510 | inline | ||
511 | typename std::enable_if<std::is_floating_point<typename GridT::ValueType>::value, int>::type | ||
512 | doLevelSetEuler(const GridT& grid) | ||
513 | { | ||
514 | LevelSetMeasure<GridT> m(grid); | ||
515 | return m.eulerCharacteristics(); | ||
516 | |||
517 | } | ||
518 | |||
519 | template<class GridT> | ||
520 | inline | ||
521 | typename std::enable_if<std::is_floating_point<typename GridT::ValueType>::value, int>::type | ||
522 | 16 | doLevelSetGenus(const GridT& grid) | |
523 | { | ||
524 | 26 | LevelSetMeasure<GridT> m(grid); | |
525 | 10 | return m.genus(); | |
526 | } | ||
527 | |||
528 | template<class GridT> | ||
529 | inline | ||
530 | typename std::enable_if<!std::is_floating_point<typename GridT::ValueType>::value, int>::type | ||
531 | doLevelSetGenus(const GridT&) | ||
532 | { | ||
533 | OPENVDB_THROW(TypeError, | ||
534 | "level set genus is supported only for scalar, floating-point grids"); | ||
535 | } | ||
536 | |||
537 | /// @endcond | ||
538 | //} | ||
539 | |||
540 | template<class GridT> | ||
541 | int | ||
542 | 8 | levelSetGenus(const GridT& grid) | |
543 | { | ||
544 | 8 | return doLevelSetGenus(grid); | |
545 | } | ||
546 | |||
547 | |||
548 | //////////////////////////////////////// | ||
549 | |||
550 | |||
551 | // Explicit Template Instantiation | ||
552 | |||
553 | #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
554 | |||
555 | #ifdef OPENVDB_INSTANTIATE_LEVELSETMEASURE | ||
556 | #include <openvdb/util/ExplicitInstantiation.h> | ||
557 | #endif | ||
558 | |||
559 | #define _FUNCTION(TreeT) \ | ||
560 | Real levelSetArea(const Grid<TreeT>&, bool) | ||
561 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
562 | #undef _FUNCTION | ||
563 | |||
564 | #define _FUNCTION(TreeT) \ | ||
565 | Real levelSetVolume(const Grid<TreeT>&, bool) | ||
566 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
567 | #undef _FUNCTION | ||
568 | |||
569 | #define _FUNCTION(TreeT) \ | ||
570 | int levelSetEulerCharacteristic(const Grid<TreeT>&) | ||
571 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
572 | #undef _FUNCTION | ||
573 | |||
574 | #define _FUNCTION(TreeT) \ | ||
575 | int levelSetGenus(const Grid<TreeT>&) | ||
576 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
577 | #undef _FUNCTION | ||
578 | |||
579 | OPENVDB_INSTANTIATE_CLASS LevelSetMeasure<FloatGrid, util::NullInterrupter>; | ||
580 | OPENVDB_INSTANTIATE_CLASS LevelSetMeasure<DoubleGrid, util::NullInterrupter>; | ||
581 | |||
582 | #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
583 | |||
584 | |||
585 | } // namespace tools | ||
586 | } // namespace OPENVDB_VERSION_NAME | ||
587 | } // namespace openvdb | ||
588 | |||
589 | #endif // OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED | ||
590 |