diff --git a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/cdt_debug_io.h b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/cdt_debug_io.h index 95840160ef8..1a754e83bd0 100644 --- a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/cdt_debug_io.h +++ b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/cdt_debug_io.h @@ -14,13 +14,18 @@ #include -#include +#include #include #include #include #include +#include +#include #include +#include + +#include namespace CGAL { @@ -57,6 +62,110 @@ namespace CGAL { CGAL::IO::write_OFF(out, mesh); } + template + struct CDT_3_read_polygon_mesh_output { + tl::expected polygon_mesh; + + std::size_t nb_of_duplicated_points = 0; + std::size_t nb_of_simplified_polygons = 0; + std::size_t nb_of_new_polygons = 0; + std::size_t nb_of_removed_invalid_polygonss = 0; + std::size_t nb_of_removed_duplicated_polygons = 0; + std::size_t nb_of_removed_isolated_poiints = 0; + + bool polygon_soup_self_intersects = false; + bool polygon_mesh_is_manifold = true; + }; + + template + CDT_3_read_polygon_mesh_output + read_polygon_mesh_for_cdt_3(const std::string &fname, + const NamedParameters &np = parameters::default_values()) + { + CDT_3_read_polygon_mesh_output result; + + namespace PMP = CGAL::Polygon_mesh_processing; + namespace PMP_internal = PMP::internal; + + using VPM = typename CGAL::GetVertexPointMap::type; + using Point = typename boost::property_traits::value_type; + + using parameters::choose_parameter; + using parameters::get_parameter; + + auto verbose = choose_parameter(get_parameter(np, internal_np::verbose), false); + + std::ostringstream local_verbose_output; + auto *cerr_buff = std::cerr.rdbuf(); + std::cerr.rdbuf(local_verbose_output.rdbuf()); + auto restore_cerr = make_scope_exit([&] + { std::cerr.rdbuf(cerr_buff); }); + + auto return_error = [&]() { + result.polygon_mesh = tl::unexpected(std::move(local_verbose_output).str()); + return result; + }; + + using Points = std::vector; + using Face = std::vector; + using Faces = std::vector; + Points points; + Faces faces; + if (!CGAL::IO::read_polygon_soup(fname, points, faces, CGAL::parameters::verbose(true))) + { + if (verbose) + std::cerr << "Warning: cannot read polygon soup" << std::endl; + return return_error(); + } + using Traits = typename PMP_internal::GetPolygonGeomTraits::type; + + auto traits = choose_parameter(get_parameter(np, internal_np::geom_traits)); + + bool do_repair = choose_parameter(get_parameter(np, internal_np::repair_polygon_soup), true); + if (do_repair) + { + result.nb_of_duplicated_points = PMP::merge_duplicate_points_in_polygon_soup(points, faces, np); + result.nb_of_simplified_polygons = PMP_internal::simplify_polygons_in_polygon_soup(points, faces, traits); + result.nb_of_new_polygons = PMP_internal::split_pinched_polygons_in_polygon_soup(points, faces, traits); + result.nb_of_removed_invalid_polygonss = PMP_internal::remove_invalid_polygons_in_polygon_soup(points, faces); + result.nb_of_removed_duplicated_polygons = PMP::merge_duplicate_polygons_in_polygon_soup(points, faces, np); + result.nb_of_removed_isolated_poiints = PMP::remove_isolated_points_in_polygon_soup(points, faces); + } + + // check if the polygon soup is pure triangles, and create a triangulated copy otherwise + bool is_pure_triangles = std::all_of(faces.begin(), faces.end(), [](const Face &f) { return f.size() == 3; }); + + // create a non-deleting pointer to `faces` (with a null deleter) + using Deleter_function = void(Faces*); + using Deleter = Deleter_function*; + using Ptr = std::unique_ptr; + Ptr triangle_faces_ptr{&faces, +[](Faces *) {}}; + if (!is_pure_triangles) + { + triangle_faces_ptr = Ptr(new Faces(faces), +[](Faces* vector){ delete vector; }); // copy `faces` + PMP::triangulate_polygons(points, *triangle_faces_ptr, np); + } + + result.polygon_soup_self_intersects = PMP::does_triangle_soup_self_intersect(points, *triangle_faces_ptr, np); + + if (!PMP::orient_polygon_soup(points, faces)) + { + result.polygon_mesh_is_manifold = false; + if (verbose) + std::cerr << "Some duplication happened during polygon soup orientation" << std::endl; + } + + if (!PMP::is_polygon_soup_a_polygon_mesh(faces)) + { + if (verbose) + std::cerr << "Warning: polygon soup does not describe a polygon mesh" << std::endl; + return return_error(); + } + PMP::polygon_soup_to_polygon_mesh(points, faces, *result.polygon_mesh, parameters::default_values(), np); + + return result; + } + } // end namespace CGAL #endif // CGAL_CDT_3_DEBUG_IO_H diff --git a/Triangulation_3/test/Triangulation_3/CMakeLists.txt b/Triangulation_3/test/Triangulation_3/CMakeLists.txt index 1776b5046a3..cc98a52d987 100644 --- a/Triangulation_3/test/Triangulation_3/CMakeLists.txt +++ b/Triangulation_3/test/Triangulation_3/CMakeLists.txt @@ -1,7 +1,7 @@ # Created by the script cgal_create_cmake_script # This is the CMake script for compiling a CGAL application. -cmake_minimum_required(VERSION 3.12...3.29) +cmake_minimum_required(VERSION 3.24...3.30) project(Triangulation_3_Tests) find_package(CGAL REQUIRED) @@ -13,6 +13,19 @@ include(CGAL_TBB_support) include_directories(BEFORE "include") +include(FetchContent) + +FetchContent_Declare( + tl-expected + GIT_REPOSITORY https://github.com/TartanLlama/expected.git + GIT_TAG 292eff8bd8ee230a7df1d6a1c00c4ea0eb2f0362 # https://github.com/TartanLlama/expected/releases/tag/v1.1.0 + FIND_PACKAGE_ARGS CONFIG REQUIRED +) + +set(EXPECTED_ENABLE_TESTS FALSE CACHE BOOL "Disable tests for tl::expected") + +FetchContent_MakeAvailable(tl-expected) + create_single_source_cgal_program("test_delaunay_3.cpp") create_single_source_cgal_program("test_delaunay_hierarchy_3.cpp") create_single_source_cgal_program("test_delaunay_hierarchy_3_old.cpp") diff --git a/Triangulation_3/test/Triangulation_3/cdt_3_from_off.cpp b/Triangulation_3/test/Triangulation_3/cdt_3_from_off.cpp index d5801e6fb43..8b5bac6883d 100644 --- a/Triangulation_3/test/Triangulation_3/cdt_3_from_off.cpp +++ b/Triangulation_3/test/Triangulation_3/cdt_3_from_off.cpp @@ -254,21 +254,33 @@ int main(int argc, char* argv[]) } CGAL_CDT_3_TASK_BEGIN(read_input_task_handle); auto start_time = std::chrono::high_resolution_clock::now(); - - Mesh mesh; - const bool ok = options.repair_mesh - ? CGAL::Polygon_mesh_processing::IO::read_polygon_mesh(options.input_filename, mesh) - : CGAL::IO::read_polygon_mesh(options.input_filename, mesh); - if(!ok) { + auto read_options = CGAL::parameters::repair_polygon_soup(options.repair_mesh).verbose(options.verbose); + auto result = CGAL::read_polygon_mesh_for_cdt_3(options.input_filename, read_options); + + if (!result.polygon_mesh) + { std::cerr << "Not a valid input file." << std::endl; + std::cerr << "Details:\n" << result.polygon_mesh.error() << std::endl; return 1; } + Mesh mesh = std::move(*result.polygon_mesh); if(!options.quiet) { std::cout << "[timings] read mesh in " << std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; std::cout << "Number of vertices: " << mesh.number_of_vertices() << '\n'; std::cout << "Number of edges: " << mesh.number_of_edges() << '\n'; std::cout << "Number of faces: " << mesh.number_of_faces() << "\n\n"; + + std::cout << "Processing was successful.\n"; + std::cout << " Number of duplicated points: " << result.nb_of_duplicated_points << '\n'; + std::cout << " Number of simplified polygons: " << result.nb_of_simplified_polygons << '\n'; + std::cout << " Number of new polygons: " << result.nb_of_new_polygons << '\n'; + std::cout << " Number of removed invalid polygons: " << result.nb_of_removed_invalid_polygonss << '\n'; + std::cout << " Number of removed duplicated polygons: " << result.nb_of_removed_duplicated_polygons << '\n'; + std::cout << " Number of removed isolated points: " << result.nb_of_removed_isolated_poiints << '\n'; + std::cout << " Polygon soup self-intersects: " << (result.polygon_soup_self_intersects ? "YES" : "no") << '\n'; + std::cout << " Polygon mesh is manifold: " << (result.polygon_mesh_is_manifold ? "yes" : "NO") << '\n'; + std::cout << std::endl; } CGAL_CDT_3_TASK_END(read_input_task_handle);