38 #ifndef OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED 39 #define OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED 72 template<
typename GridT,
73 typename InterruptT = util::NullInterrupter>
86 LevelSetMorphing(GridT& sourceGrid,
const GridT& targetGrid, InterruptT* interrupt =
nullptr)
87 : mTracker(sourceGrid, interrupt)
88 , mTarget(&targetGrid)
91 , mTemporalScheme(math::
TVD_RK2)
101 void setTarget(
const GridT& targetGrid) { mTarget = &targetGrid; }
119 return mTracker.getSpatialScheme();
124 mTracker.setSpatialScheme(scheme);
129 return mTracker.getTemporalScheme();
134 mTracker.setTemporalScheme(scheme);
180 size_t advect(ValueType time0, ValueType time1);
188 template<math::BiasedGradientScheme SpatialScheme>
189 size_t advect1(ValueType time0, ValueType time1);
193 size_t advect2(ValueType time0, ValueType time1);
198 size_t advect3(ValueType time0, ValueType time1);
201 const GridT *mTarget, *mMask;
204 ValueType mMinMask, mDeltaMask;
215 Morph(
const Morph& other);
217 Morph(Morph& other, tbb::split);
222 size_t advect(ValueType time0, ValueType time1);
224 void operator()(
const LeafRange& r)
const 226 if (mTask) mTask(const_cast<Morph*>(
this), r);
230 void operator()(
const LeafRange& r)
232 if (mTask) mTask(
this, r);
236 void join(
const Morph& other) { mMaxAbsS =
math::Max(mMaxAbsS, other.mMaxAbsS); }
239 enum ThreadingMode { PARALLEL_FOR, PARALLEL_REDUCE };
241 void cook(ThreadingMode mode,
size_t swapBuffer = 0);
244 typename GridT::ValueType sampleSpeed(ValueType time0, ValueType time1,
Index speedBuffer);
245 void sampleXformedSpeed(
const LeafRange& r,
Index speedBuffer);
246 void sampleAlignedSpeed(
const LeafRange& r,
Index speedBuffer);
250 template <
int Nominator,
int Denominator>
252 inline void euler01(
const LeafRange& r, ValueType t,
Index s) {this->euler<0,1>(r,t,0,1,s);}
253 inline void euler12(
const LeafRange& r, ValueType t) {this->euler<1,2>(r, t, 1, 1, 2);}
254 inline void euler34(
const LeafRange& r, ValueType t) {this->euler<3,4>(r, t, 1, 2, 3);}
255 inline void euler13(
const LeafRange& r, ValueType t) {this->euler<1,3>(r, t, 1, 2, 3);}
257 using FuncType =
typename std::function<void (Morph*,
const LeafRange&)>;
258 LevelSetMorphing* mParent;
259 ValueType mMinAbsS, mMaxAbsS;
266 template<
typename Gr
idT,
typename InterruptT>
270 switch (mSpatialScheme) {
272 return this->advect1<math::FIRST_BIAS >(time0, time1);
280 return this->advect1<math::HJWENO5_BIAS>(time0, time1);
291 template<
typename Gr
idT,
typename InterruptT>
292 template<math::BiasedGradientScheme SpatialScheme>
296 switch (mTemporalScheme) {
298 return this->advect2<SpatialScheme, math::TVD_RK1>(time0, time1);
300 return this->advect2<SpatialScheme, math::TVD_RK2>(time0, time1);
302 return this->advect2<SpatialScheme, math::TVD_RK3>(time0, time1);
310 template<
typename Gr
idT,
typename InterruptT>
314 LevelSetMorphing<GridT, InterruptT>::advect2(ValueType time0, ValueType time1)
317 if (trans.
mapType() == math::UniformScaleMap::mapType()) {
318 return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleMap>(time0, time1);
319 }
else if (trans.
mapType() == math::UniformScaleTranslateMap::mapType()) {
320 return this->advect3<SpatialScheme, TemporalScheme, math::UniformScaleTranslateMap>(
322 }
else if (trans.
mapType() == math::UnitaryMap::mapType()) {
323 return this->advect3<SpatialScheme, TemporalScheme, math::UnitaryMap >(time0, time1);
324 }
else if (trans.
mapType() == math::TranslationMap::mapType()) {
325 return this->advect3<SpatialScheme, TemporalScheme, math::TranslationMap>(time0, time1);
332 template<
typename Gr
idT,
typename InterruptT>
337 LevelSetMorphing<GridT, InterruptT>::advect3(ValueType time0, ValueType time1)
339 Morph<MapT, SpatialScheme, TemporalScheme> tmp(*
this);
340 return tmp.advect(time0, time1);
346 template<
typename Gr
idT,
typename InterruptT>
350 LevelSetMorphing<GridT, InterruptT>::
351 Morph<MapT, SpatialScheme, TemporalScheme>::
352 Morph(LevelSetMorphing<GridT, InterruptT>& parent)
354 , mMinAbsS(ValueType(1e-6))
355 , mMap(parent.mTracker.grid().transform().template constMap<MapT>().get())
360 template<
typename Gr
idT,
typename InterruptT>
364 LevelSetMorphing<GridT, InterruptT>::
365 Morph<MapT, SpatialScheme, TemporalScheme>::
366 Morph(
const Morph& other)
367 : mParent(other.mParent)
368 , mMinAbsS(other.mMinAbsS)
369 , mMaxAbsS(other.mMaxAbsS)
375 template<
typename Gr
idT,
typename InterruptT>
379 LevelSetMorphing<GridT, InterruptT>::
380 Morph<MapT, SpatialScheme, TemporalScheme>::
381 Morph(Morph& other, tbb::split)
382 : mParent(other.mParent)
383 , mMinAbsS(other.mMinAbsS)
384 , mMaxAbsS(other.mMaxAbsS)
390 template<
typename Gr
idT,
typename InterruptT>
396 advect(ValueType time0, ValueType time1)
398 namespace ph = std::placeholders;
404 while (time0 < time1 && mParent->mTracker.checkInterrupter()) {
405 mParent->mTracker.leafs().rebuildAuxBuffers(auxBuffers);
407 const ValueType dt = this->sampleSpeed(time0, time1, auxBuffers);
411 switch(TemporalScheme) {
415 mTask = std::bind(&Morph::euler01, ph::_1, ph::_2, dt, 2);
418 this->cook(PARALLEL_FOR, 1);
423 mTask = std::bind(&Morph::euler01, ph::_1, ph::_2, dt, 2);
426 this->cook(PARALLEL_FOR, 1);
430 mTask = std::bind(&Morph::euler12, ph::_1, ph::_2, dt);
433 this->cook(PARALLEL_FOR, 1);
438 mTask = std::bind(&Morph::euler01, ph::_1, ph::_2, dt, 3);
441 this->cook(PARALLEL_FOR, 1);
445 mTask = std::bind(&Morph::euler34, ph::_1, ph::_2, dt);
448 this->cook(PARALLEL_FOR, 2);
452 mTask = std::bind(&Morph::euler13, ph::_1, ph::_2, dt);
455 this->cook(PARALLEL_FOR, 2);
459 OPENVDB_THROW(ValueError,
"Temporal integration scheme not supported!");
465 mParent->mTracker.leafs().removeAuxBuffers();
468 mParent->mTracker.track();
474 template<
typename Gr
idT,
typename InterruptT>
477 inline typename GridT::ValueType
478 LevelSetMorphing<GridT, InterruptT>::
479 Morph<MapT, SpatialScheme, TemporalScheme>::
480 sampleSpeed(ValueType time0, ValueType time1,
Index speedBuffer)
482 namespace ph = std::placeholders;
485 const size_t leafCount = mParent->mTracker.leafs().leafCount();
486 if (leafCount==0 || time0 >= time1)
return ValueType(0);
488 const math::Transform& xform = mParent->mTracker.grid().transform();
489 if (mParent->mTarget->transform() == xform &&
490 (mParent->mMask ==
nullptr || mParent->mMask->transform() == xform)) {
491 mTask = std::bind(&Morph::sampleAlignedSpeed, ph::_1, ph::_2, speedBuffer);
493 mTask = std::bind(&Morph::sampleXformedSpeed, ph::_1, ph::_2, speedBuffer);
495 this->cook(PARALLEL_REDUCE);
497 static const ValueType CFL = (TemporalScheme ==
math::TVD_RK1 ? ValueType(0.3) :
498 TemporalScheme == math::
TVD_RK2 ? ValueType(0.9) :
499 ValueType(1.0))/math::
Sqrt(ValueType(3.0));
500 const ValueType dt =
math::Abs(time1 - time0), dx = mParent->mTracker.voxelSize();
501 return math::Min(dt, ValueType(CFL*dx/mMaxAbsS));
504 template<
typename Gr
idT,
typename InterruptT>
508 LevelSetMorphing<GridT, InterruptT>::
509 Morph<MapT, SpatialScheme, TemporalScheme>::
510 sampleXformedSpeed(
const LeafRange& range,
Index speedBuffer)
512 using VoxelIterT =
typename LeafType::ValueOnCIter;
513 using SamplerT = tools::GridSampler<typename GridT::ConstAccessor, tools::BoxSampler>;
515 const MapT& map = *mMap;
516 mParent->mTracker.checkInterrupter();
518 typename GridT::ConstAccessor targetAcc = mParent->mTarget->getAccessor();
519 SamplerT target(targetAcc, mParent->mTarget->transform());
520 if (mParent->mMask ==
nullptr) {
521 for (
typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
522 ValueType* speed = leafIter.buffer(speedBuffer).data();
524 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
525 ValueType& s = speed[voxelIter.pos()];
526 s -= target.wsSample(map.applyMap(voxelIter.getCoord().asVec3d()));
533 const ValueType
min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask);
534 const bool invMask = mParent->isMaskInverted();
535 typename GridT::ConstAccessor maskAcc = mParent->mMask->getAccessor();
536 SamplerT mask(maskAcc, mParent->mMask->transform());
537 for (
typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
538 ValueType* speed = leafIter.buffer(speedBuffer).data();
540 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
541 const Vec3R xyz = map.applyMap(voxelIter.getCoord().asVec3d());
543 ValueType& s = speed[voxelIter.pos()];
544 s -= target.wsSample(xyz);
545 s *= invMask ? 1 - a : a;
554 template<
typename Gr
idT,
typename InterruptT>
558 LevelSetMorphing<GridT, InterruptT>::
559 Morph<MapT, SpatialScheme, TemporalScheme>::
560 sampleAlignedSpeed(
const LeafRange& range,
Index speedBuffer)
562 using VoxelIterT =
typename LeafType::ValueOnCIter;
564 mParent->mTracker.checkInterrupter();
566 typename GridT::ConstAccessor target = mParent->mTarget->getAccessor();
568 if (mParent->mMask ==
nullptr) {
569 for (
typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
570 ValueType* speed = leafIter.buffer(speedBuffer).data();
572 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
573 ValueType& s = speed[voxelIter.pos()];
574 s -= target.getValue(voxelIter.getCoord());
581 const ValueType
min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask);
582 const bool invMask = mParent->isMaskInverted();
583 typename GridT::ConstAccessor mask = mParent->mMask->getAccessor();
584 for (
typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
585 ValueType* speed = leafIter.buffer(speedBuffer).data();
587 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
588 const Coord ijk = voxelIter.getCoord();
590 ValueType& s = speed[voxelIter.pos()];
591 s -= target.getValue(ijk);
592 s *= invMask ? 1 - a : a;
601 template<
typename Gr
idT,
typename InterruptT>
605 LevelSetMorphing<GridT, InterruptT>::
606 Morph<MapT, SpatialScheme, TemporalScheme>::
607 cook(ThreadingMode mode,
size_t swapBuffer)
609 mParent->mTracker.startInterrupter(
"Morphing level set");
611 const int grainSize = mParent->mTracker.getGrainSize();
612 const LeafRange range = mParent->mTracker.leafs().leafRange(grainSize);
614 if (mParent->mTracker.getGrainSize()==0) {
616 }
else if (mode == PARALLEL_FOR) {
617 tbb::parallel_for(range, *
this);
618 }
else if (mode == PARALLEL_REDUCE) {
619 tbb::parallel_reduce(range, *
this);
621 OPENVDB_THROW(ValueError,
"expected threading mode " <<
int(PARALLEL_FOR)
622 <<
" or " <<
int(PARALLEL_REDUCE) <<
", got " <<
int(mode));
625 mParent->mTracker.leafs().swapLeafBuffer(swapBuffer, grainSize == 0);
627 mParent->mTracker.endInterrupter();
630 template<
typename Gr
idT,
typename InterruptT>
633 template <
int Nominator,
int Denominator>
635 LevelSetMorphing<GridT,InterruptT>::
636 Morph<MapT, SpatialScheme, TemporalScheme>::
637 euler(
const LeafRange& range, ValueType dt,
640 using SchemeT = math::BIAS_SCHEME<SpatialScheme>;
641 using StencilT =
typename SchemeT::template ISStencil<GridType>::StencilType;
642 using VoxelIterT =
typename LeafType::ValueOnCIter;
643 using NumGrad = math::GradientNormSqrd<MapT, SpatialScheme>;
645 static const ValueType Alpha = ValueType(Nominator)/ValueType(Denominator);
646 static const ValueType Beta = ValueType(1) - Alpha;
648 mParent->mTracker.checkInterrupter();
649 const MapT& map = *mMap;
650 StencilT stencil(mParent->mTracker.grid());
652 for (
typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
653 const ValueType* speed = leafIter.buffer(speedBuffer).data();
655 const ValueType* phi = leafIter.buffer(phiBuffer).data();
656 ValueType* result = leafIter.buffer(resultBuffer).data();
657 for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
658 const Index n = voxelIter.pos();
660 stencil.moveTo(voxelIter);
661 const ValueType v = stencil.getValue() - dt * speed[n] * NumGrad::result(map, stencil);
662 result[n] = Nominator ? Alpha * phi[n] + Beta * v : v;
671 #endif // OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED
math::Vec3< Real > Vec3R
Definition: Types.h:79
Performs multi-threaded interface tracking of narrow band level sets. This is the building-block for ...
Index32 Index
Definition: Types.h:61
bool isApproxZero(const Type &x)
Return true if x is equal to zero to within the default floating-point comparison tolerance.
Definition: Math.h:320
float Sqrt(float x)
Return the square root of a floating-point value.
Definition: Math.h:715
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:109
bool isExactlyEqual(const T0 &a, const T1 &b)
Return true if a is exactly equal to b.
Definition: Math.h:395
const Type & Min(const Type &a, const Type &b)
Return the minimum of two values.
Definition: Math.h:610
Definition: FiniteDifference.h:197
Definition: FiniteDifference.h:198
Definition: FiniteDifference.h:262
Definition: FiniteDifference.h:193
TemporalIntegrationScheme
Temporal integration schemes.
Definition: FiniteDifference.h:261
Definition: FiniteDifference.h:196
Type SmoothUnitStep(Type x)
Return 0 if x < 0, 1 if x > 1 or else (3 − 2 x) x².
Definition: Math.h:256
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:136
bool isZero(const Type &x)
Return true if x is exactly equal to zero.
Definition: Math.h:308
Definition: FiniteDifference.h:265
Definition: Exceptions.h:40
const Type & Max(const Type &a, const Type &b)
Return the maximum of two values.
Definition: Math.h:549
bool isApproxEqual(const Type &a, const Type &b)
Return true if a is equal to b to within the default floating-point comparison tolerance.
Definition: Math.h:358
Definition: FiniteDifference.h:263
Definition: FiniteDifference.h:195
Coord Abs(const Coord &xyz)
Definition: Coord.h:513
Definition: Exceptions.h:92
BiasedGradientScheme
Biased Gradients are limited to non-centered differences.
Definition: FiniteDifference.h:192
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:188
Definition: FiniteDifference.h:194
Definition: FiniteDifference.h:264