GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/tools/LevelSetRebuild.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 46 65 70.8%
Functions: 6 11 54.5%
Branches: 20 48 41.7%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 #ifndef OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED
5 #define OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED
6
7 #include <openvdb/Grid.h>
8 #include <openvdb/Exceptions.h>
9 #include <openvdb/math/Math.h>
10 #include <openvdb/math/Transform.h>
11 #include <openvdb/tools/VolumeToMesh.h>
12 #include <openvdb/tools/MeshToVolume.h>
13 #include <openvdb/util/NullInterrupter.h>
14 #include <openvdb/util/Util.h>
15 #include <openvdb/openvdb.h>
16 #include <tbb/blocked_range.h>
17 #include <tbb/parallel_for.h>
18 #include <type_traits>
19
20
21 namespace openvdb {
22 OPENVDB_USE_VERSION_NAMESPACE
23 namespace OPENVDB_VERSION_NAME {
24 namespace tools {
25
26
27 /// @brief Return a new grid of type @c GridType that contains a narrow-band level set
28 /// representation of an isosurface of a given grid.
29 ///
30 /// @param grid a scalar, floating-point grid with one or more disjoint,
31 /// closed isosurfaces at the given @a isovalue
32 /// @param isovalue the isovalue that defines the implicit surface (defaults to zero,
33 /// which is typical if the input grid is already a level set or a SDF).
34 /// @param halfWidth half the width of the narrow band, in voxel units
35 /// (defaults to 3 voxels, which is required for some level set operations)
36 /// @param xform optional transform for the output grid
37 /// (if not provided, the transform of the input @a grid will be matched)
38 ///
39 /// @throw TypeError if @a grid is not scalar or not floating-point
40 ///
41 /// @note If the input grid contains overlapping isosurfaces, interior edges will be lost.
42 template<class GridType>
43 typename GridType::Ptr
44 levelSetRebuild(const GridType& grid, float isovalue = 0,
45 float halfWidth = float(LEVEL_SET_HALF_WIDTH), const math::Transform* xform = nullptr);
46
47
48 /// @brief Return a new grid of type @c GridType that contains a narrow-band level set
49 /// representation of an isosurface of a given grid.
50 ///
51 /// @param grid a scalar, floating-point grid with one or more disjoint,
52 /// closed isosurfaces at the given @a isovalue
53 /// @param isovalue the isovalue that defines the implicit surface
54 /// @param exBandWidth the exterior narrow-band width in voxel units
55 /// @param inBandWidth the interior narrow-band width in voxel units
56 /// @param xform optional transform for the output grid
57 /// (if not provided, the transform of the input @a grid will be matched)
58 ///
59 /// @throw TypeError if @a grid is not scalar or not floating-point
60 ///
61 /// @note If the input grid contains overlapping isosurfaces, interior edges will be lost.
62 template<class GridType>
63 typename GridType::Ptr
64 levelSetRebuild(const GridType& grid, float isovalue, float exBandWidth, float inBandWidth,
65 const math::Transform* xform = nullptr);
66
67
68 /// @brief Return a new grid of type @c GridType that contains a narrow-band level set
69 /// representation of an isosurface of a given grid.
70 ///
71 /// @param grid a scalar, floating-point grid with one or more disjoint,
72 /// closed isosurfaces at the given @a isovalue
73 /// @param isovalue the isovalue that defines the implicit surface
74 /// @param exBandWidth the exterior narrow-band width in voxel units
75 /// @param inBandWidth the interior narrow-band width in voxel units
76 /// @param xform optional transform for the output grid
77 /// (if not provided, the transform of the input @a grid will be matched)
78 /// @param interrupter optional interrupter object
79 ///
80 /// @throw TypeError if @a grid is not scalar or not floating-point
81 ///
82 /// @note If the input grid contains overlapping isosurfaces, interior edges will be lost.
83 template<class GridType, typename InterruptT>
84 typename GridType::Ptr
85 levelSetRebuild(const GridType& grid, float isovalue, float exBandWidth, float inBandWidth,
86 const math::Transform* xform = nullptr, InterruptT* interrupter = nullptr);
87
88
89 ////////////////////////////////////////
90
91 /// @cond OPENVDB_DOCS_INTERNAL
92
93 // Internal utility objects and implementation details
94
95 namespace internal {
96
97 class PointListTransform
98 {
99 public:
100 PointListTransform(const PointList& pointsIn, std::vector<Vec3s>& pointsOut,
101 const math::Transform& xform)
102 5 : mPointsIn(pointsIn)
103 , mPointsOut(&pointsOut)
104 5 , mXform(xform)
105 {
106 }
107
108 5 void runParallel()
109 {
110 5 tbb::parallel_for(tbb::blocked_range<size_t>(0, mPointsOut->size()), *this);
111 5 }
112
113 void runSerial()
114 {
115 (*this)(tbb::blocked_range<size_t>(0, mPointsOut->size()));
116 }
117
118 512 inline void operator()(const tbb::blocked_range<size_t>& range) const
119 {
120
2/2
✓ Branch 0 taken 1664 times.
✓ Branch 1 taken 512 times.
2176 for (size_t n = range.begin(); n < range.end(); ++n) {
121 1664 (*mPointsOut)[n] = Vec3s(mXform.worldToIndex(mPointsIn[n]));
122 }
123 512 }
124
125 private:
126 const PointList& mPointsIn;
127 std::vector<Vec3s> * const mPointsOut;
128 const math::Transform& mXform;
129 };
130
131
132 class PrimCpy
133 {
134 public:
135 PrimCpy(const PolygonPoolList& primsIn, const std::vector<size_t>& indexList,
136 std::vector<Vec4I>& primsOut)
137 5 : mPrimsIn(primsIn)
138 , mIndexList(indexList)
139 5 , mPrimsOut(&primsOut)
140 {
141 }
142
143 5 void runParallel()
144 {
145 5 tbb::parallel_for(tbb::blocked_range<size_t>(0, mIndexList.size()), *this);
146 5 }
147
148 void runSerial()
149 {
150 (*this)(tbb::blocked_range<size_t>(0, mIndexList.size()));
151 }
152
153 32 inline void operator()(const tbb::blocked_range<size_t>& range) const
154 {
155 openvdb::Vec4I quad;
156 32 quad[3] = openvdb::util::INVALID_IDX;
157 32 std::vector<Vec4I>& primsOut = *mPrimsOut;
158
159
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
64 for (size_t n = range.begin(); n < range.end(); ++n) {
160 32 size_t index = mIndexList[n];
161 32 PolygonPool& polygons = mPrimsIn[n];
162
163 // Copy quads
164
2/2
✓ Branch 0 taken 1656 times.
✓ Branch 1 taken 32 times.
1688 for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
165 1656 primsOut[index++] = polygons.quad(i);
166 }
167 32 polygons.clearQuads();
168
169 // Copy triangles (adaptive mesh)
170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) {
171 const openvdb::Vec3I& triangle = polygons.triangle(i);
172 quad[0] = triangle[0];
173 quad[1] = triangle[1];
174 quad[2] = triangle[2];
175 primsOut[index++] = quad;
176 }
177
178 32 polygons.clearTriangles();
179 }
180 32 }
181
182 private:
183 const PolygonPoolList& mPrimsIn;
184 const std::vector<size_t>& mIndexList;
185 std::vector<Vec4I> * const mPrimsOut;
186 };
187
188 } // namespace internal
189
190 /// @endcond
191
192 ////////////////////////////////////////
193
194
195 //{
196 /// @cond OPENVDB_DOCS_INTERNAL
197
198 /// The normal entry points for level set rebuild are the levelSetRebuild() functions.
199 /// doLevelSetRebuild() is mainly for internal use, but when the isovalue and half band
200 /// widths are given in ValueType units (for example, if they are queried from
201 /// a grid), it might be more convenient to call this function directly.
202 ///
203 /// @internal This overload is enabled only for grids with a scalar, floating-point ValueType.
204 template<class GridType, typename InterruptT>
205 inline typename std::enable_if<
206 std::is_floating_point<typename GridType::ValueType>::value, typename GridType::Ptr>::type
207 10 doLevelSetRebuild(const GridType& grid, typename GridType::ValueType iso,
208 typename GridType::ValueType exWidth, typename GridType::ValueType inWidth,
209 const math::Transform* xform, InterruptT* interrupter)
210 {
211 const float
212 isovalue = float(iso),
213 exBandWidth = float(exWidth),
214 inBandWidth = float(inWidth);
215
216 20 tools::VolumeToMesh mesher(isovalue);
217
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 mesher(grid);
218
219
2/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
10 math::Transform::Ptr transform = (xform != nullptr) ? xform->copy() : grid.transform().copy();
220
221
2/6
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
10 std::vector<Vec3s> points(mesher.pointListSize());
222
223 { // Copy and transform (required for MeshToVolume) points to grid space.
224 internal::PointListTransform ptnXForm(mesher.pointList(), points, *transform);
225
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 ptnXForm.runParallel();
226 mesher.pointList().reset(nullptr);
227 }
228
229 std::vector<Vec4I> primitives;
230
231 { // Copy primitives.
232 PolygonPoolList& polygonPoolList = mesher.polygonPoolList();
233
234 size_t numPrimitives = 0;
235
1/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
10 std::vector<size_t> indexlist(mesher.polygonPoolListSize());
236
237
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 5 times.
74 for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) {
238 const openvdb::tools::PolygonPool& polygons = polygonPoolList[n];
239 64 indexlist[n] = numPrimitives;
240 64 numPrimitives += polygons.numQuads();
241 64 numPrimitives += polygons.numTriangles();
242 }
243
244
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 primitives.resize(numPrimitives);
245 internal::PrimCpy primCpy(polygonPoolList, indexlist, primitives);
246
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 primCpy.runParallel();
247 }
248
249 QuadAndTriangleDataAdapter<Vec3s, Vec4I> mesh(points, primitives);
250
251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
10 if (interrupter) {
252 return meshToVolume<GridType>(*interrupter, mesh, *transform, exBandWidth, inBandWidth,
253 DISABLE_RENORMALIZATION, nullptr);
254 }
255
256 return meshToVolume<GridType>(mesh, *transform, exBandWidth, inBandWidth,
257
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 DISABLE_RENORMALIZATION, nullptr);
258 }
259
260
261 /// @internal This overload is enabled only for grids that do not have a scalar,
262 /// floating-point ValueType.
263 template<class GridType, typename InterruptT>
264 inline typename std::enable_if<
265 !std::is_floating_point<typename GridType::ValueType>::value, typename GridType::Ptr>::type
266 doLevelSetRebuild(const GridType&, typename GridType::ValueType /*isovalue*/,
267 typename GridType::ValueType /*exWidth*/, typename GridType::ValueType /*inWidth*/,
268 const math::Transform*, InterruptT*)
269 {
270 OPENVDB_THROW(TypeError,
271 "level set rebuild is supported only for scalar, floating-point grids");
272 }
273
274 /// @endcond
275 //}
276
277
278 ////////////////////////////////////////
279
280
281 template<class GridType, typename InterruptT>
282 typename GridType::Ptr
283 10 levelSetRebuild(const GridType& grid, float iso, float exWidth, float inWidth,
284 const math::Transform* xform, InterruptT* interrupter)
285 {
286 using ValueT = typename GridType::ValueType;
287 ValueT
288 10 isovalue(zeroVal<ValueT>() + ValueT(iso)),
289 10 exBandWidth(zeroVal<ValueT>() + ValueT(exWidth)),
290 10 inBandWidth(zeroVal<ValueT>() + ValueT(inWidth));
291
292 10 return doLevelSetRebuild(grid, isovalue, exBandWidth, inBandWidth, xform, interrupter);
293 }
294
295
296 template<class GridType>
297 typename GridType::Ptr
298 levelSetRebuild(const GridType& grid, float iso, float exWidth, float inWidth,
299 const math::Transform* xform)
300 {
301 using ValueT = typename GridType::ValueType;
302 ValueT
303 isovalue(zeroVal<ValueT>() + ValueT(iso)),
304 exBandWidth(zeroVal<ValueT>() + ValueT(exWidth)),
305 inBandWidth(zeroVal<ValueT>() + ValueT(inWidth));
306
307 return doLevelSetRebuild<GridType, util::NullInterrupter>(
308 grid, isovalue, exBandWidth, inBandWidth, xform, nullptr);
309 }
310
311
312 template<class GridType>
313 typename GridType::Ptr
314 levelSetRebuild(const GridType& grid, float iso, float halfVal, const math::Transform* xform)
315 {
316 using ValueT = typename GridType::ValueType;
317 ValueT
318 isovalue(zeroVal<ValueT>() + ValueT(iso)),
319 halfWidth(zeroVal<ValueT>() + ValueT(halfVal));
320
321 return doLevelSetRebuild<GridType, util::NullInterrupter>(
322 grid, isovalue, halfWidth, halfWidth, xform, nullptr);
323 }
324
325
326 ////////////////////////////////////////
327
328
329 // Explicit Template Instantiation
330
331 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
332
333 #ifdef OPENVDB_INSTANTIATE_LEVELSETREBUILD
334 #include <openvdb/util/ExplicitInstantiation.h>
335 #endif
336
337 #define _FUNCTION(TreeT) \
338 Grid<TreeT>::Ptr levelSetRebuild(const Grid<TreeT>&, float, float, const math::Transform*)
339 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
340 #undef _FUNCTION
341
342 #define _FUNCTION(TreeT) \
343 Grid<TreeT>::Ptr levelSetRebuild(const Grid<TreeT>&, float, float, float, const math::Transform*)
344 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
345 #undef _FUNCTION
346
347 #define _FUNCTION(TreeT) \
348 Grid<TreeT>::Ptr levelSetRebuild(const Grid<TreeT>&, float, float, float, const math::Transform*, \
349 util::NullInterrupter*)
350 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
351 #undef _FUNCTION
352
353 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
354
355
356 } // namespace tools
357 } // namespace OPENVDB_VERSION_NAME
358 } // namespace openvdb
359
360 #endif // OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED
361