Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | // | ||
4 | /// @author Ken Museth, D.J. Hill (openvdb port, added staggered grid support) | ||
5 | /// | ||
6 | /// @file tools/PointAdvect.h | ||
7 | /// | ||
8 | /// @brief Class PointAdvect advects points (with position) in a static velocity field | ||
9 | |||
10 | #ifndef OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED | ||
11 | #define OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED | ||
12 | |||
13 | #include "openvdb/openvdb.h" | ||
14 | #include "openvdb/Types.h" // Vec3 types and version number | ||
15 | #include "openvdb/Grid.h" // grid | ||
16 | #include "openvdb/math/Math.h" // min | ||
17 | #include "openvdb/util/NullInterrupter.h" | ||
18 | #include "openvdb/thread/Threading.h" | ||
19 | #include "Interpolation.h" // sampling | ||
20 | #include "VelocityFields.h" // VelocityIntegrator | ||
21 | |||
22 | #include <tbb/blocked_range.h> // threading | ||
23 | #include <tbb/parallel_for.h> // threading | ||
24 | |||
25 | #include <vector> | ||
26 | |||
27 | |||
28 | namespace openvdb { | ||
29 | OPENVDB_USE_VERSION_NAMESPACE | ||
30 | namespace OPENVDB_VERSION_NAME { | ||
31 | namespace tools { | ||
32 | |||
33 | /// Class that holds a Vec3 grid, to be interpreted as the closest point to a constraint | ||
34 | /// surface. Supports a method to allow a point to be projected onto the closest point | ||
35 | /// on the constraint surface. Uses Caching. | ||
36 | template<typename CptGridT = Vec3fGrid> | ||
37 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | class ClosestPointProjector |
38 | { | ||
39 | public: | ||
40 | using CptGridType = CptGridT; | ||
41 | using CptAccessor = typename CptGridType::ConstAccessor; | ||
42 | using CptValueType = typename CptGridType::ValueType; | ||
43 | |||
44 | ClosestPointProjector(): | ||
45 | mCptIterations(0) | ||
46 | { | ||
47 | } | ||
48 | 5 | ClosestPointProjector(const CptGridType& cptGrid, int n): | |
49 | mCptGrid(&cptGrid), | ||
50 | mCptAccessor(cptGrid.getAccessor()), | ||
51 | 10 | mCptIterations(n) | |
52 | { | ||
53 | } | ||
54 | ClosestPointProjector(const ClosestPointProjector &other): | ||
55 | mCptGrid(other.mCptGrid), | ||
56 | mCptAccessor(mCptGrid->getAccessor()), | ||
57 | mCptIterations(other.mCptIterations) | ||
58 | { | ||
59 | } | ||
60 | void setConstraintIterations(unsigned int cptIterations) { mCptIterations = cptIterations; } | ||
61 | unsigned int numIterations() { return mCptIterations; } | ||
62 | |||
63 | // point constraint | ||
64 | template <typename LocationType> | ||
65 | 20 | inline void projectToConstraintSurface(LocationType& W) const | |
66 | { | ||
67 | /// Entries in the CPT tree are the closest point to the constraint surface. | ||
68 | /// The interpolation step in sample introduces error so that the result | ||
69 | /// of a single sample may not lie exactly on the surface. The iterations | ||
70 | /// in the loop exist to minimize this error. | ||
71 | 20 | CptValueType result(W[0], W[1],W[2]); | |
72 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 20 times.
|
120 | for (unsigned int i = 0; i < mCptIterations; ++i) { |
73 | 100 | const Vec3R location = mCptGrid->worldToIndex(Vec3R(result[0], result[1], result[2])); | |
74 | 100 | BoxSampler::sample<CptAccessor>(mCptAccessor, location, result); | |
75 | } | ||
76 | 20 | W[0] = result[0]; | |
77 | 20 | W[1] = result[1]; | |
78 | 20 | W[2] = result[2]; | |
79 | 20 | } | |
80 | |||
81 | private: | ||
82 | const CptGridType* mCptGrid; // Closest-Point-Transform vector field | ||
83 | CptAccessor mCptAccessor; | ||
84 | unsigned int mCptIterations; | ||
85 | };// end of ClosestPointProjector class | ||
86 | |||
87 | //////////////////////////////////////// | ||
88 | |||
89 | |||
90 | /// Performs passive or constrained advection of points in a velocity field | ||
91 | /// represented by an OpenVDB grid and an optional closest-point-transform (CPT) | ||
92 | /// represented in another OpenVDB grid. Note the CPT is assumed to be | ||
93 | /// in world coordinates and NOT index coordinates! | ||
94 | /// Supports both collocated velocity grids and staggered velocity grids | ||
95 | /// | ||
96 | /// The @c PointListT template argument refers to any class with the following | ||
97 | /// interface (e.g., std::vector<openvdb::Vec3f>): | ||
98 | /// @code | ||
99 | /// class PointList { | ||
100 | /// ... | ||
101 | /// public: | ||
102 | /// using value_type = internal_vector3_type; // must support [] component access | ||
103 | /// openvdb::Index size() const; // number of points in list | ||
104 | /// value_type& operator[](int n); // world space position of nth point | ||
105 | /// }; | ||
106 | /// @endcode | ||
107 | /// | ||
108 | /// @note All methods (except size) are assumed to be thread-safe and | ||
109 | /// the positions are returned as non-const references since the | ||
110 | /// advection method needs to modify them! | ||
111 | template<typename GridT = Vec3fGrid, | ||
112 | typename PointListT = std::vector<typename GridT::ValueType>, | ||
113 | bool StaggeredVelocity = false, | ||
114 | typename InterrupterType = util::NullInterrupter> | ||
115 | class PointAdvect | ||
116 | { | ||
117 | public: | ||
118 | using GridType = GridT; | ||
119 | using PointListType = PointListT; | ||
120 | using LocationType = typename PointListT::value_type; | ||
121 | using VelocityFieldIntegrator = VelocityIntegrator<GridT, StaggeredVelocity>; | ||
122 | |||
123 | 1 | PointAdvect(const GridT& velGrid, InterrupterType* interrupter = nullptr): | |
124 | mVelGrid(&velGrid), | ||
125 | mPoints(nullptr), | ||
126 | mIntegrationOrder(1), | ||
127 | mThreaded(true), | ||
128 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | mInterrupter(interrupter) |
129 | { | ||
130 | } | ||
131 | 101 | PointAdvect(const PointAdvect &other) : | |
132 | 101 | mVelGrid(other.mVelGrid), | |
133 | 101 | mPoints(other.mPoints), | |
134 | 101 | mDt(other.mDt), | |
135 | 101 | mAdvIterations(other.mAdvIterations), | |
136 | 101 | mIntegrationOrder(other.mIntegrationOrder), | |
137 | 101 | mThreaded(other.mThreaded), | |
138 | 5 | mInterrupter(other.mInterrupter) | |
139 | { | ||
140 | } | ||
141 | ✗ | virtual ~PointAdvect() | |
142 | { | ||
143 | } | ||
144 | /// If the order of the integration is set to zero no advection is performed | ||
145 | 4 | bool earlyOut() const { return (mIntegrationOrder==0);} | |
146 | /// get & set | ||
147 | void setThreaded(bool threaded) { mThreaded = threaded; } | ||
148 | bool getThreaded() { return mThreaded; } | ||
149 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | void setIntegrationOrder(unsigned int order) {mIntegrationOrder = order;} |
150 | |||
151 | /// Constrained advection of a list of points over a time = dt * advIterations | ||
152 | 4 | void advect(PointListT& points, float dt, unsigned int advIterations = 1) | |
153 | { | ||
154 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (this->earlyOut()) return; // nothing to do! |
155 | 4 | mPoints = &points; | |
156 | 4 | mDt = dt; | |
157 | 4 | mAdvIterations = advIterations; | |
158 | |||
159 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (mInterrupter) mInterrupter->start("Advecting points by OpenVDB velocity field: "); |
160 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (mThreaded) { |
161 | 4 | tbb::parallel_for(tbb::blocked_range<size_t>(0, mPoints->size()), *this); | |
162 | } else { | ||
163 | ✗ | (*this)(tbb::blocked_range<size_t>(0, mPoints->size())); | |
164 | } | ||
165 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (mInterrupter) mInterrupter->end(); |
166 | } | ||
167 | |||
168 | /// Never call this method directly - it is use by TBB and has to be public! | ||
169 | 520 | void operator() (const tbb::blocked_range<size_t> &range) const | |
170 | { | ||
171 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 520 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
520 | if (mInterrupter && mInterrupter->wasInterrupted()) { |
172 | ✗ | thread::cancelGroupExecution(); | |
173 | } | ||
174 | |||
175 | 520 | VelocityFieldIntegrator velField(*mVelGrid); | |
176 |
4/5✓ Branch 0 taken 128 times.
✓ Branch 1 taken 128 times.
✓ Branch 2 taken 136 times.
✓ Branch 3 taken 128 times.
✗ Branch 4 not taken.
|
520 | switch (mIntegrationOrder) { |
177 | case 1: | ||
178 | { | ||
179 |
2/2✓ Branch 0 taken 2000000 times.
✓ Branch 1 taken 128 times.
|
2000128 | for (size_t n = range.begin(); n != range.end(); ++n) { |
180 | 2000000 | LocationType& X0 = (*mPoints)[n]; | |
181 | // loop over number of time steps | ||
182 |
2/2✓ Branch 0 taken 2000000 times.
✓ Branch 1 taken 2000000 times.
|
4000000 | for (unsigned int i = 0; i < mAdvIterations; ++i) { |
183 |
1/2✓ Branch 1 taken 2000000 times.
✗ Branch 2 not taken.
|
2000000 | velField.template rungeKutta<1>(mDt, X0); |
184 | } | ||
185 | } | ||
186 | } | ||
187 | break; | ||
188 | case 2: | ||
189 | { | ||
190 |
2/2✓ Branch 0 taken 2000000 times.
✓ Branch 1 taken 128 times.
|
2000128 | for (size_t n = range.begin(); n != range.end(); ++n) { |
191 | 2000000 | LocationType& X0 = (*mPoints)[n]; | |
192 | // loop over number of time steps | ||
193 |
2/2✓ Branch 0 taken 2000000 times.
✓ Branch 1 taken 2000000 times.
|
4000000 | for (unsigned int i = 0; i < mAdvIterations; ++i) { |
194 |
1/2✓ Branch 1 taken 2000000 times.
✗ Branch 2 not taken.
|
2000000 | velField.template rungeKutta<2>(mDt, X0); |
195 | } | ||
196 | } | ||
197 | } | ||
198 | break; | ||
199 | case 3: | ||
200 | { | ||
201 |
2/2✓ Branch 0 taken 2000000 times.
✓ Branch 1 taken 136 times.
|
2000136 | for (size_t n = range.begin(); n != range.end(); ++n) { |
202 | 2000000 | LocationType& X0 = (*mPoints)[n]; | |
203 | // loop over number of time steps | ||
204 |
2/2✓ Branch 0 taken 2000000 times.
✓ Branch 1 taken 2000000 times.
|
4000000 | for (unsigned int i = 0; i < mAdvIterations; ++i) { |
205 |
1/2✓ Branch 1 taken 2000000 times.
✗ Branch 2 not taken.
|
2000000 | velField.template rungeKutta<3>(mDt, X0); |
206 | } | ||
207 | } | ||
208 | } | ||
209 | break; | ||
210 | case 4: | ||
211 | { | ||
212 |
2/2✓ Branch 0 taken 2000000 times.
✓ Branch 1 taken 128 times.
|
2000128 | for (size_t n = range.begin(); n != range.end(); ++n) { |
213 | 2000000 | LocationType& X0 = (*mPoints)[n]; | |
214 | // loop over number of time steps | ||
215 |
2/2✓ Branch 0 taken 2000000 times.
✓ Branch 1 taken 2000000 times.
|
4000000 | for (unsigned int i = 0; i < mAdvIterations; ++i) { |
216 |
1/2✓ Branch 1 taken 2000000 times.
✗ Branch 2 not taken.
|
2000000 | velField.template rungeKutta<4>(mDt, X0); |
217 | } | ||
218 | } | ||
219 | } | ||
220 | break; | ||
221 | } | ||
222 | 520 | } | |
223 | |||
224 | private: | ||
225 | // the velocity field | ||
226 | const GridType* mVelGrid; | ||
227 | |||
228 | // vertex list of all the points | ||
229 | PointListT* mPoints; | ||
230 | |||
231 | // time integration parameters | ||
232 | float mDt; // time step | ||
233 | unsigned int mAdvIterations; // number of time steps | ||
234 | unsigned int mIntegrationOrder; | ||
235 | |||
236 | // operational parameters | ||
237 | bool mThreaded; | ||
238 | InterrupterType* mInterrupter; | ||
239 | |||
240 | };//end of PointAdvect class | ||
241 | |||
242 | |||
243 | template<typename GridT = Vec3fGrid, | ||
244 | typename PointListT = std::vector<typename GridT::ValueType>, | ||
245 | bool StaggeredVelocity = false, | ||
246 | typename CptGridType = GridT, | ||
247 | typename InterrupterType = util::NullInterrupter> | ||
248 | class ConstrainedPointAdvect | ||
249 | { | ||
250 | public: | ||
251 | using GridType = GridT; | ||
252 | using LocationType = typename PointListT::value_type; | ||
253 | using VelocityIntegratorType = VelocityIntegrator<GridT, StaggeredVelocity>; | ||
254 | using ClosestPointProjectorType = ClosestPointProjector<CptGridType>; | ||
255 | using PointListType = PointListT; | ||
256 | |||
257 | 1 | ConstrainedPointAdvect(const GridType& velGrid, | |
258 | const GridType& cptGrid, int cptn, InterrupterType* interrupter = nullptr): | ||
259 | mVelGrid(&velGrid), | ||
260 | mCptGrid(&cptGrid), | ||
261 | mCptIter(cptn), | ||
262 | 1 | mInterrupter(interrupter) | |
263 | { | ||
264 | } | ||
265 | ✗ | ConstrainedPointAdvect(const ConstrainedPointAdvect& other): | |
266 | ✗ | mVelGrid(other.mVelGrid), | |
267 | ✗ | mCptGrid(other.mCptGrid), | |
268 | ✗ | mCptIter(other.mCptIter), | |
269 | ✗ | mPoints(other.mPoints), | |
270 | ✗ | mDt(other.mDt), | |
271 | ✗ | mAdvIterations(other.mAdvIterations), | |
272 | ✗ | mIntegrationOrder(other.mIntegrationOrder), | |
273 | ✗ | mThreaded(other.mThreaded), | |
274 | ✗ | mInterrupter(other.mInterrupter) | |
275 | { | ||
276 | } | ||
277 |
1/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1 | virtual ~ConstrainedPointAdvect(){} |
278 | |||
279 | 1 | void setConstraintIterations(unsigned int cptIter) {mCptIter = cptIter;} | |
280 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | void setIntegrationOrder(unsigned int order) {mIntegrationOrder = order;} |
281 | |||
282 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | void setThreaded(bool threaded) { mThreaded = threaded; } |
283 | bool getThreaded() { return mThreaded; } | ||
284 | |||
285 | /// Constrained Advection a list of points over a time = dt * advIterations | ||
286 | 5 | void advect(PointListT& points, float dt, unsigned int advIterations = 1) | |
287 | { | ||
288 | 5 | mPoints = &points; | |
289 | 5 | mDt = dt; | |
290 | |||
291 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
5 | if (mIntegrationOrder==0 && mCptIter == 0) { |
292 | return; // nothing to do! | ||
293 | } | ||
294 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | (mIntegrationOrder>0) ? mAdvIterations = advIterations : mAdvIterations = 1; |
295 | |||
296 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (mInterrupter) mInterrupter->start("Advecting points by OpenVDB velocity field: "); |
297 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | const size_t N = mPoints->size(); |
298 | |||
299 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (mThreaded) { |
300 | ✗ | tbb::parallel_for(tbb::blocked_range<size_t>(0, N), *this); | |
301 | } else { | ||
302 | 5 | (*this)(tbb::blocked_range<size_t>(0, N)); | |
303 | } | ||
304 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (mInterrupter) mInterrupter->end(); |
305 | } | ||
306 | |||
307 | |||
308 | /// Never call this method directly - it is use by TBB and has to be public! | ||
309 | 5 | void operator() (const tbb::blocked_range<size_t> &range) const | |
310 | { | ||
311 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
5 | if (mInterrupter && mInterrupter->wasInterrupted()) { |
312 | ✗ | thread::cancelGroupExecution(); | |
313 | } | ||
314 | |||
315 | 5 | VelocityIntegratorType velField(*mVelGrid); | |
316 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | ClosestPointProjectorType cptField(*mCptGrid, mCptIter); |
317 |
5/6✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
5 | switch (mIntegrationOrder) { |
318 | case 0://pure CPT projection | ||
319 | { | ||
320 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | for (size_t n = range.begin(); n != range.end(); ++n) { |
321 | 4 | LocationType& X0 = (*mPoints)[n]; | |
322 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (unsigned int i = 0; i < mAdvIterations; ++i) { |
323 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | cptField.projectToConstraintSurface(X0); |
324 | } | ||
325 | } | ||
326 | } | ||
327 | break; | ||
328 | case 1://1'th order advection and CPT projection | ||
329 | { | ||
330 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | for (size_t n = range.begin(); n != range.end(); ++n) { |
331 | 4 | LocationType& X0 = (*mPoints)[n]; | |
332 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (unsigned int i = 0; i < mAdvIterations; ++i) { |
333 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | velField.template rungeKutta<1>(mDt, X0); |
334 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | cptField.projectToConstraintSurface(X0); |
335 | } | ||
336 | } | ||
337 | } | ||
338 | break; | ||
339 | case 2://2'nd order advection and CPT projection | ||
340 | { | ||
341 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | for (size_t n = range.begin(); n != range.end(); ++n) { |
342 | 4 | LocationType& X0 = (*mPoints)[n]; | |
343 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (unsigned int i = 0; i < mAdvIterations; ++i) { |
344 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | velField.template rungeKutta<2>(mDt, X0); |
345 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | cptField.projectToConstraintSurface(X0); |
346 | } | ||
347 | } | ||
348 | } | ||
349 | break; | ||
350 | |||
351 | case 3://3'rd order advection and CPT projection | ||
352 | { | ||
353 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | for (size_t n = range.begin(); n != range.end(); ++n) { |
354 | 4 | LocationType& X0 = (*mPoints)[n]; | |
355 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (unsigned int i = 0; i < mAdvIterations; ++i) { |
356 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | velField.template rungeKutta<3>(mDt, X0); |
357 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | cptField.projectToConstraintSurface(X0); |
358 | } | ||
359 | } | ||
360 | } | ||
361 | break; | ||
362 | case 4://4'th order advection and CPT projection | ||
363 | { | ||
364 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | for (size_t n = range.begin(); n != range.end(); ++n) { |
365 | 4 | LocationType& X0 = (*mPoints)[n]; | |
366 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (unsigned int i = 0; i < mAdvIterations; ++i) { |
367 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | velField.template rungeKutta<4>(mDt, X0); |
368 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | cptField.projectToConstraintSurface(X0); |
369 | } | ||
370 | } | ||
371 | } | ||
372 | break; | ||
373 | } | ||
374 | 5 | } | |
375 | |||
376 | private: | ||
377 | const GridType* mVelGrid; // the velocity field | ||
378 | const GridType* mCptGrid; | ||
379 | int mCptIter; | ||
380 | PointListT* mPoints; // vertex list of all the points | ||
381 | |||
382 | // time integration parameters | ||
383 | float mDt; // time step | ||
384 | unsigned int mAdvIterations; // number of time steps | ||
385 | unsigned int mIntegrationOrder; // order of Runge-Kutta integration | ||
386 | // operational parameters | ||
387 | bool mThreaded; | ||
388 | InterrupterType* mInterrupter; | ||
389 | };// end of ConstrainedPointAdvect class | ||
390 | |||
391 | } // namespace tools | ||
392 | } // namespace OPENVDB_VERSION_NAME | ||
393 | } // namespace openvdb | ||
394 | |||
395 | #endif // OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED | ||
396 |