diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index 808e6d3a05e..c1aedddedf4 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -69,7 +69,7 @@ create_single_source_cgal_program("self_intersection_polyhedron_test.cpp") create_single_source_cgal_program("self_intersection_surface_mesh_test.cpp") create_single_source_cgal_program("pmp_do_intersect_test.cpp") create_single_source_cgal_program("test_is_polygon_soup_a_polygon_mesh.cpp") -create_single_source_cgal_program("test_interopolated_corrected_curvatures.cpp") +create_single_source_cgal_program("test_interpolated_corrected_curvatures.cpp") create_single_source_cgal_program("test_stitching.cpp") create_single_source_cgal_program("remeshing_test.cpp") create_single_source_cgal_program("remeshing_with_isolated_constraints_test.cpp" ) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_interopolated_corrected_curvatures.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_interopolated_corrected_curvatures.cpp deleted file mode 100644 index b2dbcc0850b..00000000000 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_interopolated_corrected_curvatures.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace PMP = CGAL::Polygon_mesh_processing; - -typedef CGAL::Exact_predicates_inexact_constructions_kernel EpicKernel; -typedef CGAL::Surface_mesh SMesh; -typedef boost::graph_traits::face_descriptor face_descriptor; -typedef boost::graph_traits::edge_descriptor edge_descriptor; -typedef boost::graph_traits::vertex_descriptor vertex_descriptor; - -void test(std::string mesh_path, EpicKernel::FT rel_expansion_radius, EpicKernel::FT rel_noise_magnitude) { - SMesh pmesh; - const std::string filename = CGAL::data_file_path(mesh_path); - - if (!CGAL::IO::read_polygon_mesh(filename, pmesh)) - { - std::cerr << "Invalid input file." << std::endl; - } - - bool created = false; - - SMesh::Property_map mean_curvature_map, gaussian_curvature_map; - boost::tie(mean_curvature_map, created) = pmesh.add_property_map("v:mean_curvature_map", 0); - assert(created); - - boost::tie(gaussian_curvature_map, created) = pmesh.add_property_map("v:gaussian_curvature_map", 0); - assert(created); - - // getting the max and min edge lengthes - const auto edge_range = CGAL::edges(pmesh); - - const auto edge_length_comparator = [&, pmesh](auto l, auto r) { - return PMP::edge_length(l, pmesh) < PMP::edge_length(r, pmesh); - }; - - const edge_descriptor longest_edge = *std::max_element(edge_range.begin(), edge_range.end(), edge_length_comparator); - const EpicKernel::FT max_edge_length = PMP::edge_length(longest_edge, pmesh); - - const edge_descriptor shortest_edge = *std::min_element(edge_range.begin(), edge_range.end(), edge_length_comparator); - const EpicKernel::FT min_edge_length = PMP::edge_length(shortest_edge, pmesh); - - - if (rel_noise_magnitude > 0) - { - if (!CGAL::is_triangle_mesh(pmesh)) - return; - - SMesh::Property_map vnm; - boost::tie(vnm, created) = pmesh.add_property_map("v:vnm", { 0 , 0 , 0 }); - assert(created); - - CGAL::Polygon_mesh_processing::compute_vertex_normals(pmesh, vnm); - - PMP::random_perturbation(pmesh, rel_noise_magnitude * min_edge_length, CGAL::parameters::random_seed(0)); - PMP::interpolated_corrected_mean_curvature( - pmesh, - mean_curvature_map, - CGAL::parameters::ball_radius(rel_expansion_radius * max_edge_length).vertex_normal_map(vnm) - ); - PMP::interpolated_corrected_gaussian_curvature( - pmesh, - gaussian_curvature_map, - CGAL::parameters::ball_radius(rel_expansion_radius * max_edge_length).vertex_normal_map(vnm) - ); - } - else { - PMP::interpolated_corrected_mean_curvature( - pmesh, - mean_curvature_map, - CGAL::parameters::ball_radius(rel_expansion_radius * max_edge_length) - ); - PMP::interpolated_corrected_gaussian_curvature( - pmesh, - gaussian_curvature_map, - CGAL::parameters::ball_radius(rel_expansion_radius * max_edge_length) - ); - - } - - - - //PMP::interpolated_corrected_mean_curvature( - // pmesh, - // mean_curvature_map, - // CGAL::parameters::ball_radius(rel_expansion_radius * max_edge_length) - //); - //PMP::interpolated_corrected_gaussian_curvature( - // pmesh, - // gaussian_curvature_map, - // CGAL::parameters::ball_radius(rel_expansion_radius * max_edge_length) - //); - - - const EpicKernel::FT max_mean_curvature = *std::max_element(mean_curvature_map.begin(), mean_curvature_map.end()); - const EpicKernel::FT min_mean_curvature = *std::min_element(mean_curvature_map.begin(), mean_curvature_map.end()); - const EpicKernel::FT max_gaussian_curvature = *std::max_element(gaussian_curvature_map.begin(), gaussian_curvature_map.end()); - const EpicKernel::FT min_gaussian_curvature = *std::min_element(gaussian_curvature_map.begin(), gaussian_curvature_map.end()); - - std::cout << "# " << mesh_path << ":\n" - << "expansion radius ratio to max length / expansion radius = " << rel_expansion_radius << " / " << rel_expansion_radius * max_edge_length << ",\n" - << "max perturbation ratio to minlength / max perturbation = " << rel_noise_magnitude << " / " << rel_noise_magnitude * min_edge_length << "\n" - << "mean curvature: min = " << min_mean_curvature << " <-> " << max_mean_curvature << " = max" << "\n" - << "gaussian curvature: min = " << min_gaussian_curvature << " <-> " << max_gaussian_curvature << " = max" << "\n\n\n"; - - -} - -int main() -{ - const std::vector mesh_pathes_to_test = { - "meshes/icc_test/Sphere Quads + Tris.obj", - "meshes/icc_test/Sphere Quads + Tris 100352.obj", - "meshes/icc_test/Sphere Tris Ico.obj", - "meshes/icc_test/Sphere Tris Tet.obj", - "meshes/icc_test/Sphere Tris Oct.obj", - "meshes/icc_test/Sphere Quads.obj", - "meshes/icc_test/Sphere Quads Remesh.obj", - "meshes/icc_test/Sphere Ngons + Quads + Tris.obj", - "meshes/icc_test/Cube with fillet Quads.obj", - "meshes/cylinder.off", - "meshes/icc_test/Lantern Tris.obj", - "meshes/icc_test/Lantern Quads.obj" - }; - - const std::vector rel_expansion_radii = { 0, 0.1, 0.5, 1 }; - const std::vector rel_noise_magnitudes = { 0, 0.5, 0.9 }; - - for (auto mesh_path : mesh_pathes_to_test) { - for (EpicKernel::FT rel_expansion_radius : rel_expansion_radii) - for (EpicKernel::FT rel_noise_magnitude : rel_noise_magnitudes) - { - test(mesh_path, rel_expansion_radius, rel_noise_magnitude); - } - - std::cout << "_________________________________________________________________________________\n\n"; - } - -} diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_interpolated_corrected_curvatures.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_interpolated_corrected_curvatures.cpp new file mode 100644 index 00000000000..a4f43470332 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_interpolated_corrected_curvatures.cpp @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define ABS_ERROR 1e-6 + +namespace PMP = CGAL::Polygon_mesh_processing; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic_kernel; +typedef CGAL::Surface_mesh SMesh; +typedef CGAL::Polyhedron_3 Polyhedron; + +struct Average_test_info { + Epic_kernel::FT expansion_radius = -1; + Epic_kernel::FT mean_curvature_avg; + Epic_kernel::FT gaussian_curvature_avg; + Epic_kernel::FT principal_curvature_avg; + Epic_kernel::FT tolerance = 0.9; + + Average_test_info( + Epic_kernel::FT mean_curvature_avg, + Epic_kernel::FT gaussian_curvature_avg, + Epic_kernel::FT principal_curvature_avg, + Epic_kernel::FT expansion_radius = -1, + Epic_kernel::FT tolerance = 0.9 + ): + expansion_radius(expansion_radius), + mean_curvature_avg(mean_curvature_avg), + gaussian_curvature_avg(gaussian_curvature_avg), + principal_curvature_avg(principal_curvature_avg), + tolerance(tolerance) + { + } + +}; + +bool passes_comparison(Epic_kernel::FT result, Epic_kernel::FT expected, Epic_kernel::FT tolerance) +{ + if (abs(expected) < ABS_ERROR && abs(result) < ABS_ERROR) + return true; // expected 0, got 0 + else if (abs(expected) < ABS_ERROR) + return false; // expected 0, got non-0 + + return std::min(result, expected) / std::max(result, expected) > tolerance; +} + +template +void test_average_curvatures(std::string mesh_path, Average_test_info test_info){ + PolygonMesh pmesh; + const std::string filename = CGAL::data_file_path(mesh_path); + + if (!CGAL::IO::read_polygon_mesh(filename, pmesh) || faces(pmesh).size() == 0) + { + std::cerr << "Invalid input file." << std::endl; + } + + typedef boost::graph_traits::vertex_descriptor vertex_descriptor; + + boost::property_map>::type + mean_curvature_map = get(CGAL::dynamic_vertex_property_t(), pmesh), + gaussian_curvature_map = get(CGAL::dynamic_vertex_property_t(), pmesh); + boost::property_map>>::type + principal_curvatures_and_directions_map = get(CGAL::dynamic_vertex_property_t>(), pmesh); + + // test_info.expansion_radius -> test if no radius is provided by user. + if (test_info.expansion_radius < 0) { + PMP::interpolated_corrected_mean_curvature(pmesh, mean_curvature_map); + PMP::interpolated_corrected_gaussian_curvature(pmesh, gaussian_curvature_map); + PMP::interpolated_corrected_principal_curvatures_and_directions(pmesh, principal_curvatures_and_directions_map); + } + else { + PMP::interpolated_corrected_mean_curvature( + pmesh, + mean_curvature_map, + CGAL::parameters::ball_radius(test_info.expansion_radius) + ); + + PMP::interpolated_corrected_gaussian_curvature( + pmesh, + gaussian_curvature_map, + CGAL::parameters::ball_radius(test_info.expansion_radius) + ); + + PMP::interpolated_corrected_principal_curvatures_and_directions( + pmesh, + principal_curvatures_and_directions_map, + CGAL::parameters::ball_radius(test_info.expansion_radius) + ); + } + + Epic_kernel::FT mean_curvature_avg = 0, gaussian_curvature_avg = 0, principal_curvature_avg = 0; + + for (vertex_descriptor v : vertices(pmesh)) + { + mean_curvature_avg += get(mean_curvature_map, v); + gaussian_curvature_avg += get(gaussian_curvature_map, v); + principal_curvature_avg += get(principal_curvatures_and_directions_map, v).min_curvature + + get(principal_curvatures_and_directions_map, v).max_curvature; + } + + mean_curvature_avg /= vertices(pmesh).size(); + gaussian_curvature_avg /= vertices(pmesh).size(); + principal_curvature_avg /= vertices(pmesh).size() * 2; + + // are average curvatures equal to expected? + assert(passes_comparison(mean_curvature_avg, test_info.mean_curvature_avg, test_info.tolerance)); + assert(passes_comparison(gaussian_curvature_avg, test_info.gaussian_curvature_avg, test_info.tolerance)); + assert(passes_comparison(principal_curvature_avg, test_info.principal_curvature_avg, test_info.tolerance)); + + PMP::interpolated_corrected_curvatures( + pmesh, + CGAL::parameters::ball_radius(test_info.expansion_radius) + .vertex_mean_curvature_map(mean_curvature_map) + .vertex_gaussian_curvature_map(gaussian_curvature_map) + .vertex_principal_curvatures_and_directions_map(principal_curvatures_and_directions_map) + ); + + // are average curvatures computed from interpolated_corrected_curvatures() equal to average curvatures each computed on its own? + Epic_kernel::FT new_mean_curvature_avg = 0, new_gaussian_curvature_avg = 0, new_principal_curvature_avg = 0; + + for (vertex_descriptor v : vertices(pmesh)) + { + new_mean_curvature_avg += get(mean_curvature_map, v); + new_gaussian_curvature_avg += get(gaussian_curvature_map, v); + new_principal_curvature_avg += get(principal_curvatures_and_directions_map, v).min_curvature + + get(principal_curvatures_and_directions_map, v).max_curvature; + } + + new_mean_curvature_avg /= vertices(pmesh).size(); + new_gaussian_curvature_avg /= vertices(pmesh).size(); + new_principal_curvature_avg /= vertices(pmesh).size() * 2; + + assert(passes_comparison(mean_curvature_avg, new_mean_curvature_avg, 0.99)); + assert(passes_comparison(gaussian_curvature_avg, new_gaussian_curvature_avg, 0.99)); + assert(passes_comparison(principal_curvature_avg, new_principal_curvature_avg, 0.99)); +} + +int main() +{ + // testing on a simple sphere(r = 0.5), on both Polyhedron & SurfaceMesh: + // Expected: Mean Curvature = 2, Gaussian Curvature = 4, Principal Curvatures = 2 & 2 so 2 on avg. + test_average_curvatures("meshes/sphere.off", Average_test_info(2,4,2)); + test_average_curvatures("meshes/sphere.off", Average_test_info(2, 4, 2)); + + // Same mesh but with specified expansion radii of 0 and 0.25 (half radius of sphere) + test_average_curvatures("meshes/sphere.off", Average_test_info(2, 4, 2, 0)); + test_average_curvatures("meshes/sphere.off", Average_test_info(2, 4, 2, 0.25)); + + // testing on a simple sphere(r = 10), on both Polyhedron & SurfaceMesh: + // Expected: Mean Curvature = 0.1, Gaussian Curvature = 0.01, Principal Curvatures = 0.1 & 0.1 so 0.1 on avg. + test_average_curvatures("meshes/sphere966.off", Average_test_info(0.1, 0.01, 0.1)); + test_average_curvatures("meshes/sphere966.off", Average_test_info(0.1, 0.01, 0.1)); + + // Same mesh but with specified expansion radii of 0 and 5 (half radius of sphere) + test_average_curvatures("meshes/sphere966.off", Average_test_info(0.1, 0.01, 0.1, 0)); + test_average_curvatures("meshes/sphere966.off", Average_test_info(0.1, 0.01, 0.1, 5)); + + // testing on a simple half cylinder(r = 1), on both Polyhedron & SurfaceMesh: + // Expected: Mean Curvature = 0.5, Gaussian Curvature = 0, Principal Curvatures = 0 & 1 so 0.5 on avg. + test_average_curvatures("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5)); + test_average_curvatures("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5)); + + // Same mesh but with specified expansion radii of 0 and 0.5 (half radius of cylinder) + test_average_curvatures("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0)); + test_average_curvatures("meshes/cylinder.off", Average_test_info(0.5, 0, 0.5, 0.5)); + + + +}