mirror of https://github.com/CGAL/cgal
detect self-intersecting inputs
This commit is contained in:
parent
d1c8d2ea10
commit
c5b94332fd
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue