Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tube and Dilated Mesh Level Set Constructors #1935

Open
wants to merge 31 commits into
base: master
Choose a base branch
from

Conversation

ghurstunither
Copy link
Contributor

@ghurstunither ghurstunither commented Oct 13, 2024

Summary

This PR introduces level set constructors for

  • capsules
  • tapered capsules (different radius at the endpoints)
  • tube complex with constant or varying radius
  • dilated triangle, quad, or mixed mesh (dilated in all directions)

A base class ConvexVoxelizer, which provides the infrastructure for convex level set construction, is also introduced.


class tools::ConvexVoxelizer

tools/ConvexVoxelizer.h

This base class requires the derived class implement methods such as

A simple example that creates a level set sphere can be found in the doxygen here.

Click me to see the same example here
template <typename GridType>
class SphereVoxelizer : public ConvexVoxelizer<GridType, SphereVoxelizer<GridType>>
{
    using GridPtr = typename GridType::Ptr;

    using BaseT = ConvexVoxelizer<GridType, SphereVoxelizer<GridType>>;
    using BaseT::mXYData;
    using BaseT::tileCeil;

    using ValueT = typename BaseT::ValueT;
    using Vec3T  = typename BaseT::Vec3T;

public:

    friend class ConvexVoxelizer<GridType, SphereVoxelizer<GridType>>;

    SphereVoxelizer(GridPtr& grid, const bool& threaded = true)
    : BaseT(grid, threaded)
    {
    }

    template <typename ScalarT>
    void
    operator()(const math::Vec3<ScalarT>& pt, const ScalarT& r)
    {
        static_assert(std::is_floating_point<ScalarT>::value);

        if (r <= 0)
            return;

        initialize<ScalarT>(pt, r);

        BaseT::iterate();
    }

private:

    inline ValueT
    signedDistance(const Vec3T& p) const
    {
        return (p - mPt).length() - mRad;
    }

    inline void
    setXYRangeData(const Index& step = 1)
    {
        mXYData.reset(mX - mORad, mX + mORad, step);

        for (ValueT x = tileCeil(mX - mORad, step); x <= mX + mORad; x += step)
            mXYData.expandYRange(x, BaseT::circleBottom(mX, mY, mORad, x),
                BaseT::circleTop(mX, mY, mORad, x));
    }

    std::function<bool(ValueT&, ValueT&, const ValueT&, const ValueT&)> sphereBottomTop =
    [this](ValueT& zb, ValueT& zt, const ValueT& x, const ValueT& y)
    {
        zb = BaseT::sphereBottom(mX, mY, mZ, mORad, x, y);
        zt = BaseT::sphereTop(mX, mY, mZ, mORad, x, y);

        return std::isfinite(zb) && std::isfinite(zt);
    };

    template <typename ScalarT>
    inline void
    initialize(const math::Vec3<ScalarT>& pt, const ScalarT& r)
    {
        const ValueT vx = BaseT::voxelSize(),
                     hw = BaseT::halfWidth();

        // sphere data in index space
        mPt = Vec3T(pt)/vx;
        mRad = ValueT(r)/vx;

        mX = mPt.x(); mY = mPt.y(); mZ = mPt.z();

        // padded radius used to populate the outer halfwidth of the sdf
        mORad  = mRad + hw;

        BaseT::bottomTop = sphereBottomTop;
    }

    Vec3T mPt;
    ValueT mRad, mORad, mX, mY, mZ;
};

// usage:

// initialize level set grid with voxel size 0.1 and half width 3.0
FloatGrid::Ptr grid = createLevelSet<GridT>(0.1f, 3.0f);

// populate grid with a sphere centered at (0, 1, 2) and radius 5
SphereVoxelizer<FloatGrid> op(grid);
op(Vec3s(0.0f, 1.0f, 2.0f), 5.0f);

tools::createLevelSetCapsule

Implemented in tools/LevelSetTubes.h via class CapsuleVoxelizer:

