From da8b764aa92b647361491589bbaf06e21e1f4586 Mon Sep 17 00:00:00 2001 From: apradhana Date: Mon, 28 Oct 2024 09:18:24 -0700 Subject: [PATCH] Add a unit test for LevelSetFilter::fillet. Signed-off-by: apradhana --- openvdb/openvdb/tools/LevelSetFilter.h | 22 ++--- openvdb/openvdb/unittest/CMakeLists.txt | 1 + .../openvdb/unittest/TestLevelSetFilter.cc | 80 +++++++++++++++++++ pendingchanges/levelset_fillet.txt | 2 + 4 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 openvdb/openvdb/unittest/TestLevelSetFilter.cc create mode 100644 pendingchanges/levelset_fillet.txt diff --git a/openvdb/openvdb/tools/LevelSetFilter.h b/openvdb/openvdb/tools/LevelSetFilter.h index 0ce7dcd6f7..bc1a425815 100644 --- a/openvdb/openvdb/tools/LevelSetFilter.h +++ b/openvdb/openvdb/tools/LevelSetFilter.h @@ -98,11 +98,15 @@ class LevelSetFilter : public LevelSetTracker /// @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(); @@ -409,10 +413,10 @@ LevelSetFilter::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 inline void LevelSetFilter::Filter::filletImpl(const LeafRange& range) diff --git a/openvdb/openvdb/unittest/CMakeLists.txt b/openvdb/openvdb/unittest/CMakeLists.txt index f9665a0784..3339c97195 100644 --- a/openvdb/openvdb/unittest/CMakeLists.txt +++ b/openvdb/openvdb/unittest/CMakeLists.txt @@ -119,6 +119,7 @@ else() TestLeafManager.cc TestLeafMask.cc TestLeafOrigin.cc + TestLevelSetFilter.cc TestLevelSetRayIntersector.cc TestLevelSetUtil.cc TestLinearInterp.cc diff --git a/openvdb/openvdb/unittest/TestLevelSetFilter.cc b/openvdb/openvdb/unittest/TestLevelSetFilter.cc new file mode 100644 index 0000000000..12fc472f01 --- /dev/null +++ b/openvdb/openvdb/unittest/TestLevelSetFilter.cc @@ -0,0 +1,80 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include // for csgUnion() + +#include + +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; + + const float radius = 5.0f; + const float voxelSize = 1.0f; + const float halfWidth = 3.0f; + typename GridT::Ptr sdfGrid = tools::createLevelSetSphere(/*radius=*/radius, + /*center=*/Vec3f(-radius, 0.0f, 0.0f), + /*dx=*/voxelSize, /*halfWidth*/ halfWidth); + typename GridT::Ptr sdfGridB = tools::createLevelSetSphere(/*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); + + { + 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(); + + { + 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); + } +} diff --git a/pendingchanges/levelset_fillet.txt b/pendingchanges/levelset_fillet.txt new file mode 100644 index 0000000000..ca96a9f009 --- /dev/null +++ b/pendingchanges/levelset_fillet.txt @@ -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] \ No newline at end of file