Skip to content

Commit

Permalink
Update LevelSetTubes.h
Browse files Browse the repository at this point in the history
round cone -> tapered capsule

Signed-off-by: ghurstunither <[email protected]>
  • Loading branch information
ghurstunither committed Oct 12, 2024
1 parent 675657f commit d9d4db2
Showing 1 changed file with 64 additions and 54 deletions.
118 changes: 64 additions & 54 deletions openvdb/openvdb/tools/LevelSetTubes.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
///
/// @file LevelSetTubes.h
///
/// @brief Generate a narrow-band level set of a capsule, round cone, and tube complex.
/// @brief Generate a narrow-band level set of a capsule, tapered capsule, and tube complex.
///
/// @note By definition a level set has a fixed narrow band width
/// (the half width is defined by LEVEL_SET_HALF_WIDTH in Types.h),
Expand Down Expand Up @@ -71,13 +71,13 @@ createLevelSetCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius, float vo


/// @brief Return a grid of type @c GridType containing a narrow-band level set
/// representation of a round cone (tube with sphere caps and different radii at both ends,
/// representation of a tapered capsule (tube with sphere caps and different radii at both ends,
/// or equivalently the convex hull of two spheres with possibly different centers and radii).
///
/// @param pt1 First round cone endpoint in world units.
/// @param pt2 Second round cone endpoint in world units.
/// @param radius1 Radius of the round cone at @c pt1 in world units.
/// @param radius2 Radius of the round cone at @c pt2 in world units.
/// @param pt1 First tapered capsule endpoint in world units.
/// @param pt2 Second tapered capsule endpoint in world units.
/// @param radius1 Radius of the tapered capsule at @c pt1 in world units.
/// @param radius2 Radius of the tapered capsule at @c pt2 in world units.
/// @param voxelSize Voxel size in world units.
/// @param halfWidth Half the width of the narrow band, in voxel units.
/// @param interrupter Interrupter adhering to the util::NullInterrupter interface.
Expand All @@ -86,26 +86,26 @@ createLevelSetCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius, float vo
/// @note @c GridType::ValueType must be a floating-point scalar.
template <typename GridType, typename InterruptT>
typename GridType::Ptr
createLevelSetRoundCone(const Vec3s& pt1, const Vec3s& pt2, float radius1, float radius2,
createLevelSetTaperedCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius1, float radius2,
float voxelSize, float halfWidth = float(LEVEL_SET_HALF_WIDTH),
InterruptT* interrupter = nullptr, bool threaded = true);

/// @brief Return a grid of type @c GridType containing a narrow-band level set
/// representation of a round cone (tube with sphere caps and different radii at both ends,
/// representation of a tapered capsule (tube with sphere caps and different radii at both ends,
/// or equivalently the convex hull of two spheres with possibly different centers and radii).
///
/// @param pt1 First round cone endpoint in world units.
/// @param pt2 Second round cone endpoint in world units.
/// @param radius1 Radius of the round cone at @c pt1 in world units.
/// @param radius2 Radius of the round cone at @c pt2 in world units.
/// @param pt1 First tapered capsule endpoint in world units.
/// @param pt2 Second tapered capsule endpoint in world units.
/// @param radius1 Radius of the tapered capsule at @c pt1 in world units.
/// @param radius2 Radius of the tapered capsule at @c pt2 in world units.
/// @param voxelSize Voxel size in world units.
/// @param halfWidth Half the width of the narrow band, in voxel units.
/// @param threaded If true multi-threading is enabled (true by default).
///
/// @note @c GridType::ValueType must be a floating-point scalar.
template <typename GridType>
typename GridType::Ptr
createLevelSetRoundCone(const Vec3s& pt1, const Vec3s& pt2, float radius1, float radius2,
createLevelSetTaperedCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius1, float radius2,
float voxelSize, float halfWidth = float(LEVEL_SET_HALF_WIDTH), bool threaded = true);