template <typename GridType, typename InterruptT>
typename GridType::Ptr
createLevelSetCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius, float voxelSize,
    float halfWidth = float(LEVEL_SET_HALF_WIDTH),
    InterruptT* interrupter = nullptr, bool threaded = true);

and

template <typename GridType>
typename GridType::Ptr
createLevelSetCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius, float voxelSize,
    float halfWidth = float(LEVEL_SET_HALF_WIDTH), bool threaded = true);

Example:

const Vec3s p1(15.8f, 13.2f, 16.7f), p2(4.3f, 7.9f, -4.8f);
const float r1 = 4.3f, voxelSize = 0.1f;

FloatGrid::Ptr grid = tools::createLevelSetCapsule<FloatGrid>(p1, p2, r1, voxelSize);

yielding

Screenshot 2024-10-12 at 11 14 06 PM

tools::createLevelSetTaperedCapsule

Implemented in tools/LevelSetTubes.h via class TaperedCapsuleVoxelizer:

template <typename GridType, typename InterruptT>
typename GridType::Ptr
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);

and

template <typename GridType>
typename GridType::Ptr
createLevelSetTaperedCapsule(const Vec3s& pt1, const Vec3s& pt2, float radius1, float radius2,
    float voxelSize, float halfWidth = float(LEVEL_SET_HALF_WIDTH), bool threaded = true);

Example:

const Vec3s p1(15.8f, 13.2f, 16.7f), p2(4.3f, 7.9f, -4.8f);
const float r1 = 4.3f, r2 = 1.2f, voxelSize = 0.1f;

FloatGrid::Ptr grid = tools::createLevelSetTaperedCapsule<FloatGrid>(p1, p2, r1, r2, voxelSize);

yielding

Screenshot 2024-10-12 at 11 16 30 PM

tools::createLevelSetTubeComplex

Implemented in tools/LevelSetTubes.h via class TubeComplexVoxelizer:

template <typename GridType, typename InterruptT = util::NullInterrupter>
typename GridType::Ptr
createLevelSetTubeComplex(const std::vector<Vec3s>& vertices, const std::vector<Vec2I>& segments,
    float radius, float voxelSize, float halfWidth = float(LEVEL_SET_HALF_WIDTH),
    InterruptT* interrupter = nullptr);

and

template <typename GridType, typename InterruptT = util::NullInterrupter>
typename GridType::Ptr
createLevelSetTubeComplex(const std::vector<Vec3s>& vertices, const std::vector<Vec2I>& segments,
    const std::vector<float>& radii, float voxelSize,
    float halfWidth = float(LEVEL_SET_HALF_WIDTH), TubeRadiiPolicy radii_policy = TUBE_AUTOMATIC,
    InterruptT* interrupter = nullptr);

The different TubeRadiiPolicy are described here.

Example of constant radius:

const Vec3s p0(0.0f, 0.0f, 0.0f), p1(0.0f, 0.0f, 1.0f),
            p2(-0.471405f, -0.816497f, -0.333333f), p3(-0.471405f, 0.816497f, -0.333333f),
            p4(0.942809f, 0.0f, -0.333333f);
const float r = 0.2f, voxelSize = 0.0125f;

const std::vector<Vec3s> vertices({p0, p1, p2, p3, p4});
const std::vector<Vec2I> segments({Vec2I(0, 1), Vec2I(0, 2), Vec2I(0, 3), Vec2I(0, 4)});

FloatGrid::Ptr grid = tools::createLevelSetTubeComplex<FloatGrid>(vertices, segments, radii, voxelSize);

yielding

Screenshot 2024-10-12 at 11 26 10 PM

Example of varying radius:

const Vec3s p0(0.0f, 0.0f, 0.0f), p1(0.0f, 0.0f, 1.0f),
            p2(-0.471405f, -0.816497f, -0.333333f), p3(-0.471405f, 0.816497f, -0.333333f),
            p4(0.942809f, 0.0f, -0.333333f);
