GCC Code Coverage Report


Directory: ./
File: openvdb/openvdb/tools/LevelSetMorph.h
Date: 2022-07-25 17:40:05
Exec Total Coverage
Lines: 102 194 52.6%
Functions: 17 732 2.3%
Branches: 62 613 10.1%

Line Branch Exec Source
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @author Ken Museth
5 ///
6 /// @file tools/LevelSetMorph.h
7 ///
8 /// @brief Shape morphology of level sets. Morphing from a source
9 /// narrow-band level sets to a target narrow-band level set.
10
11 #ifndef OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED
12 #define OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED
13
14 #include "LevelSetTracker.h"
15 #include "Interpolation.h" // for BoxSampler, etc.
16 #include <openvdb/math/FiniteDifference.h>
17 #include <functional>
18 #include <limits>
19
20
21 namespace openvdb {
22 OPENVDB_USE_VERSION_NAMESPACE
23 namespace OPENVDB_VERSION_NAME {
24 namespace tools {
25
26 /// @brief Shape morphology of level sets. Morphing from a source
27 /// narrow-band level sets to a target narrow-band level set.
28 ///
29 /// @details
30 /// The @c InterruptType template argument below refers to any class
31 /// with the following interface:
32 /// @code
33 /// class Interrupter {
34 /// ...
35 /// public:
36 /// void start(const char* name = nullptr) // called when computations begin
37 /// void end() // called when computations end
38 /// bool wasInterrupted(int percent=-1) // return true to break computation
39 /// };
40 /// @endcode
41 ///
42 /// @note If no template argument is provided for this InterruptType,
43 /// the util::NullInterrupter is used, which implies that all interrupter
44 /// calls are no-ops (i.e., they incur no computational overhead).
45 template<typename GridT,
46 typename InterruptT = util::NullInterrupter>
47 class LevelSetMorphing
48 {
49 public:
50 using GridType = GridT;
51 using TreeType = typename GridT::TreeType;
52 using TrackerT = LevelSetTracker<GridT, InterruptT>;
53 using LeafRange = typename TrackerT::LeafRange;
54 using LeafType = typename TrackerT::LeafType;
55 using BufferType = typename TrackerT::BufferType;
56 using ValueType = typename TrackerT::ValueType;
57
58 /// Main constructor
59 1 LevelSetMorphing(GridT& sourceGrid, const GridT& targetGrid, InterruptT* interrupt = nullptr)
60 : mTracker(sourceGrid, interrupt)
61 , mTarget(&targetGrid)
62 , mMask(nullptr)
63 , mSpatialScheme(math::HJWENO5_BIAS)
64 , mTemporalScheme(math::TVD_RK2)
65 , mMinMask(0)
66 , mDeltaMask(1)
67
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 , mInvertMask(false)
68 {
69 }
70
71
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 virtual ~LevelSetMorphing() {}
72
73 /// Redefine the target level set
74 void setTarget(const GridT& targetGrid) { mTarget = &targetGrid; }
75
76 /// Define the alpha mask
77 void setAlphaMask(const GridT& maskGrid) { mMask = &maskGrid; }
78
79 /// Return the spatial finite-difference scheme
80 math::BiasedGradientScheme getSpatialScheme() const { return mSpatialScheme; }
81 /// Set the spatial finite-difference scheme
82 void setSpatialScheme(math::BiasedGradientScheme scheme) { mSpatialScheme = scheme; }
83
84 /// Return the temporal integration scheme
85 math::TemporalIntegrationScheme getTemporalScheme() const { return mTemporalScheme; }
86 /// Set the temporal integration scheme
87
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 void setTemporalScheme(math::TemporalIntegrationScheme scheme) { mTemporalScheme = scheme; }
88
89 /// Return the spatial finite-difference scheme
90 math::BiasedGradientScheme getTrackerSpatialScheme() const
91 {
92 return mTracker.getSpatialScheme();
93 }
94 /// Set the spatial finite-difference scheme
95 void setTrackerSpatialScheme(math::BiasedGradientScheme scheme)
96 {
97 mTracker.setSpatialScheme(scheme);
98 }
99 /// Return the temporal integration scheme
100 math::TemporalIntegrationScheme getTrackerTemporalScheme() const
101 {
102 return mTracker.getTemporalScheme();
103 }
104 /// Set the temporal integration scheme
105 void setTrackerTemporalScheme(math::TemporalIntegrationScheme scheme)
106 {
107 mTracker.setTemporalScheme(scheme);
108 }
109 /// Return the number of normalizations performed per track or normalize call.
110 int getNormCount() const { return mTracker.getNormCount(); }
111 /// Set the number of normalizations performed per track or normalize call.
112 void setNormCount(int n) { mTracker.setNormCount(n); }
113
114 /// Return the grain size used for multithreading
115 int getGrainSize() const { return mTracker.getGrainSize(); }
116 /// @brief Set the grain size used for multithreading.
117 /// @note A grain size of 0 or less disables multithreading!
118 void setGrainSize(int grainsize) { mTracker.setGrainSize(grainsize); }
119
120 /// @brief Return the minimum value of the mask to be used for the
121 /// derivation of a smooth alpha value.
122 ValueType minMask() const { return mMinMask; }
123
124 /// @brief Return the maximum value of the mask to be used for the
125 /// derivation of a smooth alpha value.
126 ValueType maxMask() const { return mDeltaMask + mMinMask; }
127
128 /// @brief Define the range for the (optional) scalar mask.
129 /// @param min Minimum value of the range.
130 /// @param max Maximum value of the range.
131 /// @details Mask values outside the range maps to alpha values of
132 /// respectfully zero and one, and values inside the range maps
133 /// smoothly to 0->1 (unless of course the mask is inverted).
134 /// @throw ValueError if @a min is not smaller than @a max.
135 void setMaskRange(ValueType min, ValueType max)
136 {
137 if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
138 mMinMask = min;
139 mDeltaMask = max-min;
140 }
141
142 /// @brief Return true if the mask is inverted, i.e. min->max in the
143 /// original mask maps to 1->0 in the inverted alpha mask.
144 bool isMaskInverted() const { return mInvertMask; }
145 /// @brief Invert the optional mask, i.e. min->max in the original
146 /// mask maps to 1->0 in the inverted alpha mask.
147 void invertMask(bool invert=true) { mInvertMask = invert; }
148
149 /// @brief Advect the level set from its current time, @a time0, to its
150 /// final time, @a time1. If @a time0 > @a time1, perform backward advection.
151 ///
152 /// @return the number of CFL iterations used to advect from @a time0 to @a time1
153 size_t advect(ValueType time0, ValueType time1);
154
155 private:
156
157 // disallow copy construction and copy by assignment!
158 LevelSetMorphing(const LevelSetMorphing&);// not implemented
159 LevelSetMorphing& operator=(const LevelSetMorphing&);// not implemented
160
161 template<math::BiasedGradientScheme SpatialScheme>
162 size_t advect1(ValueType time0, ValueType time1);
163
164 template<math::BiasedGradientScheme SpatialScheme,
165 math::TemporalIntegrationScheme TemporalScheme>
166 size_t advect2(ValueType time0, ValueType time1);
167
168 template<math::BiasedGradientScheme SpatialScheme,
169 math::TemporalIntegrationScheme TemporalScheme,
170 typename MapType>
171 size_t advect3(ValueType time0, ValueType time1);
172
173 TrackerT mTracker;
174 const GridT *mTarget, *mMask;
175 math::BiasedGradientScheme mSpatialScheme;
176 math::TemporalIntegrationScheme mTemporalScheme;
177 ValueType mMinMask, mDeltaMask;
178 bool mInvertMask;
179
180 // This templated private class implements all the level set magic.
181 template<typename MapT, math::BiasedGradientScheme SpatialScheme,
182 math::TemporalIntegrationScheme TemporalScheme>
183 struct Morph
184 {
185 /// Main constructor
186 Morph(LevelSetMorphing<GridT, InterruptT>& parent);
187 /// Shallow copy constructor called by tbb::parallel_for() threads
188 Morph(const Morph& other);
189 /// Shallow copy constructor called by tbb::parallel_reduce() threads
190 Morph(Morph& other, tbb::split);
191 /// destructor
192
2/288
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✓ Branch 60 taken 32 times.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 99 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 102 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 105 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 111 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
✗ Branch 116 not taken.
✗ Branch 117 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
✗ Branch 120 not taken.
✗ Branch 121 not taken.
✗ Branch 122 not taken.
✗ Branch 123 not taken.
✗ Branch 124 not taken.
✗ Branch 125 not taken.
✗ Branch 126 not taken.
✗ Branch 127 not taken.
✗ Branch 128 not taken.
✗ Branch 129 not taken.
✗ Branch 130 not taken.
✗ Branch 131 not taken.
✗ Branch 132 not taken.
✗ Branch 133 not taken.
✗ Branch 134 not taken.
✗ Branch 135 not taken.
✗ Branch 136 not taken.
✗ Branch 137 not taken.
✗ Branch 138 not taken.
✗ Branch 139 not taken.
✗ Branch 140 not taken.
✗ Branch 141 not taken.
✗ Branch 142 not taken.
✗ Branch 143 not taken.
✗ Branch 144 not taken.
✗ Branch 145 not taken.
✗ Branch 146 not taken.
✗ Branch 147 not taken.
✗ Branch 148 not taken.
✗ Branch 149 not taken.
✗ Branch 150 not taken.
✗ Branch 151 not taken.
✗ Branch 152 not taken.
✗ Branch 153 not taken.
✗ Branch 154 not taken.
✗ Branch 155 not taken.
✗ Branch 156 not taken.
✗ Branch 157 not taken.
✗ Branch 158 not taken.
✗ Branch 159 not taken.
✗ Branch 160 not taken.
✗ Branch 161 not taken.
✗ Branch 162 not taken.
✗ Branch 163 not taken.
✗ Branch 164 not taken.
✗ Branch 165 not taken.
✗ Branch 166 not taken.
✗ Branch 167 not taken.
✗ Branch 168 not taken.
✗ Branch 169 not taken.
✗ Branch 170 not taken.
✗ Branch 171 not taken.
✗ Branch 172 not taken.
✗ Branch 173 not taken.
✗ Branch 174 not taken.
✗ Branch 175 not taken.
✗ Branch 176 not taken.
✗ Branch 177 not taken.
✗ Branch 178 not taken.
✗ Branch 179 not taken.
✗ Branch 180 not taken.
✗ Branch 181 not taken.
✗ Branch 182 not taken.
✗ Branch 183 not taken.
✗ Branch 184 not taken.
✗ Branch 185 not taken.
✗ Branch 186 not taken.
✗ Branch 187 not taken.
✗ Branch 188 not taken.
✗ Branch 189 not taken.
✗ Branch 190 not taken.
✗ Branch 191 not taken.
✗ Branch 192 not taken.
✗ Branch 193 not taken.
✗ Branch 194 not taken.
✗ Branch 195 not taken.
✗ Branch 196 not taken.
✗ Branch 197 not taken.
✗ Branch 198 not taken.
✗ Branch 199 not taken.
✗ Branch 200 not taken.
✗ Branch 201 not taken.
✗ Branch 202 not taken.
✗ Branch 203 not taken.
✗ Branch 204 not taken.
✗ Branch 205 not taken.
✗ Branch 206 not taken.
✗ Branch 207 not taken.
✗ Branch 208 not taken.
✗ Branch 209 not taken.
✗ Branch 210 not taken.
✗ Branch 211 not taken.
✗ Branch 212 not taken.
✗ Branch 213 not taken.
✗ Branch 214 not taken.
✗ Branch 215 not taken.
✗ Branch 216 not taken.
✗ Branch 217 not taken.
✗ Branch 218 not taken.
✗ Branch 219 not taken.
✗ Branch 220 not taken.
✗ Branch 221 not taken.
✗ Branch 222 not taken.
✗ Branch 223 not taken.
✗ Branch 224 not taken.
✗ Branch 225 not taken.
✗ Branch 226 not taken.
✗ Branch 227 not taken.
✗ Branch 228 not taken.
✗ Branch 229 not taken.
✗ Branch 230 not taken.
✗ Branch 231 not taken.
✗ Branch 232 not taken.
✗ Branch 233 not taken.
✗ Branch 234 not taken.
✗ Branch 235 not taken.
✗ Branch 236 not taken.
✗ Branch 237 not taken.
✗ Branch 238 not taken.
✗ Branch 239 not taken.
✗ Branch 240 not taken.
✗ Branch 241 not taken.
✗ Branch 242 not taken.
✗ Branch 243 not taken.
✗ Branch 244 not taken.
✗ Branch 245 not taken.
✗ Branch 246 not taken.
✗ Branch 247 not taken.
✗ Branch 248 not taken.
✗ Branch 249 not taken.
✗ Branch 250 not taken.
✗ Branch 251 not taken.
✗ Branch 252 not taken.
✗ Branch 253 not taken.
✗ Branch 254 not taken.
✗ Branch 255 not taken.
✗ Branch 256 not taken.
✗ Branch 257 not taken.
✗ Branch 258 not taken.
✗ Branch 259 not taken.
✗ Branch 260 not taken.
✗ Branch 261 not taken.
✗ Branch 262 not taken.
✗ Branch 263 not taken.
✗ Branch 264 not taken.
✗ Branch 265 not taken.
✗ Branch 266 not taken.
✗ Branch 267 not taken.
✗ Branch 268 not taken.
✗ Branch 269 not taken.
✗ Branch 270 not taken.
✗ Branch 271 not taken.
✗ Branch 272 not taken.
✗ Branch 273 not taken.
✗ Branch 274 not taken.
✗ Branch 275 not taken.
✗ Branch 276 not taken.
✗ Branch 277 not taken.
✗ Branch 278 not taken.
✗ Branch 279 not taken.
✗ Branch 280 not taken.
✗ Branch 281 not taken.
✗ Branch 282 not taken.
✗ Branch 283 not taken.
✗ Branch 284 not taken.
✗ Branch 285 not taken.
✗ Branch 286 not taken.
✗ Branch 287 not taken.
160 virtual ~Morph() {}
193 /// Advect the level set from its current time, time0, to its final time, time1.
194 /// @return number of CFL iterations
195 size_t advect(ValueType time0, ValueType time1);
196 /// Used internally by tbb::parallel_for()
197
1/2
✓ Branch 0 taken 4866 times.
✗ Branch 1 not taken.
9732 void operator()(const LeafRange& r) const
198 {
199
1/2
✓ Branch 0 taken 4866 times.
✗ Branch 1 not taken.
9732 if (mTask) mTask(const_cast<Morph*>(this), r);
200 else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly");
201 }
202 /// Used internally by tbb::parallel_reduce()
203
1/2
✓ Branch 0 taken 1622 times.
✗ Branch 1 not taken.
3244 void operator()(const LeafRange& r)
204 {
205
1/2
✓ Branch 0 taken 1622 times.
✗ Branch 1 not taken.
3244 if (mTask) mTask(this, r);
206 else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly");
207 }
208 /// This is only called by tbb::parallel_reduce() threads
209
2/96
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 35 times.
✓ Branch 7 taken 29 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
99 void join(const Morph& other) { mMaxAbsS = math::Max(mMaxAbsS, other.mMaxAbsS); }
210
211 /// Enum to define the type of multithreading
212 enum ThreadingMode { PARALLEL_FOR, PARALLEL_REDUCE }; // for internal use
213 // method calling tbb
214 void cook(ThreadingMode mode, size_t swapBuffer = 0);
215
216 /// Sample field and return the CFT time step
217 typename GridT::ValueType sampleSpeed(ValueType time0, ValueType time1, Index speedBuffer);
218 void sampleXformedSpeed(const LeafRange& r, Index speedBuffer);
219 void sampleAlignedSpeed(const LeafRange& r, Index speedBuffer);
220
221 // Convex combination of Phi and a forward Euler advection steps:
222 // Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|);
223 template <int Nominator, int Denominator>
224 void euler(const LeafRange&, ValueType, Index, Index, Index);
225 3244 inline void euler01(const LeafRange& r, ValueType t, Index s) {this->euler<0,1>(r,t,0,1,s);}
226 inline void euler12(const LeafRange& r, ValueType t) {this->euler<1,2>(r, t, 1, 1, 2);}
227 3244 inline void euler34(const LeafRange& r, ValueType t) {this->euler<3,4>(r, t, 1, 2, 3);}
228 3244 inline void euler13(const LeafRange& r, ValueType t) {this->euler<1,3>(r, t, 1, 2, 3);}
229
230 using FuncType = typename std::function<void (Morph*, const LeafRange&)>;
231 LevelSetMorphing* mParent;
232 ValueType mMinAbsS, mMaxAbsS;
233 const MapT* mMap;
234 FuncType mTask;
235 }; // end of private Morph struct
236
237 };//end of LevelSetMorphing
238
239 template<typename GridT, typename InterruptT>
240 inline size_t
241 LevelSetMorphing<GridT, InterruptT>::advect(ValueType time0, ValueType time1)
242 {
243 switch (mSpatialScheme) {
244 case math::FIRST_BIAS:
245 return this->advect1<math::FIRST_BIAS >(time0, time1);
246 //case math::SECOND_BIAS:
247 //return this->advect1<math::SECOND_BIAS >(time0, time1);
248 //case math::THIRD_BIAS:
249 //return this->advect1<math::THIRD_BIAS >(time0, time1);
250 //case math::WENO5_BIAS:
251 //return this->advect1<math::WENO5_BIAS >(time0, time1);
252 case math::HJWENO5_BIAS:
253 return this->advect1<math::HJWENO5_BIAS>(time0, time1);
254 case math::SECOND_BIAS:
255 case math::THIRD_BIAS:
256 case math::WENO5_BIAS:
257 case math::UNKNOWN_BIAS:
258 default:
259 OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!");
260 }
261 return 0;
262 }
263
264 template<typename GridT, typename InterruptT>
265 template<math::BiasedGradientScheme SpatialScheme>
266 inline size_t
267 64 LevelSetMorphing<GridT, InterruptT>::advect1(ValueType time0, ValueType time1)
268 {
269
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
64 switch (mTemporalScheme) {
270 case math::TVD_RK1:
271 return this->advect2<SpatialScheme, math::TVD_RK1>(time0, time1);
272 case math::TVD_RK2:
273 return this->advect2<SpatialScheme, math::TVD_RK2>(time0, time1);
274 64 case math::TVD_RK3:
275 64 return this->advect2<SpatialScheme, math::TVD_RK3>(time0, time1);
276 case math::UNKNOWN_TIS:
277 default:
278 OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!");
279 }
280 return 0;
281 }
282
283 template<typename GridT, typename InterruptT>
284 template<math::BiasedGradientScheme SpatialScheme,
285 math::TemporalIntegrationScheme TemporalScheme>
286 inline size_t
287 64 LevelSetMorphing<GridT, InterruptT>::advect2(ValueType time0, ValueType time1)
288 {
289 const math::Transform& trans = mTracker.grid().transform();
290
1/2
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
64 if (trans.mapType() == math::UniformScaleMap::mapType()) {
291 64 return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleMap>(time0, time1);
292 } else if (trans.mapType() == math::UniformScaleTranslateMap::mapType()) {
293 return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleTranslateMap>(
294 time0, time1);
295 } else if (trans.mapType() == math::UnitaryMap::mapType()) {
296 return this->advect3<SpatialScheme, TemporalScheme, math::UnitaryMap >(time0, time1);
297 } else if (trans.mapType() == math::TranslationMap::mapType()) {
298 return this->advect3<SpatialScheme, TemporalScheme, math::TranslationMap>(time0, time1);
299 } else {
300 OPENVDB_THROW(ValueError, "MapType not supported!");
301 }
302 return 0;
303 }
304
305 template<typename GridT, typename InterruptT>
306 template<math::BiasedGradientScheme SpatialScheme,
307 math::TemporalIntegrationScheme TemporalScheme,
308 typename MapT>
309 inline size_t
310 64 LevelSetMorphing<GridT, InterruptT>::advect3(ValueType time0, ValueType time1)
311 {
312 64 Morph<MapT, SpatialScheme, TemporalScheme> tmp(*this);
313
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
128 return tmp.advect(time0, time1);
314 }
315
316
317 ///////////////////////////////////////////////////////////////////////
318
319 template<typename GridT, typename InterruptT>
320 template <typename MapT, math::BiasedGradientScheme SpatialScheme,
321 math::TemporalIntegrationScheme TemporalScheme>
322 inline
323 64 LevelSetMorphing<GridT, InterruptT>::
324 Morph<MapT, SpatialScheme, TemporalScheme>::
325 Morph(LevelSetMorphing<GridT, InterruptT>& parent)
326 : mParent(&parent)
327 , mMinAbsS(ValueType(1e-6))
328 , mMap(parent.mTracker.grid().transform().template constMap<MapT>().get())
329
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
64 , mTask(nullptr)
330 {
331 }
332
333 template<typename GridT, typename InterruptT>
334 template <typename MapT, math::BiasedGradientScheme SpatialScheme,
335 math::TemporalIntegrationScheme TemporalScheme>
336 inline
337 1947 LevelSetMorphing<GridT, InterruptT>::
338 Morph<MapT, SpatialScheme, TemporalScheme>::
339 Morph(const Morph& other)
340 1947 : mParent(other.mParent)
341 1947 , mMinAbsS(other.mMinAbsS)
342 1947 , mMaxAbsS(other.mMaxAbsS)
343 1947 , mMap(other.mMap)
344 1947 , mTask(other.mTask)
345 {
346 }
347
348 template<typename GridT, typename InterruptT>
349 template <typename MapT, math::BiasedGradientScheme SpatialScheme,
350 math::TemporalIntegrationScheme TemporalScheme>
351 inline
352 64 LevelSetMorphing<GridT, InterruptT>::
353 Morph<MapT, SpatialScheme, TemporalScheme>::
354 Morph(Morph& other, tbb::split)
355 64 : mParent(other.mParent)
356 64 , mMinAbsS(other.mMinAbsS)
357 64 , mMaxAbsS(other.mMaxAbsS)
358 64 , mMap(other.mMap)
359 64 , mTask(other.mTask)
360 {
361 }
362
363 template<typename GridT, typename InterruptT>
364 template <typename MapT, math::BiasedGradientScheme SpatialScheme,
365 math::TemporalIntegrationScheme TemporalScheme>
366 inline size_t
367 64 LevelSetMorphing<GridT, InterruptT>::
368 Morph<MapT, SpatialScheme, TemporalScheme>::
369 advect(ValueType time0, ValueType time1)
370 {
371 namespace ph = std::placeholders;
372
373 // Make sure we have enough temporal auxiliary buffers for the time
374 // integration AS WELL AS an extra buffer with the speed function!
375 static const Index auxBuffers = 1 + (TemporalScheme == math::TVD_RK3 ? 2 : 1);
376 size_t countCFL = 0;
377
3/4
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
✓ Branch 3 taken 32 times.
✗ Branch 4 not taken.
128 while (time0 < time1 && mParent->mTracker.checkInterrupter()) {
378 64 mParent->mTracker.leafs().rebuildAuxBuffers(auxBuffers);
379
380 64 const ValueType dt = this->sampleSpeed(time0, time1, auxBuffers);
381
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
64 if ( math::isZero(dt) ) break;//V is essentially zero so terminate
382
383 OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time
384 switch(TemporalScheme) {
385 case math::TVD_RK1:
386 // Perform one explicit Euler step: t1 = t0 + dt
387 // Phi_t1(1) = Phi_t0(0) - dt * Speed(2) * |Grad[Phi(0)]|
388 mTask = std::bind(&Morph::euler01, ph::_1, ph::_2, dt, /*speed*/2);
389
390 // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1)
391 this->cook(PARALLEL_FOR, 1);
392 break;
393 case math::TVD_RK2:
394 // Perform one explicit Euler step: t1 = t0 + dt
395 // Phi_t1(1) = Phi_t0(0) - dt * Speed(2) * |Grad[Phi(0)]|
396 mTask = std::bind(&Morph::euler01, ph::_1, ph::_2, dt, /*speed*/2);
397
398 // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1)
399 this->cook(PARALLEL_FOR, 1);
400
401 // Convex combine explict Euler step: t2 = t0 + dt
402 // Phi_t2(1) = 1/2 * Phi_t0(1) + 1/2 * (Phi_t1(0) - dt * Speed(2) * |Grad[Phi(0)]|)
403 mTask = std::bind(&Morph::euler12, ph::_1, ph::_2, dt);
404
405 // Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1)
406 this->cook(PARALLEL_FOR, 1);
407 break;
408 64 case math::TVD_RK3:
409 // Perform one explicit Euler step: t1 = t0 + dt
410 // Phi_t1(1) = Phi_t0(0) - dt * Speed(3) * |Grad[Phi(0)]|
411 64 mTask = std::bind(&Morph::euler01, ph::_1, ph::_2, dt, /*speed*/3);
412
413 // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1)
414 64 this->cook(PARALLEL_FOR, 1);
415
416 // Convex combine explict Euler step: t2 = t0 + dt/2
417 // Phi_t2(2) = 3/4 * Phi_t0(1) + 1/4 * (Phi_t1(0) - dt * Speed(3) * |Grad[Phi(0)]|)
418 64 mTask = std::bind(&Morph::euler34, ph::_1, ph::_2, dt);
419
420 // Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2)
421 64 this->cook(PARALLEL_FOR, 2);
422
423 // Convex combine explict Euler step: t3 = t0 + dt
424 // Phi_t3(2) = 1/3 * Phi_t0(1) + 2/3 * (Phi_t2(0) - dt * Speed(3) * |Grad[Phi(0)]|)
425 64 mTask = std::bind(&Morph::euler13, ph::_1, ph::_2, dt);
426
427 // Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2)
428 64 this->cook(PARALLEL_FOR, 2);
429 break;
430 case math::UNKNOWN_TIS:
431 default:
432 OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!");
433 }//end of compile-time resolved switch
434 OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
435
436 64 time0 += dt;
437 64 ++countCFL;
438 64 mParent->mTracker.leafs().removeAuxBuffers();
439
440 // Track the narrow band
441 64 mParent->mTracker.track();
442 }//end wile-loop over time
443
444 64 return countCFL;//number of CLF propagation steps
445 }
446
447 template<typename GridT, typename InterruptT>
448 template<typename MapT, math::BiasedGradientScheme SpatialScheme,
449 math::TemporalIntegrationScheme TemporalScheme>
450 inline typename GridT::ValueType
451 64 LevelSetMorphing<GridT, InterruptT>::
452 Morph<MapT, SpatialScheme, TemporalScheme>::
453 sampleSpeed(ValueType time0, ValueType time1, Index speedBuffer)
454 {
455 namespace ph = std::placeholders;
456
457 64 mMaxAbsS = mMinAbsS;
458
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
64 const size_t leafCount = mParent->mTracker.leafs().leafCount();
459
2/4
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
64 if (leafCount==0 || time0 >= time1) return ValueType(0);
460
461 const math::Transform& xform = mParent->mTracker.grid().transform();
462
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
64 if (mParent->mTarget->transform() == xform &&
463
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
64 (mParent->mMask == nullptr || mParent->mMask->transform() == xform)) {
464 64 mTask = std::bind(&Morph::sampleAlignedSpeed, ph::_1, ph::_2, speedBuffer);
465 } else {
466 mTask = std::bind(&Morph::sampleXformedSpeed, ph::_1, ph::_2, speedBuffer);
467 }
468 64 this->cook(PARALLEL_REDUCE);
469
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
64 if (math::isApproxEqual(mMinAbsS, mMaxAbsS)) return ValueType(0);//speed is essentially zero
470
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 31 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
64 static const ValueType CFL = (TemporalScheme == math::TVD_RK1 ? ValueType(0.3) :
471 TemporalScheme == math::TVD_RK2 ? ValueType(0.9) :
472 ValueType(1.0))/math::Sqrt(ValueType(3.0));
473
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
64 const ValueType dt = math::Abs(time1 - time0), dx = mParent->mTracker.voxelSize();
474
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
64 return math::Min(dt, ValueType(CFL*dx/mMaxAbsS));
475 }
476
477 template<typename GridT, typename InterruptT>
478 template <typename MapT, math::BiasedGradientScheme SpatialScheme,
479 math::TemporalIntegrationScheme TemporalScheme>
480 inline void
481 LevelSetMorphing<GridT, InterruptT>::
482 Morph<MapT, SpatialScheme, TemporalScheme>::
483 sampleXformedSpeed(const LeafRange& range, Index speedBuffer)
484 {
485 using VoxelIterT = typename LeafType::ValueOnCIter;
486 using SamplerT = tools::GridSampler<typename GridT::ConstAccessor, tools::BoxSampler>;
487
488 const MapT& map = *mMap;
489 mParent->mTracker.checkInterrupter();
490
491 typename GridT::ConstAccessor targetAcc = mParent->mTarget->getAccessor();
492 SamplerT target(targetAcc, mParent->mTarget->transform());
493 if (mParent->mMask == nullptr) {
494 for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
495 ValueType* speed = leafIter.buffer(speedBuffer).data();
496 bool isZero = true;
497 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
498 ValueType& s = speed[voxelIter.pos()];
499 s -= target.wsSample(map.applyMap(voxelIter.getCoord().asVec3d()));
500 if (!math::isApproxZero(s)) isZero = false;
501 mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s));
502 }
503 if (isZero) speed[0] = std::numeric_limits<ValueType>::max();//tag first voxel
504 }
505 } else {
506 const ValueType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask);
507 const bool invMask = mParent->isMaskInverted();
508 typename GridT::ConstAccessor maskAcc = mParent->mMask->getAccessor();
509 SamplerT mask(maskAcc, mParent->mMask->transform());
510 for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
511 ValueType* speed = leafIter.buffer(speedBuffer).data();
512 bool isZero = true;
513 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
514 const Vec3R xyz = map.applyMap(voxelIter.getCoord().asVec3d());//world space
515 const ValueType a = math::SmoothUnitStep((mask.wsSample(xyz)-min)*invNorm);
516 ValueType& s = speed[voxelIter.pos()];
517 s -= target.wsSample(xyz);
518 s *= invMask ? 1 - a : a;
519 if (!math::isApproxZero(s)) isZero = false;
520 mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s));
521 }
522 if (isZero) speed[0] = std::numeric_limits<ValueType>::max();//tag first voxel
523 }
524 }
525 }
526
527 template<typename GridT, typename InterruptT>
528 template <typename MapT, math::BiasedGradientScheme SpatialScheme,
529 math::TemporalIntegrationScheme TemporalScheme>
530 inline void
531 3244 LevelSetMorphing<GridT, InterruptT>::
532 Morph<MapT, SpatialScheme, TemporalScheme>::
533 sampleAlignedSpeed(const LeafRange& range, Index speedBuffer)
534 {
535 using VoxelIterT = typename LeafType::ValueOnCIter;
536
537 3244 mParent->mTracker.checkInterrupter();
538
539 3244 typename GridT::ConstAccessor target = mParent->mTarget->getAccessor();
540
541
1/2
✓ Branch 0 taken 1622 times.
✗ Branch 1 not taken.
3244 if (mParent->mMask == nullptr) {
542
2/2
✓ Branch 1 taken 1622 times.
✓ Branch 2 taken 1622 times.
6488 for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
543
1/2
✓ Branch 2 taken 1622 times.
✗ Branch 3 not taken.
3244 ValueType* speed = leafIter.buffer(speedBuffer).data();
544 bool isZero = true;
545
2/2
✓ Branch 0 taken 221022 times.
✓ Branch 1 taken 1622 times.
445288 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
546 442044 ValueType& s = speed[voxelIter.pos()];
547
4/8
✓ Branch 1 taken 221022 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 221022 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 98930 times.
✓ Branch 7 taken 122092 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
442044 s -= target.getValue(voxelIter.getCoord());
548 if (!math::isApproxZero(s)) isZero = false;
549
2/2
✓ Branch 0 taken 1512 times.
✓ Branch 1 taken 219510 times.
445068 mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s));
550 }
551
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1622 times.
3244 if (isZero) speed[0] = std::numeric_limits<ValueType>::max();//tag first voxel
552 }
553 } else {
554 const ValueType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask);
555 const bool invMask = mParent->isMaskInverted();
556 typename GridT::ConstAccessor mask = mParent->mMask->getAccessor();
557 for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
558 ValueType* speed = leafIter.buffer(speedBuffer).data();
559 bool isZero = true;
560 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
561 const Coord ijk = voxelIter.getCoord();//index space
562 const ValueType a = math::SmoothUnitStep((mask.getValue(ijk)-min)*invNorm);
563 ValueType& s = speed[voxelIter.pos()];
564 s -= target.getValue(ijk);
565 s *= invMask ? 1 - a : a;
566 if (!math::isApproxZero(s)) isZero = false;
567 mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s));
568 }
569 if (isZero) speed[0] = std::numeric_limits<ValueType>::max();//tag first voxel
570 }
571 }
572 }
573
574 template<typename GridT, typename InterruptT>
575 template <typename MapT, math::BiasedGradientScheme SpatialScheme,
576 math::TemporalIntegrationScheme TemporalScheme>
577 inline void
578 256 LevelSetMorphing<GridT, InterruptT>::
579 Morph<MapT, SpatialScheme, TemporalScheme>::
580 cook(ThreadingMode mode, size_t swapBuffer)
581 {
582
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
256 mParent->mTracker.startInterrupter("Morphing level set");
583
584
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
256 const int grainSize = mParent->mTracker.getGrainSize();
585
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
256 const LeafRange range = mParent->mTracker.leafs().leafRange(grainSize);
586
587
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
256 if (mParent->mTracker.getGrainSize()==0) {
588 (*this)(range);
589
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 32 times.
256 } else if (mode == PARALLEL_FOR) {
590 192 tbb::parallel_for(range, *this);
591
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
64 } else if (mode == PARALLEL_REDUCE) {
592 64 tbb::parallel_reduce(range, *this);
593 } else {
594 OPENVDB_THROW(ValueError, "expected threading mode " << int(PARALLEL_FOR)
595 << " or " << int(PARALLEL_REDUCE) << ", got " << int(mode));
596 }
597
598 256 mParent->mTracker.leafs().swapLeafBuffer(swapBuffer, grainSize == 0);
599
600
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
256 mParent->mTracker.endInterrupter();
601 }
602
603 template<typename GridT, typename InterruptT>
604 template<typename MapT, math::BiasedGradientScheme SpatialScheme,
605 math::TemporalIntegrationScheme TemporalScheme>
606 template <int Nominator, int Denominator>
607 inline void
608 9732 LevelSetMorphing<GridT,InterruptT>::
609 Morph<MapT, SpatialScheme, TemporalScheme>::
610 euler(const LeafRange& range, ValueType dt,
611 Index phiBuffer, Index resultBuffer, Index speedBuffer)
612 {
613 using SchemeT = math::BIAS_SCHEME<SpatialScheme>;
614 using StencilT = typename SchemeT::template ISStencil<GridType>::StencilType;
615 using VoxelIterT = typename LeafType::ValueOnCIter;
616 using NumGrad = math::GradientNormSqrd<MapT, SpatialScheme>;
617
618 static const ValueType Alpha = ValueType(Nominator)/ValueType(Denominator);
619 static const ValueType Beta = ValueType(1) - Alpha;
620
621 9732 mParent->mTracker.checkInterrupter();
622 9732 const MapT& map = *mMap;
623 9732 StencilT stencil(mParent->mTracker.grid());
624
625
2/2
✓ Branch 1 taken 4866 times.
✓ Branch 2 taken 4866 times.
19464 for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
626
1/2
✓ Branch 2 taken 4866 times.
✗ Branch 3 not taken.
9732 const ValueType* speed = leafIter.buffer(speedBuffer).data();
627
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4866 times.
9732 if (math::isExactlyEqual(speed[0], std::numeric_limits<ValueType>::max())) continue;
628
1/2
✓ Branch 2 taken 4866 times.
✗ Branch 3 not taken.
9732 const ValueType* phi = leafIter.buffer(phiBuffer).data();
629
1/2
✓ Branch 2 taken 4866 times.
✗ Branch 3 not taken.
9732 ValueType* result = leafIter.buffer(resultBuffer).data();
630
2/2
✓ Branch 0 taken 663066 times.
✓ Branch 1 taken 4866 times.
1335864 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
631 const Index n = voxelIter.pos();
632
2/2
✓ Branch 0 taken 296790 times.
✓ Branch 1 taken 366276 times.
1326144 if (math::isApproxZero(speed[n])) continue;
633
1/2
✓ Branch 1 taken 663060 times.
✗ Branch 2 not taken.
1326120 stencil.moveTo(voxelIter);
634 1326120 const ValueType v = stencil.getValue() - dt * speed[n] * NumGrad::result(map, stencil);
635 1326120 result[n] = Nominator ? Alpha * phi[n] + Beta * v : v;
636 }//loop over active voxels in the leaf of the mask
637 }//loop over leafs of the level set
638 }
639
640
641 ////////////////////////////////////////
642
643
644 // Explicit Template Instantiation
645
646 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
647
648 #ifdef OPENVDB_INSTANTIATE_LEVELSETMORPH
649 #include <openvdb/util/ExplicitInstantiation.h>
650 #endif
651
652 OPENVDB_INSTANTIATE_CLASS LevelSetMorphing<FloatGrid, util::NullInterrupter>;
653 OPENVDB_INSTANTIATE_CLASS LevelSetMorphing<DoubleGrid, util::NullInterrupter>;
654
655 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
656
657
658 } // namespace tools
659 } // namespace OPENVDB_VERSION_NAME
660 } // namespace openvdb
661
662 #endif // OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED
663