/// @brief Different policies when creating a tube complex with varying radii
Expand All @@ -114,7 +114,7 @@ createLevelSetRoundCone(const Vec3s& pt1, const Vec3s& pt2, float radius1, float
/// <dt><b>TUBE_VERTEX_RADII</b>
/// <dd>Specify that the tube complex radii are per-vertex,
/// meaning each tube has different radii at its two endpoints
/// and the complex is a collection of round cones.
/// and the complex is a collection of tapered capsules.
///
/// <dt><b>TUBE_SEGMENT_RADII</b>
/// <dd>Specify that the tube complex radii are per-segment,
Expand Down Expand Up @@ -576,14 +576,14 @@ class CapsuleVoxelizer


/// @brief Class used to generate a grid of type @c GridType containing a narrow-band level set
/// representation of a round cone.
/// representation of a tapered capsule.
///
/// @note @c GridType::ValueType must be a floating-point scalar.
template <typename GridType, typename InterruptT = util::NullInterrupter>
class RoundConeVoxelizer
class TaperedCapsuleVoxelizer
: public ConvexVoxelizer<
GridType,
RoundConeVoxelizer<GridType, InterruptT>,
TaperedCapsuleVoxelizer<GridType, InterruptT>,
InterruptT>
{
using GridPtr = typename GridType::Ptr;
Expand All @@ -593,7 +593,7 @@ class RoundConeVoxelizer

using BaseT = ConvexVoxelizer<
GridType,
RoundConeVoxelizer<GridType, InterruptT>,
TaperedCapsuleVoxelizer<GridType, InterruptT>,
InterruptT
>;

Expand All @@ -604,7 +604,7 @@ class RoundConeVoxelizer

friend class ConvexVoxelizer<
GridType,
RoundConeVoxelizer<GridType, InterruptT>,
TaperedCapsuleVoxelizer<GridType, InterruptT>,
InterruptT
>;

Expand All @@ -617,18 +617,18 @@ class RoundConeVoxelizer
///
/// @note The voxel size and half width are determined from the input grid,
/// meaning the voxel size and background value need to be set prior to voxelization
RoundConeVoxelizer(GridPtr& grid, const bool& threaded = true,
TaperedCapsuleVoxelizer(GridPtr& grid, const bool& threaded = true,
InterruptT* interrupter = nullptr)
: BaseT(grid, threaded, interrupter)
{
}

/// @brief Create a round cone
/// @brief Create a tapered capsule
///
/// @param pt1 first endpoint of the round cone in world units
/// @param pt2 second endpoint of the round cone in world units
/// @param radius1 radius of the round cone at @c pt1 in world units
/// @param radius2 radius of the round cone at @c pt2 in world units
/// @param pt1 first endpoint of the tapered capsule in world units
/// @param pt2 second endpoint of the tapered capsule in world units
/// @param radius1 radius of the tapered capsule at @c pt1 in world units
/// @param radius2 radius of the tapered capsule at @c pt2 in world units
void
operator()(const Vec3s& pt1, const Vec3s& pt2, const float& radius1, const float& radius2)
{
Expand All @@ -637,13 +637,13 @@ class RoundConeVoxelizer
// ball
if ((pt1 - pt2).lengthSqr() <= math::Pow2(radius1 - radius2)) {
OPENVDB_THROW(RuntimeError,
"The round cone is degenerate, in this case it is a ball. Consider using the CapsuleVoxelizer class instead.");
"The tapered capsule is degenerate, in this case it is a ball. Consider using the CapsuleVoxelizer class instead.");
}

// tube
if (math::Abs(radius1 - radius2) < 0.001f*BaseT::voxelSize()) {
OPENVDB_THROW(RuntimeError,
"The round cone is degenerate, in this case it is a capsule. Consider using the CapsuleVoxelizer class instead.");
"The tapered capsule is degenerate, in this case it is a capsule. Consider using the CapsuleVoxelizer class instead.");
}

initialize(pt1, pt2, radius1, radius2);
Expand All @@ -653,7 +653,8 @@ class RoundConeVoxelizer

private:

inline void setXYRangeData(const Index& step = 1) override
inline void
setXYRangeData(const Index& step = 1) override
{
// short circuit when one circle is in the other
if (mXYNorm2 <= mRdiff2) {
Expand Down Expand Up @@ -690,7 +691,8 @@ class RoundConeVoxelizer
}

// https://en.wikipedia.org/wiki/Belt_problem#Pulley_problem
inline bool pullyPoints(Vec2s& p1t, Vec2s& p2t, Vec2s& p1b, Vec2s& p2b) const
inline bool
pullyPoints(Vec2s& p1t, Vec2s& p2t, Vec2s& p1b, Vec2s& p2b) const
{
const float diff = mXYNorm2 - mRdiff2;
if (diff < 0.0f)
Expand All @@ -710,7 +712,8 @@ class RoundConeVoxelizer
return true;
}

inline void setLineXYData(const Vec2s& q1, const Vec2s& q2, const float& step)
inline void
setLineXYData(const Vec2s& q1, const Vec2s& q2, const float& step)
{
if (math::Abs(q1.x() - q2.x()) < math::Tolerance<float>::value()) {
float x = tileCeil(q1.x(), step);
Expand All @@ -734,7 +737,8 @@ class RoundConeVoxelizer
}
}

inline void setCircleXYData(const Vec2s& q1, const Vec2s& q2,
inline void
setCircleXYData(const Vec2s& q1, const Vec2s& q2,
const float& step, const bool is_pt1)
{
const Vec3s &p1 = is_pt1 ? mPt1 : mPt2;
Expand All @@ -753,7 +757,8 @@ class RoundConeVoxelizer
}
}

inline void setCircleHiXYData(const float& x1, const float& x2,
inline void
setCircleHiXYData(const float& x1, const float& x2,
const float& step, const bool& is_pt1)
{
const float x_test = static_cast<float>(math::Floor(0.5f*(x1+x2)));
Expand All @@ -772,7 +777,8 @@ class RoundConeVoxelizer
}
}

inline void setCircleLoXYData(const float& x1, const float& x2,
inline void
setCircleLoXYData(const float& x1, const float& x2,
const float& step, const bool& is_pt1)
{
const float x_test = static_cast<float>(math::Floor(0.5f*(x1+x2)));
Expand All @@ -793,7 +799,8 @@ class RoundConeVoxelizer

// Round Cone: https://iquilezles.org/articles/distfunctions/
// distance in index space
inline ValueT signedDistance(const Vec3s& p) const
inline float
signedDistance(const Vec3s& p) const
{
const Vec3s w = p - mPt1;
const ValueT y = w.dot(mV),
Expand All @@ -812,12 +819,13 @@ class RoundConeVoxelizer
return (math::Sqrt(x2*mA2*mInvVLenSqr) + y*mRdiff)*mInvVLenSqr - mRad1;
}

inline bool tileCanFit(const Index& dim) const override
inline bool
tileCanFit(const Index& dim) const override
{
return math::Max(mRad1, mRad2) >= BaseT::halfWidth() + 0.70711f * (dim-1u);
}

std::function<bool(float&, float&, const float&, const float&)> roundConeBottomTop =
std::function<bool(float&, float&, const float&, const float&)> TaperedCapsuleBottomTop =
[this](float& zb, float& zt, const float& x, const float& y)
{
const Vec2s q(x, y);
Expand Down Expand Up @@ -892,7 +900,8 @@ class RoundConeVoxelizer
};

// https://www.geometrictools.com/Documentation/IntersectionLineCone.pdf
inline void openConeFrustumBottomTop(float& conezb, float& conezt, int& cint_cnt,
inline void
openConeFrustumBottomTop(float& conezb, float& conezt, int& cint_cnt,
const float& x, const float& y) const
{
cint_cnt = 0;
Expand Down Expand Up @@ -935,7 +944,8 @@ class RoundConeVoxelizer
// ignore the c2 == c1 == 0 case, where the ray is on the boundary of the cone
}

inline bool validFrustumRange(const double& t, const double& ddotdiff) const
inline bool
validFrustumRange(const double& t, const double& ddotdiff) const
{
const double h = ddotdiff - t * mConeD.z();

Expand Down Expand Up @@ -1059,12 +1069,12 @@ class RoundConeVoxelizer
mC2 = math::Pow2(mConeD.z()) - mGamma;
mC2Inv = mC2 != 0.0 ? 1.0/mC2 : 1.0;

BaseT::bottomTop = roundConeBottomTop;
BaseT::bottomTop = TaperedCapsuleBottomTop;
}

// ------------ private members ------------

// round cone data -- populated via initialize()
// tapered capsule data -- populated via initialize()

Vec3s mPt1, mPt2, mV;

Expand All @@ -1079,14 +1089,14 @@ class RoundConeVoxelizer

float mX1, mY1, mZ1, mX2, mY2, mZ2;

}; // class RoundConeVoxelizer
}; // class TaperedCapsuleVoxelizer


/// @brief Class used to generate a grid of type @c GridType containing a narrow-band level set
/// representation of a tube complex.
///
/// @note @c GridType::ValueType must be a floating-point scalar.
/// @note Setting @c PerSegmentRadii to @c true gives a complex of capsules and a complex of round cones otherwise.
/// @note Setting @c PerSegmentRadii to @c true gives a complex of capsules and a complex of tapered capsules otherwise.
template <typename GridType, typename InterruptT = util::NullInterrupter, bool PerSegmentRadii = true>
class TubeComplexVoxelizer {

Expand Down Expand Up @@ -1246,7 +1256,7 @@ class TubeComplexVoxelizer {
inline void
perVertexRadiusVoxelize(const tbb::blocked_range<size_t>& rng)
{
RoundConeVoxelizer<GridType, InterruptT> rc_voxelizer(mGrid, false);
TaperedCapsuleVoxelizer<GridType, InterruptT> rc_voxelizer(mGrid, false);

CapsuleVoxelizer<GridType, InterruptT> c_voxelizer(mGrid, false);

Expand Down Expand Up @@ -1390,7 +1400,7 @@ createLevelSetTubeComplex(const std::vector<Vec3s>& vertices, const std::vector<
using ValueT = typename GridType::ValueType;

using CapsuleComplexVoxelizer = typename lvlset::TubeComplexVoxelizer<GridType, InterruptT, true>;
using RoundConeComplexVoxelizer = typename lvlset::TubeComplexVoxelizer<GridType, InterruptT, false>;
using TaperedCapsuleComplexVoxelizer = typename lvlset::TubeComplexVoxelizer<GridType, InterruptT, false>;

static_assert(std::is_floating_point<ValueT>::value,
"createLevelSetTubeComplex must return a scalar grid");
Expand Down Expand Up @@ -1425,7 +1435,7 @@ createLevelSetTubeComplex(const std::vector<Vec3s>& vertices, const std::vector<
GridPtr tubegrid;

if (vertices.size() == radii.size()) {
RoundConeComplexVoxelizer op(vertices, segments, radii,
TaperedCapsuleComplexVoxelizer op(vertices, segments, radii,
voxelSize, background, interrupter);

const tbb::blocked_range<size_t> segmentRange(0, op.bucketSize());
Expand Down Expand Up @@ -1489,21 +1499,21 @@ createLevelSetCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius, float vo
}


// ------------ createLevelSetRoundCone ------------- //
// ------------ createLevelSetTaperedCapsule ------------- //

template <typename GridType, typename InterruptT>
typename GridType::Ptr
createLevelSetRoundCone(const Vec3s& pt1, const Vec3s& pt2, float radius1, float radius2,
createLevelSetTaperedCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius1, float radius2,
float voxelSize, float halfWidth, InterruptT* interrupter, bool threaded)
{
using GridPtr = typename GridType::Ptr;
using ValueT = typename GridType::ValueType;

using CapsuleVoxelizer = typename lvlset::CapsuleVoxelizer<GridType, InterruptT>;
using RoundConeVoxelizer = typename lvlset::RoundConeVoxelizer<GridType, InterruptT>;
using TaperedCapsuleVoxelizer = typename lvlset::TaperedCapsuleVoxelizer<GridType, InterruptT>;

static_assert(std::is_floating_point<ValueT>::value,
"createLevelSetRoundCone must return a scalar grid");
"createLevelSetTaperedCapsule must return a scalar grid");

if (voxelSize <= 0) OPENVDB_THROW(ValueError, "voxel size must be positive");
if (halfWidth <= 0) OPENVDB_THROW(ValueError, "half-width must be positive");
Expand All @@ -1528,9 +1538,9 @@ createLevelSetRoundCone(const Vec3s& pt1, const Vec3s& pt2, float radius1, float
CapsuleVoxelizer voxelizer(grid, threaded, interrupter);
voxelizer(pt1, pt2, radius1);

} else { // round cone
} else { // tapered capsule

RoundConeVoxelizer voxelizer(grid, threaded, interrupter);
TaperedCapsuleVoxelizer voxelizer(grid, threaded, interrupter);
voxelizer(pt1, pt2, radius1, radius2);
}

Expand All @@ -1539,10 +1549,10 @@ createLevelSetRoundCone(const Vec3s& pt1, const Vec3s& pt2, float radius1, float

template <typename GridType>
typename GridType::Ptr
createLevelSetRoundCone(const Vec3s& pt1, const Vec3s& pt2, float radius1, float radius2,
createLevelSetTaperedCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius1, float radius2,
float voxelSize, float halfWidth, bool threaded)
{
return createLevelSetRoundCone<GridType, util::NullInterrupter>(
return createLevelSetTaperedCapsule<GridType, util::NullInterrupter>(
pt1, pt2, radius1, radius2, voxelSize, halfWidth, nullptr, threaded);
}

Expand Down Expand Up @@ -1578,7 +1588,7 @@ OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
#undef _FUNCTION

#define _FUNCTION(TreeT) \
Grid<TreeT>::Ptr createLevelSetRoundCone<Grid<TreeT>>(const Vec3s&, const Vec3s&, \
Grid<TreeT>::Ptr createLevelSetTaperedCapsule<Grid<TreeT>>(const Vec3s&, const Vec3s&, \
float, float, float, float, util::NullInterrupter*, bool)
OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
#undef _FUNCTION
Expand Down

0 comments on commit d9d4db2

Please sign in to comment.