const std::vector<float> radii({0.15f, 0.15f, 0.1f, 0.05f, 0.0f});
const float voxelSize = 0.0125f;

const std::vector<Vec3s> vertices({p0, p1, p2, p3, p4});
const std::vector<Vec2I> segments({Vec2I(0, 1), Vec2I(0, 2), Vec2I(0, 3), Vec2I(0, 4)});

FloatGrid::Ptr grid = tools::createLevelSetTubeComplex<FloatGrid>(vertices, segments, r, voxelSize);

yielding

Screenshot 2024-10-12 at 11 32 50 PM

tools::createLevelSetDilatedMesh

Implemented in tools/LevelSetDilatedMesh.h via class DilatedMeshVoxelizer:

template <typename GridType, typename InterruptT = util::NullInterrupter>
typename GridType::Ptr
createLevelSetDilatedMesh(
    const std::vector<Vec3s>& vertices, const std::vector<Vec3I>& triangles,
    float radius, float voxelSize, float halfWidth = float(LEVEL_SET_HALF_WIDTH),
    InterruptT* interrupter = nullptr);

and

template <typename GridType, typename InterruptT = util::NullInterrupter>
typename GridType::Ptr
createLevelSetDilatedMesh(
    const std::vector<Vec3s>& vertices, const std::vector<Vec4I>& quads,
    float radius, float voxelSize, float halfWidth = float(LEVEL_SET_HALF_WIDTH),
    InterruptT* interrupter = nullptr);

and

template <typename GridType, typename InterruptT = util::NullInterrupter>
typename GridType::Ptr
createLevelSetDilatedMesh(const std::vector<Vec3s>& vertices,
    const std::vector<Vec3I>& triangles, const std::vector<Vec4I>& quads,
    float radius, float voxelSize, float halfWidth = float(LEVEL_SET_HALF_WIDTH),
    InterruptT* interrupter = nullptr);

Initialize mesh data:

const float r = 2.9f;
const Vec3s p0(15.8f, 13.2f, 16.7f),  p1(4.3f, 7.9f, -4.8f);
const Vec3s p2(-3.0f, -7.4f, 8.9f),   p3(-2.7f, 8.9f, 30.4f);
const Vec3s p4(23.0f, 17.4f, -10.9f), p5(5.2f, -5.7f, 29.0f);
const Vec3s p6(-14.6f, 3.7f, 10.9f),  p7(35.8f, 23.4f, 5.8f);

const std::vector<Vec3s> vertices({p0, p1, p2, p3, p4, p5, p6, p7});
const std::vector<Vec3I> triangles1({Vec3I(0, 1, 2), Vec3I(0, 1, 3), Vec3I(0, 1, 4)});
const std::vector<Vec3I> triangles2({Vec3I(0, 1, 4)});
const std::vector<Vec4I> quads1({Vec4I(0, 1, 2, 5), Vec4I(0, 1, 6, 3), Vec4I(0, 1, 4, 7)});
const std::vector<Vec4I> quads2({Vec4I(0, 1, 2, 5), Vec4I(0, 1, 6, 3)});

Example of triangle mesh:

FloatGrid::Ptr grid = tools:: createLevelSetDilatedMesh <FloatGrid>(vertices, triangles1, r, voxelSize);

yielding

Screenshot 2024-10-12 at 11 41 43 PM

Example of quad mesh:

FloatGrid::Ptr grid = tools:: createLevelSetDilatedMesh <FloatGrid>(vertices, quads1, r, voxelSize);

yielding

Screenshot 2024-10-12 at 11 42 28 PM

Example of mixed mesh:

FloatGrid::Ptr grid = tools:: createLevelSetDilatedMesh <FloatGrid>(vertices, triangles2, quads2, r, voxelSize);

yielding

Screenshot 2024-10-12 at 11 43 42 PM

* Base class ConvexVoxelizer which provides the groundwork to create a level set of a convex region
* Level set constructor methods:

