Skip to content

Commit

Permalink
Add a unit-test for LevelSetFilter::fillet.
Browse files Browse the repository at this point in the history
Signed-off-by: apradhana <[email protected]>
  • Loading branch information
apradhana committed Oct 28, 2024
1 parent 1d5d2a6 commit a747b3f
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 9 deletions.
22 changes: 13 additions & 9 deletions openvdb/openvdb/tools/LevelSetFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,15 @@ class LevelSetFilter : public LevelSetTracker<GridT, InterruptT>
/// @brief One iteration of filleting on the level set.
/// @param mask Optional alpha mask.
///
/// @note This filter rounds off concave edges to create a smoother transition between surfaces
/// Fillets a level set by
/// offsetting at locations with a negative principal curvature, proportional to its magnitude
/// leaves locations with non-negative principal curvatures untouched
/// This filter converges to the convex hull if iterated enough times
/// @note This filter rounds off concave edges to create a smoother
/// transition between surfaces. Fillets a level set by offsetting
/// at locations with a negative principal curvature, proportional
/// to its magnitude leaves locations with non-negative principal
/// curvatures untouched. This filter converges to the convex hull
/// if iterated enough times.
///
/// @details See also Level Set Methods and Fast Marching Methods
/// by James Sethian, pp. 204.
void fillet(const MaskType* mask = nullptr)
{
Filter f(this, mask); f.fillet();
Expand Down Expand Up @@ -409,10 +413,10 @@ LevelSetFilter<GridT, MaskT, InterruptT>::Filter::meanCurvatureImpl(const LeafRa
}
}

/// Fillets a level set by
/// offsetting at locations with a negative principal curvature, proportional to its magnitude
/// leaves locations with non-negative principal curvatures untouched
/// This filter converges to the convex hull if iterated enough times
/// Fillets a level set by offsetting at locations with a negative principal
/// curvature, proportional to its magnitude. Leaves locations with non-negative
/// principal curvatures untouched. This filter converges to the convex hull if
/// iterated enough times.
template<typename GridT, typename MaskT, typename InterruptT>
inline void
LevelSetFilter<GridT, MaskT, InterruptT>::Filter::filletImpl(const LeafRange& range)
Expand Down
1 change: 1 addition & 0 deletions openvdb/openvdb/unittest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ else()
TestLeafManager.cc
TestLeafMask.cc
TestLeafOrigin.cc
TestLevelSetFilter.cc
TestLevelSetRayIntersector.cc
TestLevelSetUtil.cc
TestLinearInterp.cc
Expand Down
99 changes: 99 additions & 0 deletions openvdb/openvdb/unittest/TestLevelSetFilter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: Apache-2.0

#include <openvdb/Types.h>
#include <openvdb/openvdb.h>
#include <openvdb/tools/LevelSetFilter.h>
#include <openvdb/tools/LevelSetSphere.h>
#include <openvdb/io/File.h>
#include <openvdb/tools/Composite.h> // for csgUnion()

#include <gtest/gtest.h>

#include <sstream>
#include <iostream>
#include <utility>

using namespace openvdb;

class TestLevelSetFilter: public ::testing::Test
{
public:
void SetUp() override { openvdb::initialize(); }
void TearDown() override { openvdb::uninitialize(); }

void testLevelSetFillet();
}; // class TestLevelSetFilter


////////////////////////////////////////


TEST_F(TestLevelSetFilter, testLevelSetFillet)
{
using GridT = FloatGrid;
using FilterT = tools::LevelSetFilter<GridT>;

const float radius = 5.0f;
const float voxelSize = 1.0f;
const float halfWidth = 3.0f;
typename GridT::Ptr sdfGrid = tools::createLevelSetSphere<GridT>(/*radius=*/radius,
/*center=*/Vec3f(-radius, 0.0f, 0.0f),
/*dx=*/voxelSize, /*halfWidth*/ halfWidth);
typename GridT::Ptr sdfGridB = tools::createLevelSetSphere<GridT>(/*radius=*/radius,
/*center=*/Vec3f(radius, 0.0f, 0.0f),
/*dx=*/voxelSize, /*halfWidth*/ halfWidth);
typename GridT::Accessor acc = sdfGrid->getAccessor();

EXPECT_TRUE(sdfGrid);
EXPECT_TRUE(sdfGridB);

tools::csgUnion(*sdfGrid, *sdfGridB);

{
io::File file_before("mygrids_before.vdb");
GridPtrVec grids_before;
grids_before.push_back(sdfGrid);
// Write out the contents of the container.
file_before.write(grids_before);
file_before.close();

EXPECT_TRUE(sdfGrid);

Coord ijk(0, 3, 0);

// We expect that the intersection between the two spheres are at (0, 0, 0)
// so the SDF value of the union in these offsets locations should be > 0
EXPECT_TRUE(acc.getValue(ijk) > 0.f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0, 0, 1)) > 0.0f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0, 0,-1)) > 0.0f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1, 0)) > 0.0f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1, 1)) > 0.0f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1,-1)) > 0.0f);
}

FilterT filter(*sdfGrid);
filter.fillet();

{
io::File file("mygrids.vdb");
GridPtrVec grids;
grids.push_back(sdfGrid);
// Write out the contents of the container.
file.write(grids);
file.close();

EXPECT_TRUE(sdfGrid);

Coord ijk(0, 3, 0);

// After the fillet operation, we expect that the zero-isocontour is
// pushed outward.
EXPECT_TRUE(acc.getValue(ijk) < 0.f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0, 0, 1)) < 0.0f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0, 0,-1)) < 0.0f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1, 0)) < 0.0f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1, 1)) < 0.0f);
EXPECT_TRUE(acc.getValue(ijk.offsetBy(0,-1,-1)) < 0.0f);
}
}
2 changes: 2 additions & 0 deletions pendingchanges/levelset_fillet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
New Feature:
- Added fillet() method in tools::LevelSetFilter to round off concave edges to create smoother transition between surfaces. [Contributed by Greg Hurst]

0 comments on commit a747b3f

Please sign in to comment.