detect self-intersecting inputs

This commit is contained in:
Laurent Rineau 2024-11-20 15:26:47 +01:00
parent d1c8d2ea10
commit c5b94332fd
3 changed files with 142 additions and 8 deletions

View File

@ -14,13 +14,18 @@
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/boost/graph/IO/OFF.h>
#include <CGAL/IO/polygon_soup_io.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
#include <ostream>
#include <sstream>
#include <tl/expected.hpp>
namespace CGAL {
@ -57,6 +62,110 @@ namespace CGAL {
CGAL::IO::write_OFF(out, mesh);
}
template <typename PolygonMesh>
struct CDT_3_read_polygon_mesh_output {
tl::expected<PolygonMesh, std::string> 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 <typename PolygonMesh, typename NamedParameters = parameters::Default_named_parameters>
CDT_3_read_polygon_mesh_output<PolygonMesh>
read_polygon_mesh_for_cdt_3(const std::string &fname,
const NamedParameters &np = parameters::default_values())
{
CDT_3_read_polygon_mesh_output<PolygonMesh> result;
namespace PMP = CGAL::Polygon_mesh_processing;
namespace PMP_internal = PMP::internal;
using VPM = typename CGAL::GetVertexPointMap<PolygonMesh, NamedParameters>::type;
using Point = typename boost::property_traits<VPM>::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<Point>;
using Face = std::vector<std::size_t>;
using Faces = std::vector<Face>;
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<Points, Faces, NamedParameters>::type;
auto traits = choose_parameter<Traits>(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<Faces, Deleter>;
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

View File

@ -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")

View File

@ -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();
auto read_options = CGAL::parameters::repair_polygon_soup(options.repair_mesh).verbose(options.verbose);
auto result = CGAL::read_polygon_mesh_for_cdt_3<Mesh>(options.input_filename, read_options);
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) {
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::milliseconds>(
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);