createLevelSetCapsule,
createLevelSetTaperedCapsule,
createLevelSetTubeComplex,
createLevelSetThickenedMesh

Signed-off-by: ghurstunither <[email protected]>
Addresses CI Linux compilation warnings

Signed-off-by: ghurstunither <[email protected]>
Addresses CI Linux compilation warnings

Signed-off-by: ghurstunither <[email protected]>
Addresses CI Linux compilation warnings

Signed-off-by: ghurstunither <[email protected]>
Addresses CI Linux compilation warnings

Signed-off-by: ghurstunither <[email protected]>
Addresses CI Linux compilation warnings

Signed-off-by: ghurstunither <[email protected]>
Addresses CI Linux compilation warnings

Signed-off-by: ghurstunither <[email protected]>
Adhere to OpenVDB style guideline "The return type in a function definition should go on a line by itself."

Signed-off-by: ghurstunither <[email protected]>
Fixes some typos

Signed-off-by: ghurstunither <[email protected]>
Correct SPDX-License-Identifier

Signed-off-by: ghurstunither <[email protected]>
Changes createLevelSetThickenedMesh to createLevelSetDilatedMesh.

Signed-off-by: ghurstunither <[email protected]>
Copy link

linux-foundation-easycla bot commented Oct 23, 2024

CLA Signed

The committers listed above are authorized under a signed CLA.

@ghurstunither ghurstunither changed the title Tube and Thickened Mesh Level Set Constructors Tube and Dilated Mesh Level Set Constructors Oct 23, 2024
Adds the new level set constructors to pendingchanges.

Signed-off-by: ghurstunither <[email protected]>
setXYRangeData and tileCanFit are no longer virtual, making ConvexVoxelizer fully embrace CRTP.

Signed-off-by: ghurstunither <[email protected]>
ConvexVoxelizer and derived classes now work with the grid's ValueType precision instead of float.

The createLevelSetXXX constructors now are templated on a scalar type for the precision of the input data.

Signed-off-by: ghurstunither <[email protected]>
Store reference to grid in ConvexVoxelizer instead of the tree.

Signed-off-by: ghurstunither <[email protected]>
Adds test file for testing tubes.

* to test capsule and tapered capsule area, volume, gauss&mean curvature analytically derived formulas

* tests for capsules

Signed-off-by: ghurstunither <[email protected]>
Tests for tapered capsules.

Signed-off-by: ghurstunither <[email protected]>
Tests for tube complex.

Signed-off-by: ghurstunither <[email protected]>
Fix some linux build failures.

Signed-off-by: ghurstunither <[email protected]>
Adds tests for dilated mesh.

Signed-off-by: ghurstunither <[email protected]>
Correct type in test.

Signed-off-by: ghurstunither <[email protected]>
Removes unnecessary includes.

Signed-off-by: ghurstunither <[email protected]>
@ghurstunither
Copy link
Contributor Author

@danrbailey I believe I have made every change you suggested a couple meetings ago.

  • The ConvexVoxelizer class now performs arithmetic with type ValueT instead of float
  • Store reference to grid instead of the tree
  • No more virtual methods in ConvexVoxelizer
  • Expanded the unit tests and moved them out of TestTools.cc

Do you think it makes sense to move the content in ConvexVoxelizer.h into LevelSetUtil.h, changing the class tools::ConvexVoxelizer to tools::level_set_util_internal::ConvexVoxelizer? After all this is a level set utility, but it would make LevelSetUtil.h much larger.

Adds more dilated mesh tests for more degenerate cases and different types of triangle 'defects'.

Signed-off-by: ghurstunither <[email protected]>
Fixes incorrect type for faces.

Signed-off-by: ghurstunither <[email protected]>
Split dilated mesh tests into multiple fixtures.

Signed-off-by: ghurstunither <[email protected]>
Handle negative radii & empty data containers more carefully.

Signed-off-by: ghurstunither <[email protected]>
Trailing Spaces.

Signed-off-by: ghurstunither <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants