From a113cfc683767262f6ccc8ddf96fcd5cf64952dd Mon Sep 17 00:00:00 2001 From: apradhana Date: Tue, 27 Aug 2024 10:40:24 -0700 Subject: [PATCH] Add various tests for HalfGrid: LevelSetAdvect, Interpolation, Morphology, Grid, LevelSetUtil, RayIntersector, etc. Signed-off-by: apradhana --- openvdb/openvdb/Types.h | 7 + openvdb/openvdb/tools/Diagnostics.h | 3 +- openvdb/openvdb/tools/RayIntersector.h | 2 +- openvdb/openvdb/unittest/TestDiagnostics.cc | 23 ++ openvdb/openvdb/unittest/TestGrid.cc | 1 + .../unittest/TestLevelSetRayIntersector.cc | 105 +++--- openvdb/openvdb/unittest/TestLevelSetUtil.cc | 24 +- openvdb/openvdb/unittest/TestLinearInterp.cc | 165 ++++---- openvdb/openvdb/unittest/TestMeshToVolume.cc | 45 ++- openvdb/openvdb/unittest/TestMorphology.cc | 12 + .../openvdb/unittest/TestQuadraticInterp.cc | 14 +- openvdb/openvdb/unittest/TestTools.cc | 352 +++++++++++++++--- openvdb/openvdb/unittest/TestTree.cc | 45 ++- .../unittest/TestVolumeRayIntersector.cc | 66 ++-- .../openvdb/unittest/TestVolumeToSpheres.cc | 22 +- pendingchanges/half_grid_support.txt | 24 ++ 16 files changed, 669 insertions(+), 241 deletions(-) create mode 100644 pendingchanges/half_grid_support.txt diff --git a/openvdb/openvdb/Types.h b/openvdb/openvdb/Types.h index 26b6b5d74a..770821cedf 100644 --- a/openvdb/openvdb/Types.h +++ b/openvdb/openvdb/Types.h @@ -426,6 +426,13 @@ template struct CopyConstness +struct is_floating_point : std::is_floating_point { }; + +template<> +struct is_floating_point : std::is_floating_point { }; //////////////////////////////////////// diff --git a/openvdb/openvdb/tools/Diagnostics.h b/openvdb/openvdb/tools/Diagnostics.h index c603ddc721..d12f13dcf5 100644 --- a/openvdb/openvdb/tools/Diagnostics.h +++ b/openvdb/openvdb/tools/Diagnostics.h @@ -12,6 +12,7 @@ #define OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED #include +#include // for openvdb::is_floating_point #include #include #include @@ -428,7 +429,7 @@ template::value, + static_assert(openvdb::is_floating_point::value, "openvdb::tools::CheckNormGrad requires a scalar, floating-point grid"); using TileIterT = TreeIterT; using VoxelIterT = typename tree::IterTraits= -1 && NodeLevel < int(TreeT::DEPTH)-1, "NodeLevel out of range"); - static_assert(std::is_floating_point::value, + static_assert(openvdb::is_floating_point::value, "level set grids must have scalar, floating-point value types"); /// @brief Constructor diff --git a/openvdb/openvdb/unittest/TestDiagnostics.cc b/openvdb/openvdb/unittest/TestDiagnostics.cc index d5fbf3fbae..f59c1a1037 100644 --- a/openvdb/openvdb/unittest/TestDiagnostics.cc +++ b/openvdb/openvdb/unittest/TestDiagnostics.cc @@ -262,6 +262,29 @@ TEST_F(TestDiagnostics, testDiagnose) } }// testDiagnose +TEST_F(TestDiagnostics, testDiagnoseHalf) +{ + using namespace openvdb; + using half = openvdb::math::half; + + const half radius = 4.3f; + const openvdb::Vec3H center(half(15.8), half(13.2), half(16.7)); + const half voxelSize = 0.1f, width = 2.0f, gamma=voxelSize*width; + + HalfGrid::Ptr gridSphere = + tools::createLevelSetSphere(radius, center, voxelSize, width); + + {// check norm of gradient of sphere w/o mask + tools::CheckNormGrad c(*gridSphere, 0.75f, 1.25f); + tools::Diagnose d(*gridSphere); + std::string str = d.check(c, false, true, false, false); + //std::cerr << "NormGrad:\n" << str; + EXPECT_TRUE(str.empty()); + EXPECT_EQ(0, int(d.valueCount())); + EXPECT_EQ(0, int(d.failureCount())); + } +}// testDiagnoseHalf + TEST_F(TestDiagnostics, testCheckLevelSet) { using namespace openvdb; diff --git a/openvdb/openvdb/unittest/TestGrid.cc b/openvdb/openvdb/unittest/TestGrid.cc index d7d6056bb5..af8ea42c03 100644 --- a/openvdb/openvdb/unittest/TestGrid.cc +++ b/openvdb/openvdb/unittest/TestGrid.cc @@ -29,6 +29,7 @@ class ProxyTree: public openvdb::TreeBase public: using ValueType = int; using BuildType = int; + using ComputeType = int; using LeafNodeType = void; using ValueAllCIter = void; using ValueAllIter = void; diff --git a/openvdb/openvdb/unittest/TestLevelSetRayIntersector.cc b/openvdb/openvdb/unittest/TestLevelSetRayIntersector.cc index aeeec180a0..d9743d64c8 100644 --- a/openvdb/openvdb/unittest/TestLevelSetRayIntersector.cc +++ b/openvdb/openvdb/unittest/TestLevelSetRayIntersector.cc @@ -34,7 +34,9 @@ class TestLevelSetRayIntersector : public ::testing::Test }; -TEST_F(TestLevelSetRayIntersector, tests) +template +void +testLevelSetRayIntersectorImpl() { using namespace openvdb; typedef math::Ray RayT; @@ -45,9 +47,9 @@ TEST_F(TestLevelSetRayIntersector, tests) const Vec3f c(20.0f, 0.0f, 0.0f); const float s = 0.5f, w = 2.0f; - FloatGrid::Ptr ls = tools::createLevelSetSphere(r, c, s, w); + typename GridT::Ptr ls = tools::createLevelSetSphere(r, c, s, w); - tools::LevelSetRayIntersector lsri(*ls); + tools::LevelSetRayIntersector lsri(*ls); const Vec3T dir(1.0, 0.0, 0.0); const Vec3T eye(2.0, 0.0, 0.0); @@ -73,9 +75,9 @@ TEST_F(TestLevelSetRayIntersector, tests) const Vec3f c(20.0f, 0.0f, 0.0f); const float s = 0.5f, w = 2.0f; - FloatGrid::Ptr ls = tools::createLevelSetSphere(r, c, s, w); + typename GridT::Ptr ls = tools::createLevelSetSphere(r, c, s, w); - tools::LevelSetRayIntersector lsri(*ls); + tools::LevelSetRayIntersector lsri(*ls); const Vec3T dir(1.0,-0.0,-0.0); const Vec3T eye(2.0, 0.0, 0.0); @@ -101,28 +103,30 @@ TEST_F(TestLevelSetRayIntersector, tests) const Vec3f c(0.0f, 20.0f, 0.0f); const float s = 1.5f, w = 2.0f; - FloatGrid::Ptr ls = tools::createLevelSetSphere(r, c, s, w); + typename GridT::Ptr ls = tools::createLevelSetSphere(r, c, s, w); - tools::LevelSetRayIntersector lsri(*ls); + tools::LevelSetRayIntersector lsri(*ls); const Vec3T dir(0.0, 1.0, 0.0); const Vec3T eye(0.0,-2.0, 0.0); RayT ray(eye, dir); Vec3T xyz(0); Real time = 0; + constexpr double tolerance = std::is_floating_point_v ? 1e-6 : 1e-3; + EXPECT_TRUE(lsri.intersectsWS(ray, xyz, time)); - ASSERT_DOUBLES_APPROX_EQUAL( 0.0, xyz[0]); - ASSERT_DOUBLES_APPROX_EQUAL(15.0, xyz[1]); - ASSERT_DOUBLES_APPROX_EQUAL( 0.0, xyz[2]); - ASSERT_DOUBLES_APPROX_EQUAL(17.0, time); + EXPECT_NEAR( 0.0, xyz[0], tolerance); + EXPECT_NEAR(15.0, xyz[1], tolerance); + EXPECT_NEAR( 0.0, xyz[2], tolerance); + EXPECT_NEAR(17.0, time, tolerance); double t0=0, t1=0; EXPECT_TRUE(ray.intersects(c, r, t0, t1)); - ASSERT_DOUBLES_APPROX_EQUAL(t0, time); + EXPECT_NEAR(t0, time, tolerance); //std::cerr << "\nray("< ? 1e-6 : 2e-5; + EXPECT_TRUE(lsri.intersectsWS(ray, xyz, time)); //std::cerr << "\nIntersection at xyz = " << xyz << std::endl; //analytical intersection test double t0=0, t1=0; EXPECT_TRUE(ray.intersects(c, r, t0, t1)); - ASSERT_DOUBLES_APPROX_EQUAL(t0, time); - ASSERT_DOUBLES_APPROX_EQUAL((ray(t0)-c).length()-r, 0); - ASSERT_DOUBLES_APPROX_EQUAL((ray(t1)-c).length()-r, 0); + EXPECT_NEAR(t0, time, tolerance); + EXPECT_NEAR((ray(t0)-c).length()-r, 0, tolerance); + EXPECT_NEAR((ray(t1)-c).length()-r, 0, tolerance); //std::cerr << "\nray("< c(-g, g); + const T g = gamma + voxelSize; + tools::CheckRange c(-g, g); tools::Diagnose d(*grid); std::string str = d.check(c); //std::cerr << "Values out of range:\n" << str; @@ -288,7 +308,7 @@ TEST_F(TestTools, testLevelSetAdvect) } /* {//test tracker - GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + typename GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); using TrackerT = openvdb::tools::LevelSetTracker; TrackerT tracker(*grid); tracker.setSpatialScheme(openvdb::math::HJWENO5_BIAS); @@ -302,11 +322,11 @@ TEST_F(TestTools, testLevelSetAdvect) tracker.track(); fw("Tracker", 0, 0); } + } */ - /* {//test EnrightField - GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + typename GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); using FieldT = openvdb::tools::EnrightField; FieldT field; @@ -324,11 +344,12 @@ TEST_F(TestTools, testLevelSetAdvect) for (float t = 0, dt = 0.5f; !grid->empty() && t < 1.0f; t += dt) { fw("Enright", t + dt, advect.advect(t, t + dt)); } - } + } */ /* + using VectT = Vec3fGrid; {// test DiscreteGrid - Aligned - GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + typename GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); VectT vect(openvdb::Vec3f(1,0,0)); using FieldT = openvdb::tools::DiscreteField; FieldT field(vect); @@ -344,18 +365,18 @@ TEST_F(TestTools, testLevelSetAdvect) for (float t = 0, dt = 0.5f; !grid->empty() && t < 1.0f; t += dt) { fw("Aligned", t + dt, advect.advect(t, t + dt)); } - } + } */ /* {// test DiscreteGrid - Transformed - GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); + typename GridT::Ptr grid = openvdb::tools::createLevelSetSphere(radius, center, voxelSize); VectT vect(openvdb::Vec3f(0,0,0)); VectT::Accessor acc = vect.getAccessor(); for (openvdb::Coord ijk(0); ijk[0]; FieldT field(vect); using AdvectT = openvdb::tools::LevelSetAdvection; @@ -370,23 +391,38 @@ TEST_F(TestTools, testLevelSetAdvect) for (float t = 0, dt = 0.5f; !grid->empty() && t < 1.0f; t += dt) { fw("Xformed", t + dt, advect.advect(t, t + dt)); } - } + } */ -}//testLevelSetAdvect +} + +TEST_F(TestTools, testLevelSetAdvectFloat) +{ + testLevelSetAdvectImpl(); +}//testLevelSetAdvectFloat + + +TEST_F(TestTools, testLevelSetAdvectHalf) +{ + testLevelSetAdvectImpl(); +}//testLevelSetAdvectHalf //////////////////////////////////////// -TEST_F(TestTools, testLevelSetMorph) + +template +void +testLevelSetMorphImpl() { - using GridT = openvdb::FloatGrid; + using ValueT = typename GridT::ValueType; + using Vec3T = typename openvdb::math::Vec3; {//test morphing overlapping but aligned spheres const int dim = 64; - const openvdb::Vec3f C1(0.35f, 0.35f, 0.35f), C2(0.4f, 0.4f, 0.4f); - const float radius = 0.15f, voxelSize = 1.0f/(dim-1); + const Vec3T C1(ValueT(0.35), ValueT(0.35), ValueT(0.35)), C2(ValueT(0.4), ValueT(0.4), ValueT(0.4)); + const ValueT radius = ValueT(0.15), voxelSize = ValueT(1.0)/(dim-1); - GridT::Ptr source = openvdb::tools::createLevelSetSphere(radius, C1, voxelSize); - GridT::Ptr target = openvdb::tools::createLevelSetSphere(radius, C2, voxelSize); + typename GridT::Ptr source = openvdb::tools::createLevelSetSphere(radius, C1, voxelSize); + typename GridT::Ptr target = openvdb::tools::createLevelSetSphere(radius, C2, voxelSize); using MorphT = openvdb::tools::LevelSetMorphing; MorphT morph(*source, *target); @@ -410,16 +446,16 @@ TEST_F(TestTools, testLevelSetMorph) const float invDx = 1.0f/voxelSize; openvdb::math::Stats s; - for (GridT::ValueOnCIter it = source->tree().cbeginValueOn(); it; ++it) { + for (typename GridT::ValueOnCIter it = source->tree().cbeginValueOn(); it; ++it) { s.add( invDx*(*it - target->tree().getValue(it.getCoord())) ); } - for (GridT::ValueOnCIter it = target->tree().cbeginValueOn(); it; ++it) { + for (typename GridT::ValueOnCIter it = target->tree().cbeginValueOn(); it; ++it) { s.add( invDx*(*it - target->tree().getValue(it.getCoord())) ); } //s.print("Morph"); EXPECT_NEAR(0.0, s.min(), 0.50); - EXPECT_NEAR(0.0, s.max(), 0.50); - EXPECT_NEAR(0.0, s.avg(), 0.02); + EXPECT_NEAR(0.0, s.max(), 0.60); //Float passes with 0.50 tol + EXPECT_NEAR(0.0, s.avg(), 0.025); //Float passes with 0.025 tol /* openvdb::math::Histogram h(s, 30); for (GridT::ValueOnCIter it = source->tree().cbeginValueOn(); it; ++it) { @@ -498,7 +534,18 @@ TEST_F(TestTools, testLevelSetMorph) } */ -}//testLevelSetMorph +}//testLevelSetMorphImpl + +TEST_F(TestTools, testLevelSetMorphFloat) +{ + testLevelSetMorphImpl(); +} + + +TEST_F(TestTools, testLevelSetMorphHalf) +{ + testLevelSetMorphImpl(); +} //////////////////////////////////////// @@ -699,9 +746,188 @@ TEST_F(TestTools, testLevelSetMeasure) EXPECT_EQ(2, x); } } - }//testLevelSetMeasure + +template +void +testLevelSetMeasureImpl() +{ + using ValueT = typename GridT::ValueType; + // Double can pass with 0.1% percentage, Half requires 0.25% tolerance. + const typename GridT::ValueType percentage = ValueT(0.25)/ValueT(100.0);//i.e. 0.25% + const int dim = 256; + openvdb::Real area, volume, mean, gauss; + + // First sphere + openvdb::Vec3f C(0.35f, 0.35f, 0.35f); + openvdb::Real r = 0.15, voxelSize = 1.0/(dim-1); + const openvdb::Real Pi = openvdb::math::pi(); + typename GridT::Ptr sphere = openvdb::tools::createLevelSetSphere(float(r), C, float(voxelSize)); + + using MeasureT = openvdb::tools::LevelSetMeasure; + MeasureT m(*sphere); + + /// Test area and volume of sphere in world units + area = 4*Pi*r*r; + volume = 4.0/3.0*Pi*r*r*r; + // Test accuracy of computed measures to within 0.1% of the exact measure. + EXPECT_NEAR(area, m.area(), percentage*area); + EXPECT_NEAR(volume, m.volume(), percentage*volume); + + // Test area, volume and average mean curvature of sphere in world units + mean = 1.0/r; + // Test accuracy of computed measures to within 0.1% of the exact measure. + EXPECT_NEAR(area, m.area(), percentage*area); + EXPECT_NEAR(volume, m.volume(), percentage*volume); + EXPECT_NEAR(mean, m.avgMeanCurvature(), percentage*mean); + + // Test area, volume, average mean curvature and average gaussian curvature of sphere in world units + gauss = 1.0/(r*r); + // Test accuracy of computed measures to within 0.1% of the exact measure. + EXPECT_NEAR(area, m.area(), percentage*area); + EXPECT_NEAR(volume, m.volume(), percentage*volume); + EXPECT_NEAR(mean, m.avgMeanCurvature(), percentage*mean); + EXPECT_NEAR(gauss, m.avgGaussianCurvature(), percentage*gauss); + EXPECT_EQ(0, m.genus()); + + // Test measures of sphere in voxel units + r /= voxelSize; + area = 4*Pi*r*r; + volume = 4.0/3.0*Pi*r*r*r; + mean = 1.0/r; + // Test accuracy of computed measures to within 0.1% of the exact measure. + EXPECT_NEAR(area, m.area(false), percentage*area); + EXPECT_NEAR(volume, m.volume(false), percentage*volume); + EXPECT_NEAR(mean, m.avgMeanCurvature(false), percentage*mean); + + gauss = 1.0/(r*r); + // Test accuracy of computed measures to within 0.1% of the exact measure. + EXPECT_NEAR(area, m.area(false), percentage*area); + EXPECT_NEAR(volume, m.volume(false), percentage*volume); + EXPECT_NEAR(mean, m.avgMeanCurvature(false), percentage*mean); + EXPECT_NEAR(gauss, m.avgGaussianCurvature(false), percentage*gauss); + EXPECT_EQ(0, m.genus()); + + // Second sphere + C = openvdb::Vec3f(5.4f, 6.4f, 8.4f); + r = 0.57; + sphere = openvdb::tools::createLevelSetSphere(float(r), C, float(voxelSize)); + m.init(*sphere); + + // Test all measures of sphere in world units + area = 4*Pi*r*r; + volume = 4.0/3.0*Pi*r*r*r; + mean = 1.0/r; + gauss = 1.0/(r*r); + // Test accuracy of computed measures to within 0.1% of the exact measure. + EXPECT_NEAR(area, m.area(), percentage*area); + EXPECT_NEAR(volume, m.volume(), percentage*volume); + EXPECT_NEAR(mean, m.avgMeanCurvature(), percentage*mean); + EXPECT_NEAR(gauss, m.avgGaussianCurvature(), percentage*gauss); + EXPECT_EQ(0, m.genus()); + + // Test all measures of sphere in voxel units + r /= voxelSize; + area = 4*Pi*r*r; + volume = 4.0/3.0*Pi*r*r*r; + mean = 1.0/r; + gauss = 1.0/(r*r); + // Test accuracy of computed measures to within 0.1% of the exact measure. + EXPECT_NEAR(area, m.area(false), percentage*area); + EXPECT_NEAR(volume, m.volume(false), percentage*volume); + EXPECT_NEAR(mean, m.avgMeanCurvature(false), percentage*mean); + EXPECT_NEAR(gauss, m.avgGaussianCurvature(false), percentage*gauss); + EXPECT_NEAR(area, openvdb::tools::levelSetArea(*sphere,false), + percentage*area); + EXPECT_NEAR(volume,openvdb::tools::levelSetVolume(*sphere,false), + percentage*volume); + EXPECT_EQ(0, openvdb::tools::levelSetGenus(*sphere)); + + // Read level set from file + /* + util::CpuTimer timer; + openvdb::initialize();//required whenever I/O of OpenVDB files is performed! + openvdb::io::File sourceFile("/usr/pic1/Data/OpenVDB/LevelSetModels/venusstatue.vdb"); + sourceFile.open(); + GridT::Ptr model = openvdb::gridPtrCast(sourceFile.getGrids()->at(0)); + m.reinit(*model); + + //m.setGrainSize(1); + timer.start("\nParallel measure of area and volume"); + m.measure(a, v, false); + timer.stop(); + std::cerr << "Model: area = " << a << ", volume = " << v << std::endl; + + timer.start("\nParallel measure of area, volume and curvature"); + m.measure(a, v, c, false); + timer.stop(); + std::cerr << "Model: area = " << a << ", volume = " << v + << ", average curvature = " << c << std::endl; + + m.setGrainSize(0); + timer.start("\nSerial measure of area and volume"); + m.measure(a, v, false); + timer.stop(); + std::cerr << "Model: area = " << a << ", volume = " << v << std::endl; + + timer.start("\nSerial measure of area, volume and curvature"); + m.measure(a, v, c, false); + timer.stop(); + std::cerr << "Model: area = " << a << ", volume = " << v + << ", average curvature = " << c << std::endl; + */ + // Run these tests if it is a FloatGrid (but not HalfGrid) because csgUnion currently does not support HalfGrid + if constexpr(std::is_floating_point::value) + { + {// testing total genus of multiple disjoint level set spheres with different radius + const float dx = 0.5f, r = 50.0f; + auto grid = openvdb::createLevelSet(dx); + EXPECT_THROW(openvdb::tools::levelSetGenus(*grid), openvdb::RuntimeError); + for (int i=1; i<=3; ++i) { + auto sphere = openvdb::tools::createLevelSetSphere(r+float(i)*5.0f , openvdb::Vec3f(100.0f*float(i)), dx); + openvdb::tools::csgUnion(*grid, *sphere); + const int x = openvdb::tools::levelSetEulerCharacteristic(*grid);// since they are not overlapping re-normalization is not required + EXPECT_EQ(2*i, x); + } + } + {// testing total genus of multiple disjoint level set cubes of different size + const float dx = 0.5f, size = 50.0f; + auto grid = openvdb::createLevelSet(dx); + EXPECT_THROW(openvdb::tools::levelSetGenus(*grid), openvdb::RuntimeError); + for (int i=1; i<=2; ++i) { + auto shape = openvdb::tools::createLevelSetCube(size, openvdb::Vec3f(100.0f*float(i)), dx); + openvdb::tools::csgUnion(*grid, *shape); + const int x = openvdb::tools::levelSetEulerCharacteristic(*grid); + EXPECT_EQ(2*i, x); + } + } + {// testing Euler characteristic and total genus of multiple intersecting (connected) level set spheres + const float dx = 0.5f, r = 50.0f; + auto grid = openvdb::createLevelSet(dx); + EXPECT_THROW(openvdb::tools::levelSetGenus(*grid), openvdb::RuntimeError); + for (int i=1; i<=4; ++i) { + auto sphere = openvdb::tools::createLevelSetSphere( r , openvdb::Vec3f(30.0f*float(i), 0.0f, 0.0f), dx); + openvdb::tools::csgUnion(*grid, *sphere); + const int genus = openvdb::tools::levelSetGenus(*grid); + const int x = openvdb::tools::levelSetEulerCharacteristic(*grid); + EXPECT_EQ(0, genus); + EXPECT_EQ(2, x); + } + } + } +}//testLevelSetMeasureImpl + +TEST_F(TestTools, testLevelSetMeasureFloat) +{ + testLevelSetMeasureImpl(); +}//testLevelSetMeasureFloat + +TEST_F(TestTools, testLevelSetMeasureHalf) +{ + testLevelSetMeasureImpl(); +}//testLevelSetMeasureHalf + TEST_F(TestTools, testMagnitude) { using namespace openvdb; @@ -1137,20 +1363,24 @@ TEST_F(TestTools, testPointScatter) //////////////////////////////////////// -TEST_F(TestTools, testVolumeAdvect) +template +void +testVolumeAdvectImpl() { using namespace openvdb; + using ValueT = typename GridT::ValueType; + using Vec3T = typename openvdb::math::Vec3; - Vec3fGrid velocity(Vec3f(1.0f, 0.0f, 0.0f)); - using GridT = FloatGrid; + // TODO: Define Vec3TreeT = typename tree::Tree4::Type and use Grid + Vec3fGrid velocity(Vec3T(ValueT(1.0), ValueT(0.0), ValueT(0.0))); using AdvT = tools::VolumeAdvection; using SamplerT = tools::Sampler<1>; {//test non-uniform grids (throws) - GridT::Ptr density0 = GridT::create(0.0f); + typename GridT::Ptr density0 = GridT::create(0.0f); density0->transform().preScale(Vec3d(1.0, 2.0, 3.0));//i.e. non-uniform voxels AdvT a(velocity); - EXPECT_THROW((a.advect(*density0, 0.1f)), RuntimeError); + EXPECT_THROW((a.advect(*density0, ValueT(0.1))), RuntimeError); } {// test spatialOrder and temporalOrder @@ -1198,7 +1428,7 @@ TEST_F(TestTools, testVolumeAdvect) } {//test RK4 advect without a mask - GridT::Ptr density0 = GridT::create(0.0f), density1; + typename GridT::Ptr density0 = GridT::create(0.0f), density1; density0->fill(CoordBBox(Coord(0),Coord(6)), 1.0f); EXPECT_EQ(density0->tree().getValue(Coord( 3,3,3)), 1.0f); EXPECT_EQ(density0->tree().getValue(Coord(24,3,3)), 0.0f); @@ -1224,7 +1454,7 @@ TEST_F(TestTools, testVolumeAdvect) EXPECT_TRUE( density0->tree().isValueOn(Coord(24,3,3))); } {//test MAC advect without a mask - GridT::Ptr density0 = GridT::create(0.0f), density1; + typename GridT::Ptr density0 = GridT::create(0.0f), density1; density0->fill(CoordBBox(Coord(0),Coord(6)), 1.0f); EXPECT_EQ(density0->tree().getValue(Coord( 3,3,3)), 1.0f); EXPECT_EQ(density0->tree().getValue(Coord(24,3,3)), 0.0f); @@ -1250,7 +1480,7 @@ TEST_F(TestTools, testVolumeAdvect) EXPECT_TRUE( density0->tree().isValueOn(Coord(24,3,3))); } {//test advect with a mask - GridT::Ptr density0 = GridT::create(0.0f), density1; + typename GridT::Ptr density0 = GridT::create(0.0f), density1; density0->fill(CoordBBox(Coord(0),Coord(6)), 1.0f); EXPECT_EQ(density0->tree().getValue(Coord( 3,3,3)), 1.0f); EXPECT_EQ(density0->tree().getValue(Coord(24,3,3)), 0.0f); @@ -1312,7 +1542,15 @@ TEST_F(TestTools, testVolumeAdvect) } } */ -}// testVolumeAdvect +}// testVolumeAdvectImpl + +TEST_F(TestTools, testVolumeAdvectFloat) { + testVolumeAdvectImpl(); +} + +TEST_F(TestTools, testVolumeAdvectHalf) { + testVolumeAdvectImpl(); +} //////////////////////////////////////// diff --git a/openvdb/openvdb/unittest/TestTree.cc b/openvdb/openvdb/unittest/TestTree.cc index 92ab90a5a1..0db932ca47 100644 --- a/openvdb/openvdb/unittest/TestTree.cc +++ b/openvdb/openvdb/unittest/TestTree.cc @@ -21,6 +21,7 @@ #include // for remove() #include #include +#include #define ASSERT_DOUBLES_EXACTLY_EQUAL(expected, actual) \ EXPECT_NEAR((expected), (actual), /*tolerance=*/0.0); @@ -1951,19 +1952,23 @@ TEST_F(TestTree, testFill) }// testFill -TEST_F(TestTree, testSignedFloodFill) +template +void +testSignedFloodFillImpl() { + using ValueT = typename GridT::ValueType; + using Vec3T = typename openvdb::math::Vec3; // Use a custom tree configuration to ensure we flood-fill at all levels! - using LeafT = openvdb::tree::LeafNode;//4^3 + using LeafT = openvdb::tree::LeafNode;//4^3 using InternalT = openvdb::tree::InternalNode;//4^3 using RootT = openvdb::tree::RootNode;// child nodes are 16^3 using TreeT = openvdb::tree::Tree; - const float outside = 2.0f, inside = -outside, radius = 20.0f; + const ValueT outside = ValueT(2.0), inside = -outside, radius = ValueT(20.0); {//first test flood filling of a leaf node - const LeafT::ValueType fill0=5, fill1=-fill0; + const typename LeafT::ValueType fill0=5, fill1=-fill0; openvdb::tools::SignedFloodFillOp sff(fill0, fill1); int D = LeafT::dim(), C=D/2; @@ -1991,7 +1996,7 @@ TEST_F(TestTree, testSignedFloodFill) EXPECT_EQ(fill1, leaf.getValue(last)); } - openvdb::Grid::Ptr grid = openvdb::Grid::create(outside); + typename openvdb::Grid::Ptr grid = openvdb::Grid::create(outside); TreeT& tree = grid->tree(); const RootT& root = tree.root(); const openvdb::Coord dim(3*16, 3*16, 3*16); @@ -2001,16 +2006,16 @@ TEST_F(TestTree, testSignedFloodFill) EXPECT_TRUE(root.getTableSize()==0); //make narrow band of sphere without setting sign for the background values! - openvdb::Grid::Accessor acc = grid->getAccessor(); - const openvdb::Vec3f center(static_cast(C[0]), - static_cast(C[1]), - static_cast(C[2])); + typename openvdb::Grid::Accessor acc = grid->getAccessor(); + const Vec3T center(static_cast(C[0]), + static_cast(C[1]), + static_cast(C[2])); openvdb::Coord xyz; for (xyz[0]=0; xyz[0]transform().indexToWorld(xyz); - const float dist = float((p-center).length() - radius); + const ValueT dist = float((p-center).length() - radius); if (fabs(dist) > outside) continue; acc.setValue(xyz, dist); } @@ -2025,8 +2030,8 @@ TEST_F(TestTree, testSignedFloodFill) for (xyz[1]=0; xyz[1]transform().indexToWorld(xyz); - const float dist = float((p-center).length() - radius); - const float val = acc.getValue(xyz); + const ValueT dist = ValueT((p-center).length() - radius); + const ValueT val = acc.getValue(xyz); if (dist < inside) { ASSERT_DOUBLES_EXACTLY_EQUAL( val, outside); } else if (dist>outside) { @@ -2047,8 +2052,8 @@ TEST_F(TestTree, testSignedFloodFill) for (xyz[1]=0; xyz[1]transform().indexToWorld(xyz); - const float dist = float((p-center).length() - radius); - const float val = acc.getValue(xyz); + const ValueT dist = ValueT((p-center).length() - radius); + const ValueT val = acc.getValue(xyz); if (dist < inside) { ASSERT_DOUBLES_EXACTLY_EQUAL( val, inside); } else if (dist>outside) { @@ -2063,6 +2068,18 @@ TEST_F(TestTree, testSignedFloodFill) EXPECT_TRUE(root.getTableSize()>size_before);//added inside root tiles EXPECT_TRUE(!tree.isValueOn(C)); ASSERT_DOUBLES_EXACTLY_EQUAL(inside,tree.getValue(C)); +}//testSignedFloodFillImpl + + +TEST_F(TestTree, testSignedFloodFillFloat) +{ + testSignedFloodFillImpl(); +} + + +TEST_F(TestTree, testSignedFloodFillHalf) +{ + testSignedFloodFillImpl(); } diff --git a/openvdb/openvdb/unittest/TestVolumeRayIntersector.cc b/openvdb/openvdb/unittest/TestVolumeRayIntersector.cc index ed2fef1ae0..d92d7090e1 100644 --- a/openvdb/openvdb/unittest/TestVolumeRayIntersector.cc +++ b/openvdb/openvdb/unittest/TestVolumeRayIntersector.cc @@ -27,14 +27,16 @@ class TestVolumeRayIntersector : public ::testing::Test }; -TEST_F(TestVolumeRayIntersector, testAll) +template +void +testVolumeRayIntersectorImpl() { using namespace openvdb; typedef math::Ray RayT; typedef RayT::Vec3Type Vec3T; {//one single leaf node - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(0,0,0), 1.0f); grid.tree().setValue(Coord(7,7,7), 1.0f); @@ -42,7 +44,7 @@ TEST_F(TestVolumeRayIntersector, testAll) const Vec3T dir( 1.0, 0.0, 0.0); const Vec3T eye(-1.0, 0.0, 0.0); const RayT ray(eye, dir);//ray in index space - tools::VolumeRayIntersector inter(grid); + tools::VolumeRayIntersector inter(grid); EXPECT_TRUE(inter.setIndexRay(ray)); double t0=0, t1=0; EXPECT_TRUE(inter.march(t0, t1)); @@ -51,7 +53,7 @@ TEST_F(TestVolumeRayIntersector, testAll) EXPECT_TRUE(!inter.march(t0, t1)); } {//same as above but with dilation - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(0,0,0), 1.0f); grid.tree().setValue(Coord(7,7,7), 1.0f); @@ -59,7 +61,7 @@ TEST_F(TestVolumeRayIntersector, testAll) const Vec3T dir( 1.0, 0.0, 0.0); const Vec3T eye(-1.0, 0.0, 0.0); const RayT ray(eye, dir);//ray in index space - tools::VolumeRayIntersector inter(grid, 1); + tools::VolumeRayIntersector inter(grid, 1); EXPECT_TRUE(inter.setIndexRay(ray)); double t0=0, t1=0; EXPECT_TRUE(inter.march(t0, t1)); @@ -68,7 +70,7 @@ TEST_F(TestVolumeRayIntersector, testAll) EXPECT_TRUE(!inter.march(t0, t1)); } {//one single leaf node - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(1,1,1), 1.0f); grid.tree().setValue(Coord(7,3,3), 1.0f); @@ -76,7 +78,7 @@ TEST_F(TestVolumeRayIntersector, testAll) const Vec3T dir( 1.0, 0.0, 0.0); const Vec3T eye(-1.0, 0.0, 0.0); const RayT ray(eye, dir);//ray in index space - tools::VolumeRayIntersector inter(grid); + tools::VolumeRayIntersector inter(grid); EXPECT_TRUE(inter.setIndexRay(ray)); double t0=0, t1=0; EXPECT_TRUE(inter.march(t0, t1)); @@ -85,7 +87,7 @@ TEST_F(TestVolumeRayIntersector, testAll) EXPECT_TRUE(!inter.march(t0, t1)); } {//same as above but with dilation - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(1,1,1), 1.0f); grid.tree().setValue(Coord(7,3,3), 1.0f); @@ -93,7 +95,7 @@ TEST_F(TestVolumeRayIntersector, testAll) const Vec3T dir( 1.0, 0.0, 0.0); const Vec3T eye(-1.0, 0.0, 0.0); const RayT ray(eye, dir);//ray in index space - tools::VolumeRayIntersector inter(grid, 1); + tools::VolumeRayIntersector inter(grid, 1); EXPECT_TRUE(inter.setIndexRay(ray)); double t0=0, t1=0; EXPECT_TRUE(inter.march(t0, t1)); @@ -102,7 +104,7 @@ TEST_F(TestVolumeRayIntersector, testAll) EXPECT_TRUE(!inter.march(t0, t1)); } {//two adjacent leaf nodes - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(0,0,0), 1.0f); grid.tree().setValue(Coord(8,0,0), 1.0f); @@ -111,7 +113,7 @@ TEST_F(TestVolumeRayIntersector, testAll) const Vec3T dir( 1.0, 0.0, 0.0); const Vec3T eye(-1.0, 0.0, 0.0); const RayT ray(eye, dir);//ray in index space - tools::VolumeRayIntersector inter(grid); + tools::VolumeRayIntersector inter(grid); EXPECT_TRUE(inter.setIndexRay(ray)); double t0=0, t1=0; EXPECT_TRUE(inter.march(t0, t1)); @@ -120,7 +122,7 @@ TEST_F(TestVolumeRayIntersector, testAll) EXPECT_TRUE(!inter.march(t0, t1)); } {//two adjacent leafs followed by a gab and leaf - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(0*8,0,0), 1.0f); grid.tree().setValue(Coord(1*8,0,0), 1.0f); @@ -130,7 +132,7 @@ TEST_F(TestVolumeRayIntersector, testAll) const Vec3T dir( 1.0, 0.0, 0.0); const Vec3T eye(-1.0, 0.0, 0.0); const RayT ray(eye, dir);//ray in index space - tools::VolumeRayIntersector inter(grid); + tools::VolumeRayIntersector inter(grid); EXPECT_TRUE(inter.setIndexRay(ray)); double t0=0, t1=0; EXPECT_TRUE(inter.march(t0, t1)); @@ -142,7 +144,7 @@ TEST_F(TestVolumeRayIntersector, testAll) EXPECT_TRUE(!inter.march(t0, t1)); } {//two adjacent leafs followed by a gab, a leaf and an active tile - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(0*8,0,0), 1.0f); grid.tree().setValue(Coord(1*8,0,0), 1.0f); @@ -152,7 +154,7 @@ TEST_F(TestVolumeRayIntersector, testAll) const Vec3T dir( 1.0, 0.0, 0.0); const Vec3T eye(-1.0, 0.0, 0.0); const RayT ray(eye, dir);//ray in index space - tools::VolumeRayIntersector inter(grid); + tools::VolumeRayIntersector inter(grid); EXPECT_TRUE(inter.setIndexRay(ray)); double t0=0, t1=0; EXPECT_TRUE(inter.march(t0, t1)); @@ -165,7 +167,7 @@ TEST_F(TestVolumeRayIntersector, testAll) } {//two adjacent leafs followed by a gab, a leaf and an active tile - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(0*8,0,0), 1.0f); grid.tree().setValue(Coord(1*8,0,0), 1.0f); @@ -175,7 +177,7 @@ TEST_F(TestVolumeRayIntersector, testAll) const Vec3T dir( 1.0, 0.0, 0.0); const Vec3T eye(-1.0, 0.0, 0.0); const RayT ray(eye, dir);//ray in index space - tools::VolumeRayIntersector inter(grid); + tools::VolumeRayIntersector inter(grid); EXPECT_TRUE(inter.setIndexRay(ray)); std::vector list; @@ -188,7 +190,7 @@ TEST_F(TestVolumeRayIntersector, testAll) } {//same as above but now with std::deque instead of std::vector - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(0*8,0,0), 1.0f); grid.tree().setValue(Coord(1*8,0,0), 1.0f); @@ -198,7 +200,7 @@ TEST_F(TestVolumeRayIntersector, testAll) const Vec3T dir( 1.0, 0.0, 0.0); const Vec3T eye(-1.0, 0.0, 0.0); const RayT ray(eye, dir);//ray in index space - tools::VolumeRayIntersector inter(grid); + tools::VolumeRayIntersector inter(grid); EXPECT_TRUE(inter.setIndexRay(ray)); std::deque list; @@ -211,11 +213,11 @@ TEST_F(TestVolumeRayIntersector, testAll) } {// Test submitted by "Jan" @ GitHub - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.tree().setValue(Coord(0*8,0,0), 1.0f); grid.tree().setValue(Coord(1*8,0,0), 1.0f); grid.tree().setValue(Coord(3*8,0,0), 1.0f); - tools::VolumeRayIntersector inter(grid); + tools::VolumeRayIntersector inter(grid); const Vec3T dir(-1.0, 0.0, 0.0); const Vec3T eye(50.0, 0.0, 0.0); @@ -233,10 +235,10 @@ TEST_F(TestVolumeRayIntersector, testAll) {// Test submitted by "Trevor" @ GitHub - FloatGrid::Ptr grid = createGrid(0.0f); + typename GridT::Ptr grid = createGrid(0.0f); grid->tree().setValue(Coord(0,0,0), 1.0f); tools::dilateActiveValues(grid->tree(), 1, tools::NN_FACE, tools::IGNORE_TILES); - tools::VolumeRayIntersector inter(*grid); + tools::VolumeRayIntersector inter(*grid); //std::cerr << "BBox = " << inter.bbox() << std::endl; @@ -251,10 +253,10 @@ TEST_F(TestVolumeRayIntersector, testAll) {// Test submitted by "Trevor" @ GitHub - FloatGrid::Ptr grid = createGrid(0.0f); + typename GridT::Ptr grid = createGrid(0.0f); grid->tree().setValue(Coord(0,0,0), 1.0f); tools::dilateActiveValues(grid->tree(), 1, tools::NN_FACE, tools::IGNORE_TILES); - tools::VolumeRayIntersector inter(*grid); + tools::VolumeRayIntersector inter(*grid); //GridPtrVec grids; //grids.push_back(grid); @@ -275,9 +277,9 @@ TEST_F(TestVolumeRayIntersector, testAll) {// Test derived from the test submitted by "Trevor" @ GitHub - FloatGrid grid(0.0f); + GridT grid(0.0f); grid.fill(math::CoordBBox(Coord(-1,-1,-1),Coord(1,1,1)), 1.0f); - tools::VolumeRayIntersector inter(grid); + tools::VolumeRayIntersector inter(grid); //std::cerr << "BBox = " << inter.bbox() << std::endl; const Vec3T eye(-0.25, -0.25, 10.0); @@ -290,3 +292,13 @@ TEST_F(TestVolumeRayIntersector, testAll) //std::cerr << "t0=" << t0 << " t1=" << t1 << std::endl; } } + +TEST_F(TestVolumeRayIntersector, testAllFloat) +{ + testVolumeRayIntersectorImpl(); +} + +TEST_F(TestVolumeRayIntersector, testAllHalf) +{ + testVolumeRayIntersectorImpl(); +} diff --git a/openvdb/openvdb/unittest/TestVolumeToSpheres.cc b/openvdb/openvdb/unittest/TestVolumeToSpheres.cc index 7d7979b6be..53d727fac9 100644 --- a/openvdb/openvdb/unittest/TestVolumeToSpheres.cc +++ b/openvdb/openvdb/unittest/TestVolumeToSpheres.cc @@ -165,16 +165,20 @@ TEST_F(TestVolumeToSpheres, testMinimumSphereCount) } -TEST_F(TestVolumeToSpheres, testClosestSurfacePoint) +template +void +testClosestSurfacePointImpl() { using namespace openvdb; + using ValueT = typename GridT::ValueType; + using Vec3T = typename openvdb::math::Vec3; - const float voxelSize = 1.0f; - const Vec3f center{0.0f}; // ensure multiple internal nodes + const ValueT voxelSize = ValueT(1.0); + const Vec3T center{ValueT(0.0)}; // ensure multiple internal nodes for (const float radius: { 8.0f, 50.0f }) { // Construct a spherical level set. - const auto sphere = tools::createLevelSetSphere(radius, center, voxelSize); + const auto sphere = tools::createLevelSetSphere(radius, center, voxelSize); EXPECT_TRUE(sphere); // Construct the corners of a cube that exactly encloses the sphere. @@ -191,7 +195,7 @@ TEST_F(TestVolumeToSpheres, testClosestSurfacePoint) // Compute the distance from a corner of the cube to the surface of the sphere. const auto distToSurface = Vec3d{radius}.length() - radius; - auto csp = tools::ClosestSurfacePoint::create(*sphere); + auto csp = tools::ClosestSurfacePoint::create(*sphere); EXPECT_TRUE(csp); // Move each corner point to the closest surface point. @@ -223,3 +227,11 @@ TEST_F(TestVolumeToSpheres, testClosestSurfacePoint) ///< @todo off by half a voxel in y and z } } + +TEST_F(TestVolumeToSpheres, testClosestSurfacePointFloat) { + testClosestSurfacePointImpl(); +} + +TEST_F(TestVolumeToSpheres, testClosestSurfacePointHalf) { + testClosestSurfacePointImpl(); +} diff --git a/pendingchanges/half_grid_support.txt b/pendingchanges/half_grid_support.txt new file mode 100644 index 0000000000..312e971abd --- /dev/null +++ b/pendingchanges/half_grid_support.txt @@ -0,0 +1,24 @@ +# TODO +Tests: +[X] LevelSetAdvect +[X] Interpolation +[X] TestMorphology +[X] TestGrid +[X] LevelSetAdvect +[X] LevelSetMeasure - Written, but not passing +[X] LevelSetMorph +[X] LevelSetSphere +[X] LevelSetUtil +[X] RayIntersector +[X] RayTracer +[X] SignedFloodFill +[X] VolumeAdvect +[X] VolumeToSpheres +[-] LevelSetFilter - doesn't have any +[-] LevelSetTracker +[-] MeshToVolume +[ ] FastSweeping +[ ] Test explicit template instantiation + +# TODO, but will be addressed later: +[ ] Add support for csgUnion for HalfGrid. \ No newline at end of file