From 2224f9a31df208ee259948450afd89a31023d634 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 23 Mar 2026 11:49:55 +0100 Subject: [PATCH 001/151] display stats about Steiner point types + cosmetic changes about debug message, or "typedefs" --- ...ing_constrained_Delaunay_triangulation_3.h | 62 +++++++++++++------ ...ned_Delaunay_triangulation_vertex_data_3.h | 2 +- .../cdt_3_from_off.cpp | 2 +- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index b38e41414595..38071fbd47a0 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -13,6 +13,7 @@ #define CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H #include +#include #include #include @@ -583,20 +584,19 @@ struct Output_rep, With_point_and_info_t } std::ostream& operator()(std::ostream& out) const { - out << Time_stamper::display_id(this->it.operator->(), offset); - if(this->it.operator->() != nullptr) { - out << (this->it->ccdt_3_data().is_Steiner_vertex_on_edge() ? "(Steiner)" : "") - << (this->it->ccdt_3_data().is_Steiner_vertex_in_face() ? "(Steiner in face)" : "") - << "= " << this->it->point(); - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_BORDER)) out << " (region border)"; - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_INSIDE)) out << " (inside region)"; - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY)) out << " (cavity vertex)"; - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY_ABOVE)) out << " (vertex above)"; - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY_BELOW)) out << " (vertex below)"; - return out; - } - else - return out; + auto v_ptr = this->it.operator->(); + out << Time_stamper::display_id(v_ptr, offset); + if(v_ptr == nullptr) return out; + + out << (v_ptr->ccdt_3_data().is_Steiner_vertex_on_edge() ? "(Steiner)" : "") + << (v_ptr->ccdt_3_data().is_Steiner_vertex_in_face() ? "(Steiner in face)" : "") + << "= " << v_ptr->point(); + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_BORDER)) out << " (region border)"; + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_INSIDE)) out << " (inside region)"; + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY)) out << " (cavity vertex)"; + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY_ABOVE)) out << " (vertex above)"; + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY_BELOW)) out << " (vertex below)"; + return out; } }; @@ -646,6 +646,8 @@ class Conforming_constrained_Delaunay_triangulation_3 public: using Vertex_handle = typename Triangulation::Vertex_handle; + using Cell_handle = typename Triangulation::Cell_handle; + using Facet = typename Triangulation::Facet; #ifndef DOXYGEN_RUNNING using Constrained_polyline_id = typename CDT_3_impl::Constrained_polyline_id; @@ -1085,22 +1087,22 @@ class Conforming_constrained_Delaunay_triangulation_3 * \link TriangulationDataStructure_3::Facet `Triangulation::Facet`\endlink, * as defined by its triangulation data structure. */ - bool is_facet_constrained(const typename Triangulation::Facet& f) const { + bool is_facet_constrained(const Facet& f) const { return cdt_impl.is_facet_constrained(f); } /*! * \brief same as `is_facet_constrained(f)` with `f` being `Triangulation::Facet(ch, index)`. */ - bool is_facet_constrained(typename Triangulation::Cell_handle ch, int index) const { - return cdt_impl.is_facet_constrained(typename Triangulation::Facet(ch, index)); + bool is_facet_constrained(Cell_handle ch, int index) const { + return cdt_impl.is_facet_constrained(Facet(ch, index)); } /*! * @brief same as `face_constraint_index(f)` with `f` being `Triangulation::Facet(ch, index)`. * @pre \link Conforming_constrained_Delaunay_triangulation_3::is_facet_constrained `is_facet_constrained`\endlink(`f`) */ - CDT_3_signed_index face_constraint_index(typename Triangulation::Cell_handle ch, int i) const + CDT_3_signed_index face_constraint_index(Cell_handle ch, int i) const { return ch->ccdt_3_data().face_constraint_index(i); } @@ -1110,7 +1112,7 @@ class Conforming_constrained_Delaunay_triangulation_3 * facet \p f * @pre \link Conforming_constrained_Delaunay_triangulation_3::is_facet_constrained `is_facet_constrained`\endlink(`f`) */ - CDT_3_signed_index face_constraint_index(const typename Triangulation::Facet& f) const + CDT_3_signed_index face_constraint_index(const Facet& f) const { return face_constraint_index(f.first, f.second); } @@ -1206,6 +1208,28 @@ class Conforming_constrained_Delaunay_triangulation_3 void write_facets(std::ostream& out, const CDT& cdt, FacetRange&& facets) const { cdt_impl.write_facets(out, cdt, std::forward(facets)); } + + auto statistics(std::string prefix = "") const { + using Stats_array = std::array(CDT_3_vertex_type::nb_of_types)>; + return IO::oformat([this, prefix=std::move(prefix)](std::ostream& out) -> auto& { + out << prefix << "Number of vertices: " << triangulation().number_of_vertices() << "\n"; + out << prefix << "Number of cells: " << triangulation().number_of_cells() << "\n"; + + auto nb = [nb_vertices_by_type = Stats_array{}](CDT_3_vertex_type type) mutable -> auto& { + return nb_vertices_by_type[static_cast(type)]; + }; + + for(auto v: triangulation().finite_vertex_handles()) { + ++nb(v->ccdt_3_data().vertex_type()); + } + out << prefix << "Number of vertices by type:\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::INPUT_VERTEX) << " input vertices\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_ON_EDGE) << " Steiner vertices on edges\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_IN_FACE) << " Steiner vertices in faces\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::FREE) << " free vertices\n"; + return out; + }, IO_manip_tag{}); + } }; #ifndef DOXYGEN_RUNNING diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h index e1e716be8c84..1918ae29540d 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h @@ -35,7 +35,7 @@ namespace CGAL { struct Conforming_constrained_Delaunay_triangulation_vertex_data_3 {}; #else // DOXYGEN_RUNNING -enum class CDT_3_vertex_type { FREE, CORNER, INPUT_VERTEX = CORNER, STEINER_ON_EDGE, STEINER_IN_FACE }; +enum class CDT_3_vertex_type { FREE, CORNER, INPUT_VERTEX = CORNER, STEINER_ON_EDGE, STEINER_IN_FACE, nb_of_types }; enum class CDT_3_vertex_marker { CLEAR = 0, diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 5af86c0688ac..97cf41605870 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -714,7 +714,7 @@ int go(Mesh mesh, CDT_options options) { auto timing = std::chrono::high_resolution_clock::now() - build_start; std::cout << "[timings] built CDT from mesh in " << std::chrono::duration_cast(timing).count() << " ms\n"; - std::cout << "Number of vertices: " << cdt.number_of_vertices() << "\n\n"; + std::cout << cdt.statistics() << "\n\n"; } // Validation: check conforming status and validity From 1a76816f2992f156e88315b10548b549320ab645 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 24 Mar 2026 17:04:24 +0100 Subject: [PATCH 002/151] rewrite of CGAL::CDT_3::Debug_options::Flags --- .../Conforming_Delaunay_triangulation_3.h | 89 ++++++++++--------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 514d452402fa..e9fbe1b906f9 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -72,7 +72,10 @@ namespace CGAL { namespace CDT_3 { struct Debug_options { - enum class Flags { + enum class Flag; + bool get(Flag f) const { return flags[static_cast(f)]; } + auto get(Flag f) { return flags[static_cast(f)]; } + enum class Flag { Steiner_points = 0, Steiner_points_construction, conforming, @@ -96,66 +99,68 @@ struct Debug_options { use_epeck_for_Steiner_points, nb_of_flags }; - bool Steiner_points_construction() const { return flags[static_cast(Flags::Steiner_points_construction)]; } - void Steiner_points_construction(bool b) { flags.set(static_cast(Flags::Steiner_points_construction), b); } - bool Steiner_points() const { return flags[static_cast(Flags::Steiner_points)]; } - void Steiner_points(bool b) { flags.set(static_cast(Flags::Steiner_points), b); } + using Flags = std::bitset(Flag::nb_of_flags)>; - bool input_faces() const { return flags[static_cast(Flags::input_faces)]; } - void input_faces(bool b) { flags.set(static_cast(Flags::input_faces), b); } + bool Steiner_points_construction() const { return get(Flag::Steiner_points_construction); } + void Steiner_points_construction(bool b) { get(Flag::Steiner_points_construction) = b; } - bool missing_region() const { return flags[static_cast(Flags::missing_region)]; } - void missing_region(bool b) { flags.set(static_cast(Flags::missing_region), b); } + bool Steiner_points() const { return get(Flag::Steiner_points); } + void Steiner_points(bool b) { get(Flag::Steiner_points) = b; } - bool regions() const { return flags[static_cast(Flags::regions)]; } - void regions(bool b) { flags.set(static_cast(Flags::regions), b); } + bool input_faces() const { return get(Flag::input_faces); } + void input_faces(bool b) { get(Flag::input_faces) = b; } - bool copy_triangulation_into_hole() const { return flags[static_cast(Flags::copy_triangulation_into_hole)]; } - void copy_triangulation_into_hole(bool b) { flags.set(static_cast(Flags::copy_triangulation_into_hole), b); } + bool missing_region() const { return get(Flag::missing_region); } + void missing_region(bool b) { get(Flag::missing_region) = b; } - bool validity() const { return flags[static_cast(Flags::validity)]; } - void validity(bool b) { flags.set(static_cast(Flags::validity), b); } + bool regions() const { return get(Flag::regions); } + void regions(bool b) { get(Flag::regions) = b; } + bool copy_triangulation_into_hole() const { return get(Flag::copy_triangulation_into_hole); } + void copy_triangulation_into_hole(bool b) { get(Flag::copy_triangulation_into_hole) = b; } - bool finite_edges_map() const { return flags[static_cast(Flags::debug_finite_edges_map)]; } - void finite_edges_map(bool b) { flags.set(static_cast(Flags::debug_finite_edges_map), b); } + bool validity() const { return get(Flag::validity); } + void validity(bool b) { get(Flag::validity) = b; } - bool subconstraints_to_conform() const { return flags[static_cast(Flags::debug_subconstraints_to_conform)]; } - void subconstraints_to_conform(bool b) { flags.set(static_cast(Flags::debug_subconstraints_to_conform), b); } + bool finite_edges_map() const { return get(Flag::debug_finite_edges_map); } + void finite_edges_map(bool b) { get(Flag::debug_finite_edges_map) = b; } - bool use_finite_edges_map_flag() const { return flags[static_cast(Flags::use_finite_edges_map)]; } - void use_finite_edges_map(bool b) { flags.set(static_cast(Flags::use_finite_edges_map), b); } + bool subconstraints_to_conform() const { return get(Flag::debug_subconstraints_to_conform); } + void subconstraints_to_conform(bool b) { get(Flag::debug_subconstraints_to_conform) = b; } - bool verbose_special_cases() const { return flags[static_cast(Flags::verbose_special_cases)]; } - void verbose_special_cases(bool b) { flags.set(static_cast(Flags::verbose_special_cases), b); } + bool use_finite_edges_map_flag() const { return get(Flag::use_finite_edges_map); } + void use_finite_edges_map(bool b) { get(Flag::use_finite_edges_map) = b; } - bool encroaching_vertices() const { return flags[static_cast(Flags::debug_encroaching_vertices)]; } - void encroaching_vertices(bool b) { flags.set(static_cast(Flags::debug_encroaching_vertices), b); } + bool verbose_special_cases() const { return get(Flag::verbose_special_cases); } + void verbose_special_cases(bool b) { get(Flag::verbose_special_cases) = b; } - bool conforming_validation() const { return flags[static_cast(Flags::debug_conforming_validation)]; } - void conforming_validation(bool b) { flags.set(static_cast(Flags::debug_conforming_validation), b); } + bool encroaching_vertices() const { return get(Flag::debug_encroaching_vertices); } + void encroaching_vertices(bool b) { get(Flag::debug_encroaching_vertices) = b; } - bool constraint_hierarchy() const { return flags[static_cast(Flags::debug_constraint_hierarchy)]; } - void constraint_hierarchy(bool b) { flags.set(static_cast(Flags::debug_constraint_hierarchy), b); } + bool conforming_validation() const { return get(Flag::debug_conforming_validation); } + void conforming_validation(bool b) { get(Flag::debug_conforming_validation) = b; } - bool geometric_errors() const { return flags[static_cast(Flags::debug_geometric_errors)]; } - void geometric_errors(bool b) { flags.set(static_cast(Flags::debug_geometric_errors), b); } + bool constraint_hierarchy() const { return get(Flag::debug_constraint_hierarchy); } + void constraint_hierarchy(bool b) { get(Flag::debug_constraint_hierarchy) = b; } - bool polygon_insertion() const { return flags[static_cast(Flags::debug_polygon_insertion)]; } - void polygon_insertion(bool b) { flags.set(static_cast(Flags::debug_polygon_insertion), b); } + bool geometric_errors() const { return get(Flag::debug_geometric_errors); } + void geometric_errors(bool b) { get(Flag::debug_geometric_errors) = b; } - bool restore_faces() const { return flags[static_cast(Flags::debug_restore_faces)]; } - void restore_faces(bool b) { flags.set(static_cast(Flags::debug_restore_faces), b); } + bool polygon_insertion() const { return get(Flag::debug_polygon_insertion); } + void polygon_insertion(bool b) { get(Flag::debug_polygon_insertion) = b; } - bool display_statistics() const { return flags[static_cast(Flags::display_statistics)]; } - void display_statistics(bool b) { flags.set(static_cast(Flags::display_statistics), b); } + bool restore_faces() const { return get(Flag::debug_restore_faces); } + void restore_faces(bool b) { get(Flag::debug_restore_faces) = b; } - bool use_epeck_for_normals() const { return flags[static_cast(Flags::use_epeck_for_normals)]; } - void use_epeck_for_normals(bool b) { flags.set(static_cast(Flags::use_epeck_for_normals), b); } + bool display_statistics() const { return get(Flag::display_statistics); } + void display_statistics(bool b) { get(Flag::display_statistics) = b; } - bool use_epeck_for_Steiner_points() const { return flags[static_cast(Flags::use_epeck_for_Steiner_points)]; } - void use_epeck_for_Steiner_points(bool b) { flags.set(static_cast(Flags::use_epeck_for_Steiner_points), b); } + bool use_epeck_for_normals() const { return get(Flag::use_epeck_for_normals); } + void use_epeck_for_normals(bool b) { get(Flag::use_epeck_for_normals) = b; } + + bool use_epeck_for_Steiner_points() const { return get(Flag::use_epeck_for_Steiner_points); } + void use_epeck_for_Steiner_points(bool b) { get(Flag::use_epeck_for_Steiner_points) = b; } double segment_vertex_epsilon() const { return segment_vertex_epsilon_; } void set_segment_vertex_epsilon(double eps) { segment_vertex_epsilon_ = eps; } @@ -164,7 +169,7 @@ struct Debug_options { void set_vertex_vertex_epsilon(double eps) { vertex_vertex_epsilon_ = eps; } private: - std::bitset(Flags::nb_of_flags)> flags{}; + Flags flags{}; double segment_vertex_epsilon_ = 0.0; double vertex_vertex_epsilon_ = 0.0; }; // end struct Debug_options From ece4e564831dbab0c20c62f2aa51e0ca090239f2 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 25 Mar 2026 15:19:45 +0100 Subject: [PATCH 003/151] cosmetic changes for cdt_3_from_off.cpp - do not try to support C++20 modules (to speed up compilations) - factorize the uses of `std::chrono::*` in dedicated functions with shorter names - add a namespace in the `.cpp` file, to avoid conflicts with any C-header (in my case it was the identifier `clock`, defined as a function by ``) --- .../CMakeLists.txt | 2 + .../cdt_3_from_off.cpp | 60 ++++++++++++------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index 83e6440f3f41..78fd80e29d5e 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -10,6 +10,8 @@ create_single_source_cgal_program(test_ccdt_remeshing.cpp) find_package(Eigen3 QUIET) include(CGAL_Eigen3_support) +set(CMAKE_CXX_SCAN_FOR_MODULES FALSE) + # CDT_3: C++20 for structured bindings add_library(CDT_3_dependencies INTERFACE) target_compile_features(CDT_3_dependencies INTERFACE cxx_std_20) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 97cf41605870..425b49ee7e86 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -62,20 +64,19 @@ #endif #if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L +# define CGAL_CDT_3_CAN_USE_CXX20_RANGES 1 # include #endif -#if CGAL_CDT_3_USE_EPECK - -#include - -using K = CGAL::Exact_predicates_exact_constructions_kernel; - -#else // use Epick +#ifndef CGAL_CDT_3_USE_EPECK +# define CGAL_CDT_3_USE_EPECK 0 +#endif -using K = CGAL::Exact_predicates_inexact_constructions_kernel; +namespace CGAL::CDT_3::test::cdt_3_from_off { -#endif // use Epick +using K = std::conditional_t; using CDT = CGAL::Conforming_constrained_Delaunay_triangulation_3; using Point = K::Point_3; @@ -86,6 +87,18 @@ using vertex_descriptor = boost::graph_traits::vertex_descriptor; using edge_descriptor = boost::graph_traits::edge_descriptor; using face_descriptor = boost::graph_traits::face_descriptor; +template +auto milliseconds(std::chrono::duration duration) { + return std::chrono::duration_cast(duration).count(); +}; + +template +auto milliseconds_since(std::chrono::time_point start_time) { + return milliseconds(Clock::now() - start_time); +}; + +using clock = std::chrono::high_resolution_clock; + void help(std::ostream& out) { out << R"!!!!!( Usage: cdt_3_from_off [options] input.off output.off @@ -420,11 +433,11 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& { std::ofstream dump(options.output_filename); dump.precision(17); -#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L +#if CGAL_CDT_3_CAN_USE_CXX20_RANGES cdt.write_facets(dump, cdt.triangulation(), std::views::filter(cdt.finite_facets(), [&](auto f) { return cdt.is_facet_constrained(f); })); -#else +#else // If C++20 ranges are not available, we use boost::make_filter_iterator to filter the facets. auto is_facet_constrained = [&](auto f) { return cdt.is_facet_constrained(f); }; const auto& tr = cdt.triangulation(); auto it_begin = tr.finite_facets_begin(); @@ -432,7 +445,7 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& auto filtered_it_begin = boost::make_filter_iterator(is_facet_constrained,it_begin, it_end); auto filtered_it_end = boost::make_filter_iterator(is_facet_constrained,it_end, it_end); cdt.write_facets(dump, tr, CGAL::make_range(filtered_it_begin, filtered_it_end)); -#endif +#endif // CGAL_CDT_3_CAN_USE_CXX20_RANGES } }; } @@ -585,7 +598,7 @@ Borders_of_patches maybe_merge_facets( { auto _ = CGAL::CDT_3_MERGE_FACETS_TASK_guard(); - auto start_time = std::chrono::high_resolution_clock::now(); + auto start_time = clock::now(); if(options.merge_facets_old_method) { number_of_patches = merge_facets_old_method(mesh, pmaps, number_of_patches); @@ -596,9 +609,8 @@ Borders_of_patches maybe_merge_facets( } patch_edges = extract_patch_edges(mesh, pmaps, number_of_patches); if (!options.quiet) { - auto timing = std::chrono::high_resolution_clock::now() - start_time; std::cout << "[timings] found " << number_of_patches << " patches in " - << std::chrono::duration_cast(timing).count() << " ms\n"; + << milliseconds_since(start_time) << " ms\n"; } } @@ -705,15 +717,14 @@ int go(Mesh mesh, CDT_options options) { // Build CDT directly from the polygon mesh using the official API CDT cdt; auto output_on_exit_scope_guard = CGAL::make_scope_exit(create_output_finalizer(cdt, options)); - auto build_start = std::chrono::high_resolution_clock::now(); + auto build_start = clock::now(); cdt = CDT{std::invoke([&]() { auto np = CGAL::parameters::debug(cdt_debug).visitor(std::ref(dump_mesh_with_steiner_points)); return options.merge_facets ? CDT(mesh, np.plc_face_id(pmaps.patch_id_map)) : CDT(mesh, np); })}; if(!options.quiet) { - auto timing = std::chrono::high_resolution_clock::now() - build_start; std::cout << "[timings] built CDT from mesh in " - << std::chrono::duration_cast(timing).count() << " ms\n"; + << milliseconds_since(build_start) << " ms\n"; std::cout << cdt.statistics() << "\n\n"; } @@ -782,7 +793,10 @@ int bisect_errors(Mesh mesh, CDT_options options) { return CGAL::bisect_failures(mesh, get_size, simplify, run_test, save_mesh); } +} // end of namespaces CGAL::CDT_3::test::cdt_3_from_off + int main(int argc, char* argv[]) { + using namespace CGAL::CDT_3::test::cdt_3_from_off; std::cerr.precision(17); std::cout.precision(17); CGAL::get_default_random() = CGAL::Random(42); @@ -794,7 +808,7 @@ int main(int argc, char* argv[]) { } CGAL::CDT_3_read_polygon_mesh_output read_mesh_result; - auto start_time = std::chrono::high_resolution_clock::now(); + auto start_time = clock::now(); { auto _ = CGAL::CDT_3_READ_INPUT_TASK_guard(); @@ -821,8 +835,9 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } 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 << "[timings] read mesh \"" << options.input_filename << "\" in " + << milliseconds_since(start_time) + << " ms\n"; std::cout << "Number of vertices: " << read_mesh_result.polygon_mesh->number_of_vertices() << '\n'; std::cout << "Number of edges: " << read_mesh_result.polygon_mesh->number_of_edges() << '\n'; std::cout << "Number of faces: " << read_mesh_result.polygon_mesh->number_of_faces() << "\n\n"; @@ -853,8 +868,7 @@ int main(int argc, char* argv[]) { auto exit_code = go(std::move(*read_mesh_result.polygon_mesh), options); if(!options.quiet) { - std::cout << "[timings] total time: " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; + std::cout << "[timings] total time: " << milliseconds_since(start_time) << " ms\n"; if(exit_code != 0) std::cout << "ERROR with exit code " << exit_code << '\n'; } return exit_code; From 1f5f69c2c87a3b22e2dcd37cc81ed31a6009f966 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 27 Mar 2026 16:32:45 +0100 Subject: [PATCH 004/151] improve CGAL/IO/IO.h: add Output_rep for tuple-like types --- .../include/CGAL/Compact_container.h | 13 +- Stream_support/include/CGAL/IO/io.h | 470 ++++++++++-------- Stream_support/include/CGAL/IO/io_tags.h | 2 +- 3 files changed, 261 insertions(+), 224 deletions(-) diff --git a/STL_Extension/include/CGAL/Compact_container.h b/STL_Extension/include/CGAL/Compact_container.h index 1beb7796c328..ec04664d7ffb 100644 --- a/STL_Extension/include/CGAL/Compact_container.h +++ b/STL_Extension/include/CGAL/Compact_container.h @@ -1220,17 +1220,18 @@ namespace handle { } // namespace internal -template -class Output_rep > { +template +class Output_rep, Format_tag> { protected: using CC_iterator = CGAL::internal::CC_iterator; using Compact_container = typename CC_iterator::CC; using Time_stamper = typename Compact_container::Time_stamper; CC_iterator it; + CGAL_NO_UNIQUE_ADDRESS Format_tag format; public: - Output_rep( const CC_iterator it) : it(it) {} + Output_rep(const CC_iterator& it, Format_tag format = {}) : it(it), format(std::move(format)) {} std::ostream& operator()( std::ostream& out) const { - return (out << Time_stamper::display_id(it.operator->())); + return (out << IO::oformat(Time_stamper::display_id(it.operator->()), format)); } }; @@ -1257,7 +1258,7 @@ struct Output_rep, With_offset_tag> using Base = Output_rep; using Base::Base; - Output_rep(const CC_iterator it, With_offset_tag tag = {}) + Output_rep(const CC_iterator& it, With_offset_tag tag = {}) : Base(it), offset(tag.offset) {} std::ostream& operator()(std::ostream& out) const { @@ -1276,7 +1277,7 @@ struct Output_rep, With_point_tag> using Base::Base; - Output_rep(const CC_iterator it, With_point_tag tag = {}) + Output_rep(const CC_iterator& it, With_point_tag tag = {}) : Base(it, tag) {} std::ostream& operator()(std::ostream& out) const { diff --git a/Stream_support/include/CGAL/IO/io.h b/Stream_support/include/CGAL/IO/io.h index 86041bfaec45..c19181a122fc 100644 --- a/Stream_support/include/CGAL/IO/io.h +++ b/Stream_support/include/CGAL/IO/io.h @@ -18,6 +18,8 @@ #ifndef CGAL_IO_H #define CGAL_IO_H +#include + #include #include @@ -27,12 +29,15 @@ #include #include -#include #include +#include #include #include #include #include +#include +#include +#include #include namespace CGAL { @@ -82,6 +87,160 @@ is in `ASCII` mode. */ enum Mode {ASCII = 0, PRETTY, BINARY}; +/*! +\ingroup PkgStreamSupportRef + +returns the printing mode of the %IO stream `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode get_mode(std::ios& s) +{ + return static_cast(s.iword(Static::get_mode())); +} + +/*! +\ingroup PkgStreamSupportRef + +sets the mode of the %IO stream `s` to be the `ASCII` mode. +Returns the previous mode of `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode set_ascii_mode(std::ios& s) +{ + Mode m = get_mode(s); + s.iword(Static::get_mode()) = ASCII; + return m; +} + +/*! +\ingroup PkgStreamSupportRef + +sets the mode of the %IO stream `s` to be the `BINARY` mode. +Returns the previous mode of `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode set_binary_mode(std::ios& s) +{ + Mode m = get_mode(s); + s.iword(Static::get_mode()) = BINARY; + return m; +} + +/*! +\ingroup PkgStreamSupportRef + +sets the mode of the %IO stream `s` to be the `PRETTY` mode. +Returns the previous mode of `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode set_pretty_mode(std::ios& s) +{ + Mode m = get_mode(s); + s.iword(Static::get_mode()) = PRETTY; + return m; +} + +/*! +\ingroup PkgStreamSupportRef + +sets the printing mode of the %IO stream `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode set_mode(std::ios& s, Mode m) +{ + Mode old = get_mode(s); + s.iword(Static::get_mode()) = m; + return old; +} + +/*! +\ingroup PkgStreamSupportRef + +checks if the %IO stream `s` is in `PRETTY` mode. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +*/ +inline bool is_pretty(std::ios& s) { return s.iword(Static::get_mode()) == PRETTY; } + +/*! +\ingroup PkgStreamSupportRef + +checks if the %IO stream `s` is in `ASCII` mode. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline bool is_ascii(std::ios& s) { return s.iword(Static::get_mode()) == ASCII; } + +/*! +\ingroup PkgStreamSupportRef + +checks if the %IO stream `s` is in `BINARY` mode. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_pretty()` +*/ +inline bool is_binary(std::ios& s) { return s.iword(Static::get_mode()) == BINARY; } + } // namespace IO #ifdef DOXYGEN_RUNNING @@ -175,7 +334,7 @@ verify that a given type can be used with `oformat()`. Its value has to be for a type, the class template `Output_rep` defines `is_specialized` to the default value `false`. */ -template +template class Output_rep : public IO_rep_is_not_specialized { @@ -188,17 +347,46 @@ class Output_rep std::ostream& operator()( std::ostream& os) const { return (os << t); } }; +namespace IO { +/*! +\ingroup PkgStreamSupportRef + +Convenience function to construct an output representation (`Output_rep`) for type `T`. + +Generic IO for type `T`. +*/ +template +Output_rep oformat(const T& t) { return Output_rep(t); } + +/*! +\ingroup PkgStreamSupportRef + +Convenience function to construct an output representation (`Output_rep`) for type `T`. + +Generic IO for type `T` with formatting tag. +*/ +template +Output_rep oformat( const T& t, F format) { + if constexpr (std::is_constructible_v, const T&, F&&>) + return Output_rep(t, std::move(format)); + else + return Output_rep(t); +} + +} // namespace IO + template class Output_rep, F> { const std::optional& t; + CGAL_NO_UNIQUE_ADDRESS F format; public: - Output_rep( const std::optional& tt) : t(tt) {} + Output_rep( const std::optional& tt, F format = {}) : t(tt), format(std::move(format)) {} std::ostream& operator()( std::ostream& os) const { if (t==std::nullopt) return (os << "--"); - return (os << t.value()); + return (os << IO::oformat(t.value(), format)); } }; @@ -206,14 +394,45 @@ template class Output_rep, F> { const std::variant& t; + CGAL_NO_UNIQUE_ADDRESS F format; public: - Output_rep( const std::variant& tt) : t(tt) {} + Output_rep( const std::variant& tt, F format = {}) : t(tt), format(std::move(format)) {} std::ostream& operator()( std::ostream& os) const { - std::visit([&os](auto&& v) { os << v; }, t); + std::visit([&os, this](auto&& v) { os << IO::oformat(v, format); }, t); + return os; + } +}; + +template +class Output_rep::type>> + : public IO_rep_is_specialized +{ + const T& value; + CGAL_NO_UNIQUE_ADDRESS F format; + +public: + Output_rep( const T& value, F format = {}) : value(value), format(std::move(format)) {} + + std::ostream& operator()( std::ostream& os) const { + const auto mode = IO::get_mode(os); + const bool is_pretty = std::is_same_v || mode == IO::PRETTY; + const char* sep = (mode == IO::BINARY) ? "" : (is_pretty ? ", " : " "); + if(is_pretty) os << "("; + print_tuple_impl(os, sep, std::make_index_sequence::value>{}); + if(is_pretty) os << ")"; + return os; + } + +protected: + template + std::ostream& print_tuple_impl(std::ostream& os, const char* sep, std::index_sequence) const + { + ((os << (I == 0 ? "" : sep) << IO::oformat(std::get(value), format)), ...); return os; } + }; template @@ -241,35 +460,6 @@ class Output_rep template std::ostream& operator<<( std::ostream& os, Output_rep rep) { return rep(os); } -namespace IO { - -/*! -\ingroup PkgStreamSupportRef - -Convenience function to construct an output representation (`Output_rep`) for type `T`. - -Generic IO for type `T`. -*/ -template -Output_rep oformat(const T& t) { return Output_rep(t); } - -/*! -\ingroup PkgStreamSupportRef - -Convenience function to construct an output representation (`Output_rep`) for type `T`. - -Generic IO for type `T` with formatting tag. -*/ -template -Output_rep oformat( const T& t, F format) { - if constexpr (std::is_constructible_v, const T&, F>) - return Output_rep(t, format); - else - return Output_rep(t); -} - -} // namespace IO - /*! \ingroup PkgStreamSupportRef @@ -289,6 +479,29 @@ class Input_rep std::istream& operator()( std::istream& is) const { return (is >> t); } }; +/*! \relates Input_rep + \brief stream input to the \c Input_rep calls its \c operator(). + +\brief \cgal defines input operators for classes that are derived +from the class `std::istream`. This allows to read from input streams +as `std::cin`, as well as from `std::istringstream` and `std::ifstream`. +The input operator is defined for all classes in the \cgal `Kernel`. +*/ +template +std::istream& operator>>( std::istream& is, Input_rep rep) { return rep(is); } + +namespace IO { + +/*! +\ingroup PkgStreamSupportRef + +The definition of this function is completely symmetric to `oformat()`. +*/ +template +Input_rep iformat( T& t) { return Input_rep(t); } + +} // namespace IO + template class Input_rep> { @@ -301,7 +514,7 @@ class Input_rep> //! perform the input, calls \c operator\>\> by default. std::istream& operator()( std::istream& is) const { T v; - if(is >> v) t = v; + if(is >> IO::iformat(v)) t = v; return is; } }; @@ -451,29 +664,6 @@ class Input_rep }; #endif -/*! \relates Input_rep - \brief stream input to the \c Input_rep calls its \c operator(). - -\brief \cgal defines input operators for classes that are derived -from the class `std::istream`. This allows to read from input streams -as `std::cin`, as well as from `std::istringstream` and `std::ifstream`. -The input operator is defined for all classes in the \cgal `Kernel`. -*/ -template -std::istream& operator>>( std::istream& is, Input_rep rep) { return rep(is); } - -namespace IO { - -/*! -\ingroup PkgStreamSupportRef - -The definition of this function is completely symmetric to `oformat()`. -*/ -template -Input_rep iformat( T& t) { return Input_rep(t); } - -} // namespace IO - template class Benchmark_rep { @@ -500,160 +690,6 @@ Benchmark_rep bmformat( const T& t) { return Benchmark_rep(t); } template Benchmark_rep bmformat( const T& t, F) { return Benchmark_rep(t); } -/*! -\ingroup PkgStreamSupportRef - -returns the printing mode of the %IO stream `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode get_mode(std::ios& s) -{ - return static_cast(s.iword(Static::get_mode())); -} - -/*! -\ingroup PkgStreamSupportRef - -sets the mode of the %IO stream `s` to be the `ASCII` mode. -Returns the previous mode of `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode set_ascii_mode(std::ios& s) -{ - Mode m = get_mode(s); - s.iword(Static::get_mode()) = ASCII; - return m; -} - -/*! -\ingroup PkgStreamSupportRef - -sets the mode of the %IO stream `s` to be the `BINARY` mode. -Returns the previous mode of `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode set_binary_mode(std::ios& s) -{ - Mode m = get_mode(s); - s.iword(Static::get_mode()) = BINARY; - return m; -} - -/*! -\ingroup PkgStreamSupportRef - -sets the mode of the %IO stream `s` to be the `PRETTY` mode. -Returns the previous mode of `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode set_pretty_mode(std::ios& s) -{ - Mode m = get_mode(s); - s.iword(Static::get_mode()) = PRETTY; - return m; -} - -/*! -\ingroup PkgStreamSupportRef - -sets the printing mode of the %IO stream `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode set_mode(std::ios& s, Mode m) -{ - Mode old = get_mode(s); - s.iword(Static::get_mode()) = m; - return old; -} - -/*! -\ingroup PkgStreamSupportRef - -checks if the %IO stream `s` is in `PRETTY` mode. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -*/ -inline bool is_pretty(std::ios& s) { return s.iword(Static::get_mode()) == PRETTY; } - -/*! -\ingroup PkgStreamSupportRef - -checks if the %IO stream `s` is in `ASCII` mode. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline bool is_ascii(std::ios& s) { return s.iword(Static::get_mode()) == ASCII; } - -/*! -\ingroup PkgStreamSupportRef - -checks if the %IO stream `s` is in `BINARY` mode. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_pretty()` -*/ -inline bool is_binary(std::ios& s) { return s.iword(Static::get_mode()) == BINARY; } - } // namespace IO template < class T > @@ -735,10 +771,10 @@ inline std::istream &operator>>(std::istream &is, Color& col) { case ASCII : is >> ir >> ig >> ib >> ia; - r = (unsigned char)ir; - g = (unsigned char)ig; - b = (unsigned char)ib; - a = (unsigned char)ia; + r = static_cast(ir); + g = static_cast(ig); + b = static_cast(ib); + a = static_cast(ia); break; case BINARY : read(is, r); diff --git a/Stream_support/include/CGAL/IO/io_tags.h b/Stream_support/include/CGAL/IO/io_tags.h index 2be296695313..3c794a9aa9cf 100644 --- a/Stream_support/include/CGAL/IO/io_tags.h +++ b/Stream_support/include/CGAL/IO/io_tags.h @@ -18,7 +18,6 @@ #ifndef CGAL_IO_TAGS_H #define CGAL_IO_TAGS_H -#include #include namespace CGAL { @@ -52,6 +51,7 @@ template<> struct Io_traits { typedef io_Read_write Io_tag; }; template<> struct Io_traits { typedef io_Read_write Io_tag; }; struct IO_manip_tag{}; +struct Pretty_tag{}; } //namespace CGAL From 38876a3e2db9d5a27fb107c70e5e821f9046a182 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 27 Mar 2026 16:32:54 +0100 Subject: [PATCH 005/151] WIP --- ...ing_constrained_Delaunay_triangulation_3.h | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 38071fbd47a0..b795f73e2eaf 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -127,6 +127,7 @@ #include #include #include +#include #include #include #include @@ -915,6 +916,7 @@ class Conforming_constrained_Delaunay_triangulation_3 std::cout << "Number of vertices after CDT: " << cdt_impl.number_of_vertices() << "\n\n"; } } + move_Steiner_vertices_to_the_volume(); // TODO: check + DO NOT COMMIT // std::cerr << cdt_3_format("cdt_impl: {} vertices, {} cells\n", cdt_impl.number_of_vertices(), // cdt_impl.number_of_cells()); @@ -1230,6 +1232,186 @@ class Conforming_constrained_Delaunay_triangulation_3 return out; }, IO_manip_tag{}); } + +protected: + template + std::optional + find_in_incident_facets(Vertex_handle v, Pred pred) const + { + boost::container::flat_set, + boost::container::small_vector> found_cells; + boost::container::small_vector cells; + cells.push_back(v->cell()); + found_cells.insert(cells[0]); + int head=0; + int tail=1; + do { + Cell_handle c = cells[head]; + for (int i=0; i<4; ++i) { + if (c->vertex(i) == v) continue; + Cell_handle next = c->neighbor(i); + if(c < next && pred(Facet(c, i))) { + return Facet(c, i); + } + if (! found_cells.insert(next).second ) + continue; + cells.push_back(next); + ++tail; + } + ++head; + } while(head != tail); + return std::nullopt; + } + + auto move_one_Steiner_vertex_on_face_to_the_volume(Vertex_handle v) { + auto is_constrained = [this](Facet f) { return is_facet_constrained(f); }; + + const auto face_id = v->ccdt_3_data().face_index(); + auto incident_constrained_facet_opt = find_in_incident_facets(v, is_constrained); + CGAL_assertion(incident_constrained_facet_opt.has_value()); + auto incident_constrained_facet = *incident_constrained_facet_opt; + CGAL_assertion(face_constraint_index(incident_constrained_facet) == face_id); + move_one_Steiner_vertex_to_the_volume(v); + } + + template + void dump_link_mesh_of_vertex_to_ply(Vertex_handle v, const CellsWithPartitionRange& cells) const { + using Point_3 = typename Traits::Point_3; + std::vector points; + std::vector> triangles; + std::vector triangle_partition_ids; + std::size_t vertex_index = 0; + for(auto [c, partition_id] : cells) { + for(int i = 0; i < 4; ++i) { + if(c->vertex(i) == v) { + std::array triangle; + std::iota(triangle.begin(), triangle.end(), vertex_index); + vertex_index += 3; + triangles.push_back(triangle); + triangle_partition_ids.push_back(partition_id); + } else { + points.push_back(c->vertex(i)->point()); + } + } + } + using Mesh = CGAL::Surface_mesh; + Mesh link_mesh; + using face_descriptor = typename Mesh::Face_index; + std::vector> triangle_index_to_face_descriptor; + + CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, triangles, link_mesh, + CGAL::parameters::polygon_to_face_output_iterator(std::back_inserter(triangle_index_to_face_descriptor))); + auto [patch_id_map, _] = link_mesh.template add_property_map("f:patch_id", 0); + for(auto [triangle_index, fd] : triangle_index_to_face_descriptor) { + put(patch_id_map, fd, triangle_partition_ids[triangle_index]); + } + std::stringstream filename; + filename << "dump_link_mesh_vertex" << IO::oformat(v, With_offset_tag{}) << ".ply"; + std::ofstream out(filename.str()); + CGAL::IO::write_PLY(out, link_mesh); + out.close(); + } + + auto move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { + std::cerr << "Moving Steiner vertex " << IO::oformat(v, With_point_and_info_tag{}) << " to the volume\n"; + std::size_t nb_of_incident_cells{0}; + triangulation().incident_cells( + v, boost::make_function_output_iterator( + [nb_of_incident_cells_ptr = &nb_of_incident_cells](Cell_handle) { ++(*nb_of_incident_cells_ptr); })); + std::cerr << " - Number of incident cells: " << nb_of_incident_cells << '\n'; + + enum class Type_of_facet { + INCIDENT, + OPPOSITE + }; + + struct Facet_of_star_component { + Cell_handle cell; + int index_of_opposite_vertex; + Type_of_facet type; + + bool operator<(const Facet_of_star_component& other) const { + if(cell != other.cell) + return cell < other.cell; + return index_of_opposite_vertex < other.index_of_opposite_vertex; + } + }; + + auto register_cell_and_check_if_new = + [seen_cells = CGAL::unordered_flat_set{}](Cell_handle c) mutable + { + auto [_, inserted] = seen_cells.insert(c); + return inserted; + }; + + boost::container::small_vector facets_of_star_components; + boost::container::small_vector component_offsets(1); + boost::container::small_vector cells_in_other_components; + auto first_cell = v->cell(); + facets_of_star_components.emplace_back(first_cell, first_cell->index(v), Type_of_facet::OPPOSITE); + register_cell_and_check_if_new(first_cell); + auto queue_head=0u; + do { + auto [c, facet_index, facet_type] = facets_of_star_components[queue_head++]; + if(facet_type == Type_of_facet::OPPOSITE) { + for (int i=0; i<4; ++i) { + if (c->vertex(i) == v) continue; + Cell_handle next = c->neighbor(i); + const bool facet_c_to_next_is_constrained = is_facet_constrained(Facet(c, i)); + if(facet_c_to_next_is_constrained) { + facets_of_star_components.emplace_back(c, i, Type_of_facet::INCIDENT); + cells_in_other_components.push_back(next); + } else { + auto next_is_a_new_cell = register_cell_and_check_if_new(next); + if(next_is_a_new_cell) { + facets_of_star_components.emplace_back(next, next->index(v), Type_of_facet::OPPOSITE); + } + } + } + } + if(queue_head == facets_of_star_components.size()) { + std::cerr << " - Partition " << component_offsets.size() << " has " + << facets_of_star_components.size() - component_offsets.back() << " facets\n"; + component_offsets.push_back(facets_of_star_components.size()); + while(!cells_in_other_components.empty()) { + auto other_cell = cells_in_other_components.back(); + cells_in_other_components.pop_back(); + auto other_cell_is_a_new_cell = register_cell_and_check_if_new(other_cell); + if(other_cell_is_a_new_cell) { + facets_of_star_components.emplace_back(other_cell, other_cell->index(v), Type_of_facet::OPPOSITE); + break; + } + } + } + } while(queue_head != facets_of_star_components.size()); + + { // DEBUG, DO NOT COMMIT as it is + boost::container::small_vector, 128> cells; + for(std::size_t component_index = 1; component_index < component_offsets.size(); ++component_index) { + for(std::size_t i = component_offsets[component_index - 1], end = component_offsets[component_index]; i < end; + ++i) + { + const auto& [cell, facet_index, facet_type] = facets_of_star_components[i]; + if(facet_type == Type_of_facet::OPPOSITE) { + cells.emplace_back(cell, facet_index); + } + } + } + dump_link_mesh_of_vertex_to_ply(v, cells); // DO NOT COMMIT AS IT IS: use a debug flag to enable it + } + + std::cerr << " " << "nb of components: " << component_offsets.size() - 1 << '\n'; + } + + auto move_Steiner_vertices_to_the_volume() { + for(auto v: triangulation().finite_vertex_handles()) { + auto type = v->ccdt_3_data().vertex_type(); + if(type == CDT_3_vertex_type::STEINER_IN_FACE) + move_one_Steiner_vertex_on_face_to_the_volume(v); + else if (type == CDT_3_vertex_type::STEINER_ON_EDGE) + move_one_Steiner_vertex_to_the_volume(v); + } + } }; #ifndef DOXYGEN_RUNNING From 2f0a8ed5e2e002a14b3dd6f82c638a9b2965527f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 27 Mar 2026 17:38:31 +0100 Subject: [PATCH 006/151] WIP: improve dump_link_mesh_of_vertex_to_ply --- ...ing_constrained_Delaunay_triangulation_3.h | 76 +++++++++---------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index b795f73e2eaf..326118288856 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1274,37 +1274,46 @@ class Conforming_constrained_Delaunay_triangulation_3 move_one_Steiner_vertex_to_the_volume(v); } - template - void dump_link_mesh_of_vertex_to_ply(Vertex_handle v, const CellsWithPartitionRange& cells) const { + template + void dump_link_mesh_of_vertex_to_ply(Vertex_handle v, const Offsets& component_offsets, const Facets& facets) const { using Point_3 = typename Traits::Point_3; - std::vector points; - std::vector> triangles; - std::vector triangle_partition_ids; - std::size_t vertex_index = 0; - for(auto [c, partition_id] : cells) { - for(int i = 0; i < 4; ++i) { - if(c->vertex(i) == v) { - std::array triangle; - std::iota(triangle.begin(), triangle.end(), vertex_index); - vertex_index += 3; - triangles.push_back(triangle); - triangle_partition_ids.push_back(partition_id); - } else { - points.push_back(c->vertex(i)->point()); - } - } - } using Mesh = CGAL::Surface_mesh; - Mesh link_mesh; using face_descriptor = typename Mesh::Face_index; - std::vector> triangle_index_to_face_descriptor; - - CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, triangles, link_mesh, - CGAL::parameters::polygon_to_face_output_iterator(std::back_inserter(triangle_index_to_face_descriptor))); + Mesh link_mesh; auto [patch_id_map, _] = link_mesh.template add_property_map("f:patch_id", 0); - for(auto [triangle_index, fd] : triangle_index_to_face_descriptor) { - put(patch_id_map, fd, triangle_partition_ids[triangle_index]); - } + for(std::size_t component_index = 1; component_index < component_offsets.size(); ++component_index) { + std::vector points; + std::vector> triangles; + std::size_t vertex_index = 0; + Mesh component_mesh; + for(std::size_t facet_id = component_offsets[component_index - 1], + end = component_offsets[component_index]; + facet_id < end; ++facet_id) + { + const auto& [cell, facet_index, _] = facets[facet_id]; + for(int i = 0; i < 3; ++i) { + points.push_back(cell->vertex(Triangulation::vertex_triple_index(facet_index, i))->point()); + } // end for each vertex of the facet + // swap the orientation: our facets are oriented toward the center of the star component + // but we want them to be oriented outward + triangles.push_back({vertex_index, vertex_index + 2, vertex_index + 1}); + vertex_index += 3; + } // end for each facet in the component + CGAL::Polygon_mesh_processing::merge_duplicate_points_in_polygon_soup(points, triangles); + + std::vector> triangle_index_to_face_descriptor; + CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, triangles, component_mesh, + CGAL::parameters::polygon_to_face_output_iterator(std::back_inserter(triangle_index_to_face_descriptor))); + + std::vector new_faces; + auto out_it = boost::make_function_output_iterator( + [&new_faces](std::pair fd_pair) { new_faces.push_back(fd_pair.second); }); + CGAL::copy_face_graph(component_mesh, link_mesh, CGAL::parameters::face_to_face_output_iterator(out_it)); + for(auto fd : new_faces) { + put(patch_id_map, fd, static_cast(component_index)); + } + } // end for each component + std::stringstream filename; filename << "dump_link_mesh_vertex" << IO::oformat(v, With_offset_tag{}) << ".ply"; std::ofstream out(filename.str()); @@ -1386,18 +1395,7 @@ class Conforming_constrained_Delaunay_triangulation_3 } while(queue_head != facets_of_star_components.size()); { // DEBUG, DO NOT COMMIT as it is - boost::container::small_vector, 128> cells; - for(std::size_t component_index = 1; component_index < component_offsets.size(); ++component_index) { - for(std::size_t i = component_offsets[component_index - 1], end = component_offsets[component_index]; i < end; - ++i) - { - const auto& [cell, facet_index, facet_type] = facets_of_star_components[i]; - if(facet_type == Type_of_facet::OPPOSITE) { - cells.emplace_back(cell, facet_index); - } - } - } - dump_link_mesh_of_vertex_to_ply(v, cells); // DO NOT COMMIT AS IT IS: use a debug flag to enable it + dump_link_mesh_of_vertex_to_ply(v, component_offsets, facets_of_star_components); // DO NOT COMMIT AS IT IS: use a debug flag to enable it } std::cerr << " " << "nb of components: " << component_offsets.size() - 1 << '\n'; From 013cb1f4bae88b5f4338e55ebcc3f9e70fe43806 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 30 Mar 2026 09:05:54 +0200 Subject: [PATCH 007/151] WIP: deal with infinite cells --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 326118288856..63e56127a8bc 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1291,6 +1291,9 @@ class Conforming_constrained_Delaunay_triangulation_3 facet_id < end; ++facet_id) { const auto& [cell, facet_index, _] = facets[facet_id]; + if(triangulation().is_infinite(Facet(cell, facet_index))) { + continue; + } for(int i = 0; i < 3; ++i) { points.push_back(cell->vertex(Triangulation::vertex_triple_index(facet_index, i))->point()); } // end for each vertex of the facet From 38936b02870450b7a7d3fa1810a3af3c01cbe081 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 3 Apr 2026 11:03:39 +0200 Subject: [PATCH 008/151] modernize T_2::fill_hole_delaunay --- .../include/CGAL/Triangulation_2.h | 287 ++++++------------ 1 file changed, 86 insertions(+), 201 deletions(-) diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index fea1aba15eec..7adc69f5b358 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -2030,32 +2030,30 @@ fill_hole(Vertex_handle v, std::list & hole, OutputItFaces fit) } template +template void Triangulation_2:: -fill_hole_delaunay(std::list & first_hole) +fill_hole_delaunay(std::list & first_hole, OutputItFaces fit) { - typedef std::list Hole; - typedef std::list Hole_list; - - Face_handle f, ff, fn; - int i, ii, in; - Hole_list hole_list; - - hole_list.push_front(first_hole); + auto create_face = [&](auto &&... args) { + Face_handle newf = this->tds().create_face(std::forward(args)...); + *fit++ = newf; + return newf; + }; + using Hole = std::list; + std::list hole_list{1, first_hole}; while( ! hole_list.empty()) { Hole& hole = hole_list.front(); - typename Hole::iterator hit = hole.begin(); - // if the hole has only three edges, create the triangle if (hole.size() == 3) { - hit = hole.begin(); - f = (*hit).first; i = (*hit).second; - ff = (* ++hit).first; ii = (*hit).second; - fn = (* ++hit).first; in = (*hit).second; - create_face(f,i,ff,ii,fn,in); + auto hit = hole.begin(); + auto [f0, i0] = *hit; ++hit; + auto [f1, i1] = *hit; ++hit; + auto [f2, i2] = *hit; + create_face(f0,i0,f1,i1,f2,i2); hole_list.pop_front(); continue; } @@ -2065,212 +2063,99 @@ fill_hole_delaunay(std::list & first_hole) // and the new triangle adjacent to that edge // cut the hole and push it back + // two helper functions: + auto cw_vertex = [](Edge e) { return e.first->vertex( cw(e.second)); }; + auto ccw_vertex = [](Edge e) { return e.first->vertex(ccw(e.second)); }; + // first, ensure that a neighboring face // whose vertices on the hole boundary are finite // is the first of the hole - bool finite= false; - while (!finite){ - ff = (hole.front()).first; - ii = (hole.front()).second; - if ( is_infinite(ff->vertex(cw(ii))) || - is_infinite(ff->vertex(ccw(ii)))) { - hole.push_back(hole.front()); - hole.pop_front(); - } - else finite=true; + for (auto eit = hole.begin(); + is_infinite(cw_vertex(*eit)) || is_infinite(ccw_vertex(*eit)); + eit = hole.begin()) + { + hole.splice(hole.end(), hole, hole.begin()); // push the first element to the end of the list } // take the first neighboring face and pop it; - ff = (hole.front()).first; - ii =(hole.front()).second; + auto first_edge = hole.front(); hole.pop_front(); - Vertex_handle v0 = ff->vertex(cw(ii)); - Vertex_handle v1 = ff->vertex(ccw(ii)); - Vertex_handle v2 = infinite_vertex(); - Vertex_handle v3; - const Point& p0 = v0->point(); - const Point& p1 = v1->point(); - - typename Hole::iterator hdone = hole.end(); - hit = hole.begin(); - typename Hole::iterator cut_after(hit); - - // if tested vertex is c with respect to the vertex opposite - // to nullptr neighbor, - // stop at the before last face; - hdone--; - while( hit != hdone) { - fn = (*hit).first; - in = (*hit).second; - Vertex_handle vv = fn->vertex(ccw(in)); - if (is_infinite(vv)) { - if(is_infinite(v2)) cut_after = hit; - } - else { // vv is a finite vertex - const Point & p = vv->point(); - if (orientation(p0,p1,p) == COUNTERCLOCKWISE) { - if (is_infinite(v2)) { v2=vv; v3=vv; cut_after=hit;} - else{ - // - if (this->side_of_oriented_circle(p0,p1,v3->point(),p,true) == ON_POSITIVE_SIDE){ - v2=vv; v3=vv; cut_after=hit;} - } - } - } - ++hit; - } + const Vertex_handle v0 = cw_vertex(first_edge); + const Vertex_handle v1 = ccw_vertex(first_edge); - // create new triangle and update adjacency relations - Face_handle newf; + // search in `hole` an edge `cut_after`, and its vertex `v2==ccw_vertex(*cut_after)` + // such that the triangle (v0,v1,v2) is a Delaunay triangle + const auto [cut_after, v2] = std::invoke([&]() { + const auto hole_end = hole.cend(); + const auto hole_last_one = std::prev(hole_end); - //update the hole and push back in the Hole_List stack - // if v2 belongs to the neighbor following or preceding *f - // the hole remain a single hole - // otherwise it is split in two holes + auto cut_after = hole_end; + Vertex_handle v2 = infinite_vertex(); - fn = (hole.front()).first; - in = (hole.front()).second; - if (fn->has_vertex(v2, i) && i == fn->ccw(in)) { - newf = create_face(ff,ii,fn,in); - hole.pop_front(); - hole.push_front(Edge( newf,1)); - } - else{ - fn = (hole.back()).first; - in = (hole.back()).second; - if (fn->has_vertex(v2, i) && i== fn->cw(in)) { - newf = create_face(fn,in,ff,ii); - hole.pop_back(); - hole.push_back(Edge(newf,1)); - } - else { - // split the hole in two holes - newf = create_face(ff,ii,v2); - Hole new_hole; - ++cut_after; - while( hole.begin() != cut_after ) - { - new_hole.push_back(hole.front()); - hole.pop_front(); - } + const Point& p0 = v0->point(); + const Point& p1 = v1->point(); - hole.push_front(Edge( newf,1)); - new_hole.push_front(Edge( newf,0)); - hole_list.push_front(new_hole); + for(auto hit = hole.cbegin(); hit != hole_last_one; ++hit) + { + const Vertex_handle vv = ccw_vertex(*hit); + if (is_infinite(vv)) { + if(is_infinite(v2)) cut_after = hit; + } + else { // vv is a finite vertex + const Point& p = vv->point(); + if (orientation(p0,p1,p) == COUNTERCLOCKWISE) { + if (is_infinite(v2) || + this->side_of_oriented_circle(p0,p1,v2->point(),p,true) == ON_POSITIVE_SIDE) { + cut_after = hit; v2 = vv; + } + } + } } + return std::make_pair(cut_after, v2); + }); + CGAL_assertion(cut_after != hole.cend()); + + // create new triangle (v0, v1, v2) and update adjacency relations + + // Update the hole and push back in the Hole_List stack. + // If v2 belongs to the neighbor following or preceding `first_edge`: + // - then the hole remain a single hole, + // - otherwise it is split in two holes + // Note that `first_edge`, that was the `front()` of the hole, has + // already been popped from it. + const auto& [ff, ii] = first_edge; + if (auto [fn, in] = hole.front(); fn->vertex(ccw(in)) == v2) { + Face_handle newf = create_face(ff,ii,fn,in); + hole.front() = Edge(newf,1); + } + else if (auto [fn, in] = hole.back(); fn->vertex(cw(in)) == v2) { + Face_handle newf = create_face(fn,in,ff,ii); + hole.back() = Edge(newf,1); + hole.back() = Edge(newf,1); + } + else { + Face_handle newf = create_face(ff,ii,v2); + + // split the hole in two holes: + // - transfer the range [hole.begin(), cut_after] (`cut_after` included) to `new_hole` + // - `hole` keeps the rest: [cut_after+1, hole.end()( + Hole new_hole; + new_hole.splice(new_hole.end(), hole, hole.begin(), std::next(cut_after)); + new_hole.push_front(Edge(newf,0)); // close the new hole with the edge (newf, 0) + hole_list.push_front(new_hole); // and push it in the stack + + hole.push_front(Edge(newf,1)); // close the hole with the edge (newf, 1) } } } template < class Gt, class Tds > -template void Triangulation_2:: -fill_hole_delaunay(std::list & first_hole, OutputItFaces fit) +fill_hole_delaunay(std::list & first_hole) { - typedef typename Gt::Side_of_oriented_circle_2 In_circle; - typedef std::list Hole; - typedef std::list Hole_list; - - In_circle in_circle = geom_traits().side_of_oriented_circle_2_object(); - - Face_handle f, ff, fn; - int i, ii, in; - Hole_list hole_list; - hole_list.push_front(first_hole); - - while(!hole_list.empty()) { - Hole& hole = hole_list.front(); - typename Hole::iterator hit = hole.begin(); - - if (hole.size() == 3) { - hit = hole.begin(); - f = (*hit).first; i = (*hit).second; - ff = (* ++hit).first; ii = (*hit).second; - fn = (* ++hit).first; in = (*hit).second; - Face_handle newf = create_face(f,i,ff,ii,fn,in); - *fit++ = newf; - hole_list.pop_front(); - continue; - } - - bool finite= false; - while (!finite){ - ff = (hole.front()).first; - ii = (hole.front()).second; - if ( is_infinite(ff->vertex(cw(ii))) || - is_infinite(ff->vertex(ccw(ii)))) { - hole.push_back(hole.front()); - hole.pop_front(); - } else finite=true; - } - - ff = (hole.front()).first; - ii =(hole.front()).second; - hole.pop_front(); - - Vertex_handle v0 = ff->vertex(cw(ii)); - Vertex_handle v1 = ff->vertex(ccw(ii)); - Vertex_handle v2 = infinite_vertex(); - const Point& p0 = v0->point(); - const Point& p1 = v1->point(); - - typename Hole::iterator hdone = hole.end(); - hit = hole.begin(); - typename Hole::iterator cut_after(hit); - - hdone--; - while( hit != hdone) { - fn = (*hit).first; - in = (*hit).second; - Vertex_handle vv = fn->vertex(ccw(in)); - if (is_infinite(vv)) { - if(is_infinite(v2)) cut_after = hit; - } else { // vv is a finite vertex - const Point & p = vv->point(); - if (orientation(p0,p1,p) == CGAL::COUNTERCLOCKWISE) { - if (is_infinite(v2)) { v2 = vv; cut_after = hit;} - else{ - if (in_circle(p0,p1,v2->point(),p) == CGAL::ON_POSITIVE_SIDE){ - v2 = vv; cut_after = hit; - } - } - } - } - ++hit; - } - - Face_handle newf; - - fn = (hole.front()).first; - in = (hole.front()).second; - if (fn->has_vertex(v2, i) && i == fn->ccw(in)) { - newf = create_face(ff,ii,fn,in); - hole.pop_front(); - hole.push_front(Edge( newf,1)); - } else { - fn = (hole.back()).first; - in = (hole.back()).second; - if (fn->has_vertex(v2, i) && i== fn->cw(in)) { - newf = create_face(fn,in,ff,ii); - hole.pop_back(); - hole.push_back(Edge(newf,1)); - } else { - newf = create_face(ff,ii,v2); - Hole new_hole; - ++cut_after; - while( hole.begin() != cut_after ) { - new_hole.push_back(hole.front()); - hole.pop_front(); - } - hole.push_front(Edge(newf, 1)); - new_hole.push_front(Edge(newf, 0)); - hole_list.push_front(new_hole); - } - } - *fit++ = newf; - } + return fill_hole_delaunay(first_hole, CGAL::Emptyset_iterator{}); } template From 4086491c7043791448b972f9a3bb888012961561 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 3 Apr 2026 13:30:11 +0200 Subject: [PATCH 009/151] WIP: call to PMP::kernel works well --- .../Conforming_constrained_Delaunay_triangulation_3.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 63e56127a8bc..35a3ce035f72 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -1315,6 +1316,13 @@ class Conforming_constrained_Delaunay_triangulation_3 for(auto fd : new_faces) { put(patch_id_map, fd, static_cast(component_index)); } + auto opt_kernel_center_point = CGAL::Polygon_mesh_processing::kernel_point(component_mesh); + if(opt_kernel_center_point.has_value()) { + auto kernel_center_point = *opt_kernel_center_point; + std::cerr << " - kernel center point of component " << component_index << ": " << kernel_center_point + << " (distance to vertex: " << CGAL::sqrt(CGAL::squared_distance(kernel_center_point, v->point())) << ")" + << "\n"; + } } // end for each component std::stringstream filename; From d532f83463acfaa719e479c108c5200ba1f4e6a0 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 7 Apr 2026 16:57:44 +0200 Subject: [PATCH 010/151] fill_hole_delaunay is now a free function --- .../include/CGAL/Triangulation_2.h | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index 7adc69f5b358..d9554bbb9262 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -653,14 +653,19 @@ projection_traits_has_inexact_negative_orientation(const Point &p, const Point & template void fill_hole(Vertex_handle v, std::list & hole, OutputItFaces fit); - template - void fill_hole_delaunay(std::list & hole, OutputItFaces fit); - void make_hole(Vertex_handle v, std::list & hole, std::set &faces_set); public: void make_hole(Vertex_handle v, std::list & hole); + + // convenience function to get the hole + auto make_hole(Vertex_handle v) { + std::list hole; + make_hole(v, hole); + return hole; + } + // template // Vertex_handle star_hole( Point p, // EdgeIt edge_begin, @@ -2029,14 +2034,19 @@ fill_hole(Vertex_handle v, std::list & hole, OutputItFaces fit) *fit++ = newf; } -template -template +template void -Triangulation_2:: -fill_hole_delaunay(std::list & first_hole, OutputItFaces fit) +fill_hole_delaunay(Triangulation_2& tr, + std::list::Edge>& first_hole, + OutputItFaces fit) { + using Tr = Triangulation_2; + using Vertex_handle = typename Tr::Vertex_handle; + using Edge = typename Tr::Edge; + using Face_handle = typename Tr::Face_handle; + auto create_face = [&](auto &&... args) { - Face_handle newf = this->tds().create_face(std::forward(args)...); + Face_handle newf = tr.tds().create_face(std::forward(args)...); *fit++ = newf; return newf; }; @@ -2064,14 +2074,14 @@ fill_hole_delaunay(std::list & first_hole, OutputItFaces fit) // cut the hole and push it back // two helper functions: - auto cw_vertex = [](Edge e) { return e.first->vertex( cw(e.second)); }; - auto ccw_vertex = [](Edge e) { return e.first->vertex(ccw(e.second)); }; + auto cw_vertex = [](Edge e) { return e.first->vertex( Tr::cw(e.second)); }; + auto ccw_vertex = [](Edge e) { return e.first->vertex(Tr::ccw(e.second)); }; // first, ensure that a neighboring face // whose vertices on the hole boundary are finite // is the first of the hole for (auto eit = hole.begin(); - is_infinite(cw_vertex(*eit)) || is_infinite(ccw_vertex(*eit)); + tr.is_infinite(cw_vertex(*eit)) || tr.is_infinite(ccw_vertex(*eit)); eit = hole.begin()) { hole.splice(hole.end(), hole, hole.begin()); // push the first element to the end of the list @@ -2091,22 +2101,22 @@ fill_hole_delaunay(std::list & first_hole, OutputItFaces fit) const auto hole_last_one = std::prev(hole_end); auto cut_after = hole_end; - Vertex_handle v2 = infinite_vertex(); + Vertex_handle v2 = tr.infinite_vertex(); - const Point& p0 = v0->point(); - const Point& p1 = v1->point(); + const auto& p0 = v0->point(); + const auto& p1 = v1->point(); for(auto hit = hole.cbegin(); hit != hole_last_one; ++hit) { const Vertex_handle vv = ccw_vertex(*hit); - if (is_infinite(vv)) { - if(is_infinite(v2)) cut_after = hit; + if (tr.is_infinite(vv)) { + if(tr.is_infinite(v2)) cut_after = hit; } else { // vv is a finite vertex - const Point& p = vv->point(); - if (orientation(p0,p1,p) == COUNTERCLOCKWISE) { - if (is_infinite(v2) || - this->side_of_oriented_circle(p0,p1,v2->point(),p,true) == ON_POSITIVE_SIDE) { + const auto& p = vv->point(); + if (tr.orientation(p0,p1,p) == COUNTERCLOCKWISE) { + if (tr.is_infinite(v2) || + tr.side_of_oriented_circle(p0,p1,v2->point(),p,true) == ON_POSITIVE_SIDE) { cut_after = hit; v2 = vv; } } @@ -2125,11 +2135,11 @@ fill_hole_delaunay(std::list & first_hole, OutputItFaces fit) // Note that `first_edge`, that was the `front()` of the hole, has // already been popped from it. const auto& [ff, ii] = first_edge; - if (auto [fn, in] = hole.front(); fn->vertex(ccw(in)) == v2) { + if (auto [fn, in] = hole.front(); fn->vertex(Tr::ccw(in)) == v2) { Face_handle newf = create_face(ff,ii,fn,in); hole.front() = Edge(newf,1); } - else if (auto [fn, in] = hole.back(); fn->vertex(cw(in)) == v2) { + else if (auto [fn, in] = hole.back(); fn->vertex(Tr::cw(in)) == v2) { Face_handle newf = create_face(fn,in,ff,ii); hole.back() = Edge(newf,1); hole.back() = Edge(newf,1); @@ -2155,7 +2165,7 @@ void Triangulation_2:: fill_hole_delaunay(std::list & first_hole) { - return fill_hole_delaunay(first_hole, CGAL::Emptyset_iterator{}); + return CGAL::fill_hole_delaunay(*this, first_hole, CGAL::Emptyset_iterator{}); } template From b4cb348b1950e71138d3feef4504a252c9c3c362 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 7 Apr 2026 17:02:48 +0200 Subject: [PATCH 011/151] WIP remove_Steiner_vertex_from_all_cdt_2 --- ...ing_constrained_Delaunay_triangulation_3.h | 169 +++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 35a3ce035f72..e707fe65ef28 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -127,6 +127,7 @@ #include #include #include +#include #include #include #include @@ -1410,6 +1411,9 @@ class Conforming_constrained_Delaunay_triangulation_3 } std::cerr << " " << "nb of components: " << component_offsets.size() - 1 << '\n'; + std::size_t nb_of_new_2d_faces = 0; + cdt_impl.remove_Steiner_vertex_from_all_cdt_2(v, CGAL::Counting_output_iterator{&nb_of_new_2d_faces}); + std::cerr << " - number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; } auto move_Steiner_vertices_to_the_volume() { @@ -1511,6 +1515,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Itag = No_constraint_intersection_requiring_constructions_tag; using CDT_base = Constrained_Delaunay_triangulation_2; using CDT = Constrained_triangulation_plus_2; + using CT = typename CDT::Ctr; template struct CDT_2_dual_color_map { @@ -1526,6 +1531,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Color_map_is_in_region = CDT_2_dual_color_map<&Face_info::is_in_region>; }; // CDT_2_types using CDT_2 = typename CDT_2_types::CDT; + using CT_2 = typename CDT_2_types::CT; using CDT_2_traits = typename CDT_2_types::Projection_traits; using CDT_2_face_handle = typename CDT_2::Face_handle; using CDT_2_edge = typename CDT_2::Edge; @@ -4494,6 +4500,167 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } + template + auto remove_Steiner_vertex_from_all_cdt_2(Vertex_handle v, + New_CDT_2_face_handles_output_iterator new_face_handles_out) + { + auto remove_from_cdt_2 = [&](Vertex_handle v, CDT_3_vertex_type vertex_type, Face_index face_id) { + dump_face(face_id); + std::cerr << "Remove Steiner vertex " << IO::oformat(v, with_point_and_info) << " from CDT_2 of face F#" << face_id << '\n'; + auto& cdt_2 = non_const_face_cdt_2(face_id); + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << IO::oformat(v, with_point_and_info) + << " from face F#" << face_id << '\n'; + CGAL_assertion(false); + } + + typename CDT_2::Locate_type lt; + int i; + auto fh_2d = cdt_2.locate(v->point(), lt, i); + CGAL_assertion(lt == CDT_2::VERTEX); + + auto vh_2d = fh_2d->vertex(i); + CGAL_assertion(vh_2d->info().vertex_handle_3d == v); + CGAL_assertion(cdt_2.point(vh_2d) == v->point()); + + if(vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE) { + + auto half_hole = [&](CDT_2_face_handle incident_fh) { + struct Result { + std::list hole_side; + boost::container::small_vector to_delete; + CDT_2_edge last_incident_edge; + } result; + auto edge_circulator = cdt_2.incident_edges(vh_2d, incident_fh); + --edge_circulator; + CGAL_assertion_code(auto done = edge_circulator); + while(!cdt_2.is_constrained(*edge_circulator)) { + --edge_circulator; + CGAL_assertion(edge_circulator != done); + } + CGAL_assertion_code(done = edge_circulator); + do { + ++edge_circulator; + const auto [f, i] = *edge_circulator; + result.to_delete.push_back(f); + const auto edge = cdt_2.mirror_edge(CDT_2_edge{f, cdt_2.cw(i)}); + result.hole_side.push_back(edge); + const auto [n, ni] = edge; + n->vertex(cdt_2. cw(ni))->set_face(n); + n->vertex(cdt_2.ccw(ni))->set_face(n); + CGAL_assertion(edge_circulator != done); + } while(!cdt_2.is_constrained(*edge_circulator)); + result.last_incident_edge = *edge_circulator; + return result; + }; + + auto bore_hole_and_fill_with_delaunay = + [&](auto& hole_boundary, const auto faces_to_delete, bool is_outside_the_face) { + std::vector hole_edges(hole_boundary.begin(), hole_boundary.end()); + std::for_each(faces_to_delete.begin(), faces_to_delete.end(), + [&cdt_2](CDT_2_face_handle f) { cdt_2.delete_face(f); }); + if(hole_boundary.size() > 2) { + fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { + auto& info = fh->info(); + info.is_outside_the_face = is_outside_the_face; + info.missing_subface = true; + info.is_in_region = 0; + info.facet_3d = {}; + *new_face_handles_out++ = fh; + })); + } else { + CGAL_assertion(hole_boundary.size() == 2); + auto [f, i] = hole_boundary.front(); + auto [g, j] = hole_boundary.back(); + g->set_neighbor(j, f); + f->set_neighbor(i, g); + } + std::for_each(hole_edges.begin(), hole_edges.end(), + [&cdt_2](CDT_2_edge e) { + auto [f, i] = cdt_2.mirror_edge(e); + f->set_constraint(i, cdt_2.is_constrained(e)); + }); + }; + + auto [hole_side_1, hole_side_1_faces_to_delete, side_1_last_incident_edge] = half_hole(vh_2d->face()); + const auto [side_1_last_face, side_1_last_edge_index] = side_1_last_incident_edge; + const bool side_1_hole_is_outside_the_face = side_1_last_face->info().is_outside_the_face; + + const auto side_2_first_face = side_1_last_face->neighbor(side_1_last_edge_index); + + auto [hole_side_2, hole_side_2_faces_to_delete, side_2_last_incident_edge] = half_hole(side_2_first_face); + const auto [side_2_last_face, side_2_last_edge_index] = side_2_last_incident_edge; + const bool side_2_hole_is_outside_the_face = side_2_last_face->info().is_outside_the_face; + + const auto side_1_first_face = side_2_last_face->neighbor(side_2_last_edge_index); + + CGAL_assertion(side_1_last_incident_edge.first != side_2_last_incident_edge.first); + + // unmark the two constrained edges + side_1_last_face->set_constraint(side_1_last_edge_index, false); + side_2_first_face->set_constraint(side_2_first_face->index(side_1_last_face), false); + side_2_last_face->set_constraint(side_2_last_edge_index, false); + side_1_first_face->set_constraint(side_1_first_face->index(side_2_last_face), false); + + // start boring and sewing the two half-holes: + // - we start from side 1, + // - `side_2_last_face` will temporarily be modified to close `hole_side_1` + const auto v1 = side_1_last_face->vertex(cdt_2.ccw(side_1_last_edge_index)); + side_2_last_face->set_vertex(cdt_2.cw(side_2_last_edge_index), v1); + hole_side_1.push_back(side_2_last_incident_edge); + + // - bore the hole on side 1, and fill it with CDT triangles + bore_hole_and_fill_with_delaunay(hole_side_1, hole_side_1_faces_to_delete, side_1_hole_is_outside_the_face); + + // - now the edge opposite to `(side_2_last_face, side_2_last_edge_index)` is on the border of the hole + // `hole_side_2`, so we can start sewing from there + const auto side_1_border_edge = cdt_2.mirror_edge(CDT_2_edge{side_2_last_face, side_2_last_edge_index}); + hole_side_2.push_back(side_1_border_edge); + bore_hole_and_fill_with_delaunay(hole_side_2, hole_side_2_faces_to_delete, side_2_hole_is_outside_the_face); + + // - then re-mark the edge between the two holes as a constrained edge + const auto [f, i] = side_1_border_edge; + f->set_constraint(i, true); + const auto n = f->neighbor(i); + n->set_constraint(n->index(f), true); + } else { + auto hole = cdt_2.make_hole(vh_2d); + std::vector hole_edges(hole.begin(), hole.end()); + fill_hole_delaunay(cdt_2, hole, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { + auto& info = fh->info(); + info.is_outside_the_face = false; + info.missing_subface = true; + info.is_in_region = 0; + info.facet_3d = {}; + *new_face_handles_out++ = fh; + })); + std::for_each(hole_edges.begin(), hole_edges.end(), + [&cdt_2](CDT_2_edge e) { + auto [f, i] = cdt_2.mirror_edge(e); + f->set_constraint(i, cdt_2.is_constrained(e)); + }); + } + cdt_2.delete_vertex(vh_2d); + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << IO::oformat(v, with_point_and_info) + << " from face F#" << face_id << '\n'; + CGAL_assertion(false); + } + }; + + auto type = v->ccdt_3_data().vertex_type(); + if(type == CDT_3_vertex_type::STEINER_IN_FACE) { + auto face_id = v->ccdt_3_data().face_index(); + remove_from_cdt_2(v, type, face_id); + } + else if (type == CDT_3_vertex_type::STEINER_ON_EDGE) { + const auto c_id = v->ccdt_3_data().constrained_polyline_id(*this); + for(const auto& [_, face_id] : CGAL::make_range(this->incident_faces_to_polyline.equal_range(c_id))) { + remove_from_cdt_2(v, type, face_id); + } + } + } + void add_bbox_points_if_not_dimension_3() { if(this->dimension() != 3) { const auto bbox = CGAL::bbox_3(this->points_begin(), this->points_end(), this->geom_traits()); @@ -5198,7 +5365,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void face_constraint_misses_subfaces_reset(std::size_t pos) { face_constraint_misses_subfaces[pos] = false; } -}; +}; // end of Conforming_constrained_Delaunay_triangulation_3_impl #endif // DOXYGEN_RUNNING From b023ee39ca5dbc45d403ffaae2f5d7f3a9d2d0cf Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 7 Apr 2026 17:17:31 +0200 Subject: [PATCH 012/151] fill_hole_delaunay must return the output iterator --- Triangulation_2/include/CGAL/Triangulation_2.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index d9554bbb9262..70ea13d98b11 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -2035,7 +2035,7 @@ fill_hole(Vertex_handle v, std::list & hole, OutputItFaces fit) } template -void +OutputItFaces fill_hole_delaunay(Triangulation_2& tr, std::list::Edge>& first_hole, OutputItFaces fit) @@ -2158,6 +2158,7 @@ fill_hole_delaunay(Triangulation_2& tr, hole.push_front(Edge(newf,1)); // close the hole with the edge (newf, 1) } } + return fit; } template < class Gt, class Tds > @@ -2165,7 +2166,7 @@ void Triangulation_2:: fill_hole_delaunay(std::list & first_hole) { - return CGAL::fill_hole_delaunay(*this, first_hole, CGAL::Emptyset_iterator{}); + CGAL::fill_hole_delaunay(*this, first_hole, CGAL::Emptyset_iterator{}); } template From db7c11c04cfe09c4e1c9d5c5a88a5a65e54cf9dc Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 7 Apr 2026 17:18:00 +0200 Subject: [PATCH 013/151] factorize to remove copy-pasting --- ...ing_constrained_Delaunay_triangulation_3.h | 99 +++++++++---------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index e707fe65ef28..5664946d3579 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -4516,12 +4516,50 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D typename CDT_2::Locate_type lt; int i; - auto fh_2d = cdt_2.locate(v->point(), lt, i); + const auto fh_2d = cdt_2.locate(v->point(), lt, i); CGAL_assertion(lt == CDT_2::VERTEX); - auto vh_2d = fh_2d->vertex(i); + const auto vh_2d = fh_2d->vertex(i); CGAL_assertion(vh_2d->info().vertex_handle_3d == v); - CGAL_assertion(cdt_2.point(vh_2d) == v->point()); + + auto fill_with_delaunay = + [&](auto& hole_boundary, bool is_outside_the_face) { + std::vector hole_edges(hole_boundary.begin(), hole_boundary.end()); + if(hole_boundary.size() > 2) { + fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { + auto& info = fh->info(); + info.is_outside_the_face = is_outside_the_face; + info.missing_subface = true; + info.is_in_region = 0; + info.facet_3d = {}; + *new_face_handles_out++ = fh; + })); + } else { + CGAL_assertion(hole_boundary.size() == 2); + auto [f, i] = hole_boundary.front(); + auto [g, j] = hole_boundary.back(); + g->set_neighbor(j, f); + f->set_neighbor(i, g); + } + std::for_each(hole_edges.begin(), hole_edges.end(), + [&cdt_2](CDT_2_edge e) { + auto [f, i] = cdt_2.mirror_edge(e); + f->set_constraint(i, cdt_2.is_constrained(e)); + }); + }; + auto bore_hole_and_fill_with_delaunay = + [&](auto& hole_boundary, const auto faces_to_delete, bool is_outside_the_face) { + std::for_each(faces_to_delete.begin(), faces_to_delete.end(), + [&cdt_2](CDT_2_face_handle f) { cdt_2.delete_face(f); }); + fill_with_delaunay(hole_boundary, is_outside_the_face); + }; + + auto set_constrained = [&](CDT_2_edge e, bool is_constrained) { + auto [f, i] = e; + f->set_constraint(i, is_constrained); + auto [n, ni] = cdt_2.mirror_edge(e); + n->set_constraint(ni, is_constrained); + }; if(vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE) { @@ -4554,34 +4592,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return result; }; - auto bore_hole_and_fill_with_delaunay = - [&](auto& hole_boundary, const auto faces_to_delete, bool is_outside_the_face) { - std::vector hole_edges(hole_boundary.begin(), hole_boundary.end()); - std::for_each(faces_to_delete.begin(), faces_to_delete.end(), - [&cdt_2](CDT_2_face_handle f) { cdt_2.delete_face(f); }); - if(hole_boundary.size() > 2) { - fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { - auto& info = fh->info(); - info.is_outside_the_face = is_outside_the_face; - info.missing_subface = true; - info.is_in_region = 0; - info.facet_3d = {}; - *new_face_handles_out++ = fh; - })); - } else { - CGAL_assertion(hole_boundary.size() == 2); - auto [f, i] = hole_boundary.front(); - auto [g, j] = hole_boundary.back(); - g->set_neighbor(j, f); - f->set_neighbor(i, g); - } - std::for_each(hole_edges.begin(), hole_edges.end(), - [&cdt_2](CDT_2_edge e) { - auto [f, i] = cdt_2.mirror_edge(e); - f->set_constraint(i, cdt_2.is_constrained(e)); - }); - }; - auto [hole_side_1, hole_side_1_faces_to_delete, side_1_last_incident_edge] = half_hole(vh_2d->face()); const auto [side_1_last_face, side_1_last_edge_index] = side_1_last_incident_edge; const bool side_1_hole_is_outside_the_face = side_1_last_face->info().is_outside_the_face; @@ -4597,10 +4607,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(side_1_last_incident_edge.first != side_2_last_incident_edge.first); // unmark the two constrained edges - side_1_last_face->set_constraint(side_1_last_edge_index, false); - side_2_first_face->set_constraint(side_2_first_face->index(side_1_last_face), false); - side_2_last_face->set_constraint(side_2_last_edge_index, false); - side_1_first_face->set_constraint(side_1_first_face->index(side_2_last_face), false); + set_constrained(side_1_last_incident_edge, false); + set_constrained(side_2_last_incident_edge, false); // start boring and sewing the two half-holes: // - we start from side 1, @@ -4619,26 +4627,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D bore_hole_and_fill_with_delaunay(hole_side_2, hole_side_2_faces_to_delete, side_2_hole_is_outside_the_face); // - then re-mark the edge between the two holes as a constrained edge - const auto [f, i] = side_1_border_edge; - f->set_constraint(i, true); - const auto n = f->neighbor(i); - n->set_constraint(n->index(f), true); + set_constrained(side_1_border_edge, true); } else { auto hole = cdt_2.make_hole(vh_2d); - std::vector hole_edges(hole.begin(), hole.end()); - fill_hole_delaunay(cdt_2, hole, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { - auto& info = fh->info(); - info.is_outside_the_face = false; - info.missing_subface = true; - info.is_in_region = 0; - info.facet_3d = {}; - *new_face_handles_out++ = fh; - })); - std::for_each(hole_edges.begin(), hole_edges.end(), - [&cdt_2](CDT_2_edge e) { - auto [f, i] = cdt_2.mirror_edge(e); - f->set_constraint(i, cdt_2.is_constrained(e)); - }); + fill_with_delaunay(hole, /*is_outside_the_face=*/false); } cdt_2.delete_vertex(vh_2d); if(!cdt_2.CT_2::is_valid(true, 3)) { @@ -4659,6 +4651,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D remove_from_cdt_2(v, type, face_id); } } + return new_face_handles_out; } void add_bbox_points_if_not_dimension_3() { From ec10ca1bfd9db7c5d572ccce137eb336fe80d062 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 8 Apr 2026 14:21:54 +0200 Subject: [PATCH 014/151] remove a warning --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 5664946d3579..b9b411f6f6e2 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -4602,8 +4602,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto [side_2_last_face, side_2_last_edge_index] = side_2_last_incident_edge; const bool side_2_hole_is_outside_the_face = side_2_last_face->info().is_outside_the_face; - const auto side_1_first_face = side_2_last_face->neighbor(side_2_last_edge_index); - CGAL_assertion(side_1_last_incident_edge.first != side_2_last_incident_edge.first); // unmark the two constrained edges From 6bbe13df04c72f5eef7e070637b88511c3e3a3a4 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 8 Apr 2026 14:26:54 +0200 Subject: [PATCH 015/151] refactoring: extract a function --- ...ing_constrained_Delaunay_triangulation_3.h | 264 +++++++++--------- 1 file changed, 137 insertions(+), 127 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index b9b411f6f6e2..217931bf833a 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -4501,152 +4501,161 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } template - auto remove_Steiner_vertex_from_all_cdt_2(Vertex_handle v, - New_CDT_2_face_handles_output_iterator new_face_handles_out) + New_CDT_2_face_handles_output_iterator + remove_from_cdt_2(Vertex_handle v, + CDT_3_vertex_type vertex_type, + Face_index face_id, + New_CDT_2_face_handles_output_iterator new_face_handles_out) { - auto remove_from_cdt_2 = [&](Vertex_handle v, CDT_3_vertex_type vertex_type, Face_index face_id) { - dump_face(face_id); - std::cerr << "Remove Steiner vertex " << IO::oformat(v, with_point_and_info) << " from CDT_2 of face F#" << face_id << '\n'; - auto& cdt_2 = non_const_face_cdt_2(face_id); - if(!cdt_2.CT_2::is_valid(true, 3)) { - std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << IO::oformat(v, with_point_and_info) - << " from face F#" << face_id << '\n'; - CGAL_assertion(false); - } + dump_face(face_id); + std::cerr << "Remove Steiner vertex " << IO::oformat(v, with_point_and_info) << " from CDT_2 of face F#" << face_id + << '\n'; + auto& cdt_2 = non_const_face_cdt_2(face_id); + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << IO::oformat(v, with_point_and_info) + << " from face F#" << face_id << '\n'; + CGAL_assertion(false); + } - typename CDT_2::Locate_type lt; - int i; - const auto fh_2d = cdt_2.locate(v->point(), lt, i); - CGAL_assertion(lt == CDT_2::VERTEX); - - const auto vh_2d = fh_2d->vertex(i); - CGAL_assertion(vh_2d->info().vertex_handle_3d == v); - - auto fill_with_delaunay = - [&](auto& hole_boundary, bool is_outside_the_face) { - std::vector hole_edges(hole_boundary.begin(), hole_boundary.end()); - if(hole_boundary.size() > 2) { - fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { - auto& info = fh->info(); - info.is_outside_the_face = is_outside_the_face; - info.missing_subface = true; - info.is_in_region = 0; - info.facet_3d = {}; - *new_face_handles_out++ = fh; - })); - } else { - CGAL_assertion(hole_boundary.size() == 2); - auto [f, i] = hole_boundary.front(); - auto [g, j] = hole_boundary.back(); - g->set_neighbor(j, f); - f->set_neighbor(i, g); - } - std::for_each(hole_edges.begin(), hole_edges.end(), - [&cdt_2](CDT_2_edge e) { - auto [f, i] = cdt_2.mirror_edge(e); - f->set_constraint(i, cdt_2.is_constrained(e)); - }); - }; - auto bore_hole_and_fill_with_delaunay = - [&](auto& hole_boundary, const auto faces_to_delete, bool is_outside_the_face) { - std::for_each(faces_to_delete.begin(), faces_to_delete.end(), - [&cdt_2](CDT_2_face_handle f) { cdt_2.delete_face(f); }); - fill_with_delaunay(hole_boundary, is_outside_the_face); - }; + typename CDT_2::Locate_type lt; + int i; + const auto fh_2d = cdt_2.locate(v->point(), lt, i); + CGAL_assertion(lt == CDT_2::VERTEX); + + const auto vh_2d = fh_2d->vertex(i); + CGAL_assertion(vh_2d->info().vertex_handle_3d == v); + + auto fill_with_delaunay = [&](auto& hole_boundary, bool is_outside_the_face) { + const std::vector hole_edges(hole_boundary.begin(), hole_boundary.end()); + if(hole_boundary.size() > 2) { + fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { + auto& info = fh->info(); + info.is_outside_the_face = is_outside_the_face; + info.missing_subface = true; + info.is_in_region = 0; + info.facet_3d = {}; + *new_face_handles_out++ = fh; + })); + } else { + CGAL_assertion(hole_boundary.size() == 2); + auto [f, i] = hole_boundary.front(); + auto [g, j] = hole_boundary.back(); + g->set_neighbor(j, f); + f->set_neighbor(i, g); + } + std::for_each(hole_edges.begin(), hole_edges.end(), [&cdt_2](CDT_2_edge e) { + auto [f, i] = cdt_2.mirror_edge(e); + f->set_constraint(i, cdt_2.is_constrained(e)); + }); + }; + auto bore_hole_and_fill_with_delaunay = [&](auto& hole_boundary, const auto& faces_to_delete, + bool is_outside_the_face) { + std::for_each(faces_to_delete.begin(), faces_to_delete.end(), + [&cdt_2](CDT_2_face_handle f) { cdt_2.delete_face(f); }); + fill_with_delaunay(hole_boundary, is_outside_the_face); + }; - auto set_constrained = [&](CDT_2_edge e, bool is_constrained) { - auto [f, i] = e; - f->set_constraint(i, is_constrained); - auto [n, ni] = cdt_2.mirror_edge(e); - n->set_constraint(ni, is_constrained); - }; + auto set_constrained = [&](CDT_2_edge e, bool is_constrained) { + auto [f, i] = e; + f->set_constraint(i, is_constrained); + auto [n, ni] = cdt_2.mirror_edge(e); + n->set_constraint(ni, is_constrained); + }; - if(vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE) { + if(vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE) { - auto half_hole = [&](CDT_2_face_handle incident_fh) { - struct Result { - std::list hole_side; - boost::container::small_vector to_delete; - CDT_2_edge last_incident_edge; - } result; - auto edge_circulator = cdt_2.incident_edges(vh_2d, incident_fh); + auto half_hole = [&](CDT_2_face_handle incident_fh) { + struct Result + { + std::list hole_side; + boost::container::small_vector to_delete; + CDT_2_edge last_incident_edge; + } result; + auto edge_circulator = cdt_2.incident_edges(vh_2d, incident_fh); + --edge_circulator; + CGAL_assertion_code(auto done = edge_circulator); + while(!cdt_2.is_constrained(*edge_circulator)) { --edge_circulator; - CGAL_assertion_code(auto done = edge_circulator); - while(!cdt_2.is_constrained(*edge_circulator)) { - --edge_circulator; - CGAL_assertion(edge_circulator != done); - } - CGAL_assertion_code(done = edge_circulator); - do { - ++edge_circulator; - const auto [f, i] = *edge_circulator; - result.to_delete.push_back(f); - const auto edge = cdt_2.mirror_edge(CDT_2_edge{f, cdt_2.cw(i)}); - result.hole_side.push_back(edge); - const auto [n, ni] = edge; - n->vertex(cdt_2. cw(ni))->set_face(n); - n->vertex(cdt_2.ccw(ni))->set_face(n); - CGAL_assertion(edge_circulator != done); - } while(!cdt_2.is_constrained(*edge_circulator)); - result.last_incident_edge = *edge_circulator; - return result; - }; + CGAL_assertion(edge_circulator != done); + } + CGAL_assertion_code(done = edge_circulator); + do { + ++edge_circulator; + const auto [f, i] = *edge_circulator; + result.to_delete.push_back(f); + const auto edge = cdt_2.mirror_edge(CDT_2_edge{f, cdt_2.cw(i)}); + result.hole_side.push_back(edge); + const auto [n, ni] = edge; + n->vertex(cdt_2.cw(ni))->set_face(n); + n->vertex(cdt_2.ccw(ni))->set_face(n); + CGAL_assertion(edge_circulator != done); + } while(!cdt_2.is_constrained(*edge_circulator)); + result.last_incident_edge = *edge_circulator; + return result; + }; - auto [hole_side_1, hole_side_1_faces_to_delete, side_1_last_incident_edge] = half_hole(vh_2d->face()); - const auto [side_1_last_face, side_1_last_edge_index] = side_1_last_incident_edge; - const bool side_1_hole_is_outside_the_face = side_1_last_face->info().is_outside_the_face; + auto [hole_side_1, hole_side_1_faces_to_delete, side_1_last_incident_edge] = half_hole(vh_2d->face()); + const auto [side_1_last_face, side_1_last_edge_index] = side_1_last_incident_edge; + const bool side_1_hole_is_outside_the_face = side_1_last_face->info().is_outside_the_face; - const auto side_2_first_face = side_1_last_face->neighbor(side_1_last_edge_index); + const auto side_2_first_face = side_1_last_face->neighbor(side_1_last_edge_index); - auto [hole_side_2, hole_side_2_faces_to_delete, side_2_last_incident_edge] = half_hole(side_2_first_face); - const auto [side_2_last_face, side_2_last_edge_index] = side_2_last_incident_edge; - const bool side_2_hole_is_outside_the_face = side_2_last_face->info().is_outside_the_face; + auto [hole_side_2, hole_side_2_faces_to_delete, side_2_last_incident_edge] = half_hole(side_2_first_face); + const auto [side_2_last_face, side_2_last_edge_index] = side_2_last_incident_edge; + const bool side_2_hole_is_outside_the_face = side_2_last_face->info().is_outside_the_face; - CGAL_assertion(side_1_last_incident_edge.first != side_2_last_incident_edge.first); + CGAL_assertion(side_1_last_incident_edge.first != side_2_last_incident_edge.first); - // unmark the two constrained edges - set_constrained(side_1_last_incident_edge, false); - set_constrained(side_2_last_incident_edge, false); + // unmark the two constrained edges + set_constrained(side_1_last_incident_edge, false); + set_constrained(side_2_last_incident_edge, false); - // start boring and sewing the two half-holes: - // - we start from side 1, - // - `side_2_last_face` will temporarily be modified to close `hole_side_1` - const auto v1 = side_1_last_face->vertex(cdt_2.ccw(side_1_last_edge_index)); - side_2_last_face->set_vertex(cdt_2.cw(side_2_last_edge_index), v1); - hole_side_1.push_back(side_2_last_incident_edge); + // start boring and sewing the two half-holes: + // - we start from side 1, + // - `side_2_last_face` will temporarily be modified to close `hole_side_1` + const auto v1 = side_1_last_face->vertex(cdt_2.ccw(side_1_last_edge_index)); + side_2_last_face->set_vertex(cdt_2.cw(side_2_last_edge_index), v1); + hole_side_1.push_back(side_2_last_incident_edge); - // - bore the hole on side 1, and fill it with CDT triangles - bore_hole_and_fill_with_delaunay(hole_side_1, hole_side_1_faces_to_delete, side_1_hole_is_outside_the_face); + // - bore the hole on side 1, and fill it with CDT triangles + bore_hole_and_fill_with_delaunay(hole_side_1, hole_side_1_faces_to_delete, side_1_hole_is_outside_the_face); - // - now the edge opposite to `(side_2_last_face, side_2_last_edge_index)` is on the border of the hole - // `hole_side_2`, so we can start sewing from there - const auto side_1_border_edge = cdt_2.mirror_edge(CDT_2_edge{side_2_last_face, side_2_last_edge_index}); - hole_side_2.push_back(side_1_border_edge); - bore_hole_and_fill_with_delaunay(hole_side_2, hole_side_2_faces_to_delete, side_2_hole_is_outside_the_face); + // - now the edge opposite to `(side_2_last_face, side_2_last_edge_index)` is on the border of the hole + // `hole_side_2`, so we can start sewing from there + const auto side_1_border_edge = cdt_2.mirror_edge(CDT_2_edge{side_2_last_face, side_2_last_edge_index}); + hole_side_2.push_back(side_1_border_edge); + bore_hole_and_fill_with_delaunay(hole_side_2, hole_side_2_faces_to_delete, side_2_hole_is_outside_the_face); - // - then re-mark the edge between the two holes as a constrained edge - set_constrained(side_1_border_edge, true); - } else { - auto hole = cdt_2.make_hole(vh_2d); - fill_with_delaunay(hole, /*is_outside_the_face=*/false); - } - cdt_2.delete_vertex(vh_2d); - if(!cdt_2.CT_2::is_valid(true, 3)) { - std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << IO::oformat(v, with_point_and_info) - << " from face F#" << face_id << '\n'; - CGAL_assertion(false); - } - }; + // - then re-mark the edge between the two holes as a constrained edge + set_constrained(side_1_border_edge, true); + } else { + auto hole = cdt_2.make_hole(vh_2d); + fill_with_delaunay(hole, /*is_outside_the_face=*/false); + } + cdt_2.delete_vertex(vh_2d); + dump_face(face_id, std::string("dump_face_") + std::to_string(face_id) + "_after_removing_Steiner_vertex_" + std::to_string(vh_2d->time_stamp()) + ".off"); + + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << IO::oformat(v, with_point_and_info) + << " from face F#" << face_id << '\n'; + CGAL_assertion(false); + } + return new_face_handles_out; + } - auto type = v->ccdt_3_data().vertex_type(); + template + auto remove_Steiner_vertex_from_all_cdt_2(Vertex_handle v, + New_CDT_2_face_handles_output_iterator new_face_handles_out) + { + const auto type = v->ccdt_3_data().vertex_type(); if(type == CDT_3_vertex_type::STEINER_IN_FACE) { - auto face_id = v->ccdt_3_data().face_index(); - remove_from_cdt_2(v, type, face_id); + const auto face_id = v->ccdt_3_data().face_index(); + new_face_handles_out = remove_from_cdt_2(v, type, face_id, new_face_handles_out); } else if (type == CDT_3_vertex_type::STEINER_ON_EDGE) { const auto c_id = v->ccdt_3_data().constrained_polyline_id(*this); for(const auto& [_, face_id] : CGAL::make_range(this->incident_faces_to_polyline.equal_range(c_id))) { - remove_from_cdt_2(v, type, face_id); + new_face_handles_out = remove_from_cdt_2(v, type, face_id, new_face_handles_out); } } return new_face_handles_out; @@ -5127,9 +5136,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D write_region_to_OFF(dump_region, cdt_2); } - void dump_face(CDT_3_signed_index face_index) { + void dump_face(CDT_3_signed_index face_index, std::string filename = {}) const { const auto& cdt_2 = face_cdt_2(face_index); - std::ofstream dump_region(std::string("dump_face_") + std::to_string(face_index) + ".off"); + if(filename.empty()) filename = std::string("dump_face_") + std::to_string(face_index) + ".off"; + std::ofstream dump_region(filename); dump_region.precision(17); write_region_to_OFF(dump_region, cdt_2); } From 8d2c3170195402b1adea0c1880b919c6af0fde45 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Apr 2026 11:39:52 +0200 Subject: [PATCH 016/151] simplify the use of make_indenting_guards --- .../include/CGAL/IO/Indenting_ostream.h | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/Stream_support/include/CGAL/IO/Indenting_ostream.h b/Stream_support/include/CGAL/IO/Indenting_ostream.h index bff5f78a9858..c4afdc3ca79e 100644 --- a/Stream_support/include/CGAL/IO/Indenting_ostream.h +++ b/Stream_support/include/CGAL/IO/Indenting_ostream.h @@ -22,6 +22,9 @@ #include #include +#include +#include +#include #include #include #include @@ -300,11 +303,13 @@ using Indenting_wstream_guard = Basic_indenting_stream_guard; * This helper function creates `Basic_indenting_stream_guard` objects for * multiple streams at once. All streams will use the same indentation settings, * and their original streambufs will be automatically restored when the guards - * go out of scope. The guards are returned in a tuple. + * go out of scope. The guards are returned in a tuple. If no stream is + * provided, guards are created for `std::cout`, `std::cerr`, and `std::clog`. * * \tparam Streams The stream types (deduced from arguments) * \param spaces_per_level Number of spaces per indentation level - * \param streams The streams to apply indentation to + * \param streams The streams to apply indentation to. If omitted, the helper + * creates guards for `std::cout`, `std::cerr`, and `std::clog`. * \return A tuple of Basic_indenting_stream_guard objects * * \sa Basic_indenting_stream_guard @@ -318,7 +323,11 @@ using Indenting_wstream_guard = Basic_indenting_stream_guard; */ template auto make_indenting_guards(int spaces_per_level, Streams&... streams) { - return std::make_tuple(Basic_indenting_stream_guard(streams, spaces_per_level)...); + if constexpr (sizeof...(Streams) == 0) { + return make_indenting_guards(spaces_per_level, std::cout, std::cerr, std::clog); + } else { + return std::make_tuple(Basic_indenting_stream_guard(streams, spaces_per_level)...); + } } /** @@ -327,18 +336,24 @@ auto make_indenting_guards(int spaces_per_level, Streams&... streams) { * \brief creates indenting guards for multiple streams with a custom indent string. * * This overload allows specifying a custom indentation string instead of a number - * of spaces. + * of spaces. If no stream is provided, guards are created for `std::cout`, + * `std::cerr`, and `std::clog`. * * \tparam Streams The stream types (deduced from arguments) * \param indent_string The indentation string to use - * \param streams The streams to apply indentation to + * \param streams The streams to apply indentation to. If omitted, the helper + * creates guards for `std::cout`, `std::cerr`, and `std::clog`. * \return A tuple of Basic_indenting_stream_guard objects * * \sa Basic_indenting_stream_guard */ template auto make_indenting_guards(const std::string& indent_string, Streams&... streams) { - return std::make_tuple(Basic_indenting_stream_guard(streams, indent_string)...); + if constexpr (sizeof...(Streams) == 0) { + return make_indenting_guards(indent_string, std::cout, std::cerr, std::clog); + } else { + return std::make_tuple(Basic_indenting_stream_guard(streams, indent_string)...); + } } } // namespace IO From c58643126c80abab8b7aadf7c7df5c2ba8fc6751 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Apr 2026 13:55:35 +0200 Subject: [PATCH 017/151] move the code to cdt_impl --- ...ing_constrained_Delaunay_triangulation_3.h | 343 +++++++++--------- 1 file changed, 178 insertions(+), 165 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 217931bf833a..5ec6437ae351 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -918,10 +918,6 @@ class Conforming_constrained_Delaunay_triangulation_3 std::cout << "Number of vertices after CDT: " << cdt_impl.number_of_vertices() << "\n\n"; } } - move_Steiner_vertices_to_the_volume(); // TODO: check + DO NOT COMMIT - - // std::cerr << cdt_3_format("cdt_impl: {} vertices, {} cells\n", cdt_impl.number_of_vertices(), - // cdt_impl.number_of_cells()); } /*! @@ -1108,7 +1104,7 @@ class Conforming_constrained_Delaunay_triangulation_3 */ CDT_3_signed_index face_constraint_index(Cell_handle ch, int i) const { - return ch->ccdt_3_data().face_constraint_index(i); + return cdt_impl.face_constraint_index(ch, i); } /*! @@ -1118,7 +1114,7 @@ class Conforming_constrained_Delaunay_triangulation_3 */ CDT_3_signed_index face_constraint_index(const Facet& f) const { - return face_constraint_index(f.first, f.second); + return cdt_impl.face_constraint_index(f.first, f.second); } /*! @@ -1193,6 +1189,15 @@ class Conforming_constrained_Delaunay_triangulation_3 bool write_missing_segments_file(std::ostream& out) { return cdt_impl.write_missing_segments_file(out); } void write_all_segments_file(std::ostream& out) { cdt_impl.write_all_segments_file(out); } + // Post-processing helpers + /// \brief Moves Steiner vertices on the boundary to the volume. + /// + /// This function moves Steiner vertices that lie on edges or faces + /// of the constrained surface to inside the volume. + void move_Steiner_vertices_to_the_volume() { + cdt_impl.move_Steiner_vertices_to_the_volume(); + } + // Lightweight accessors used by tests/tools size_type number_of_vertices() const { return static_cast(cdt_impl.number_of_vertices()); } size_type number_of_cells() const { return triangulation().number_of_cells(); } @@ -1234,6 +1239,142 @@ class Conforming_constrained_Delaunay_triangulation_3 return out; }, IO_manip_tag{}); } +}; + +#ifndef DOXYGEN_RUNNING +// ---------------------------------------------------------- +// Conforming_constrained_Delaunay_triangulation_3_impl +// ---------------------------------------------------------- +template +class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_Delaunay_triangulation_3 { +public: + using Conforming_Dt = Conforming_Delaunay_triangulation_3; + using Conforming_Dt::tr; + static_assert(Conforming_Dt::t_3_is_not_movable || CGAL::is_nothrow_movable_v); + + using Vertex_handle = typename T_3::Vertex_handle; + using Edge = typename T_3::Edge; + using Facet = typename T_3::Facet; + using Cell_handle = typename T_3::Cell_handle; + + using Geom_traits = typename T_3::Geom_traits; + using Point_3 = typename T_3::Point; + using Segment_3 = typename Geom_traits::Segment_3; + using Vector_3 = typename Geom_traits::Vector_3; + using Locate_type = typename T_3::Locate_type; + using size_type = typename T_3::size_type; + + using Vertex_marker = CDT_3_vertex_marker; + using Cell_marker = CDT_3_cell_marker; + + using Face_index = CDT_3_signed_index; + + using Conforming_Dt::Conforming_Dt; + + using Tds = typename T_3::Triangulation_data_structure; + using Concurrency_tag = typename Tds::Concurrency_tag; + + + static std::string io_signature() { + return Get_io_signature()(); + } + + struct Is_constrained { + const Conforming_constrained_Delaunay_triangulation_3_impl& cdt; + bool operator()(Facet f) const { + return cdt.is_facet_constrained(f); + } + }; + + using Constrained_facets_iterator = boost::filter_iterator; + using Constrained_facets_range = CGAL::Iterator_range; + + Constrained_facets_iterator constrained_facets_begin() const { + return {Is_constrained{*this}, this->all_facets_begin(), this->all_facets_end()}; + } + Constrained_facets_iterator constrained_facets_end() const { + return {Is_constrained{*this}, this->all_facets_end(), this->all_facets_end()}; + } + Constrained_facets_range constrained_facets() const { + return {constrained_facets_begin(), constrained_facets_end()}; + } +private: + struct CDT_2_types + { + struct Projection_traits : public Projection_traits_3 + { + using Projection_traits_3::Projection_traits_3; // inherit cstr + }; + static_assert(CGAL::is_nothrow_movable_v); + + struct Vertex_info + { + Vertex_handle vertex_handle_3d = {}; + }; + + using Color_value_type = std::int8_t; + struct Face_info + { + Color_value_type is_outside_the_face = -1; + Color_value_type is_in_region = 0; + std::bitset<3> is_edge_also_in_3d_triangulation = 0; + bool missing_subface = true; + Facet facet_3d = {}; + }; + using Vb1 = Triangulation_vertex_base_with_info_2; + using Vb = Triangulation_simplex_base_with_time_stamp; + using Fb1 = Triangulation_face_base_with_info_2; + using Fb = Constrained_triangulation_face_base_2; + using TDS = Triangulation_data_structure_2; + using Itag = No_constraint_intersection_requiring_constructions_tag; + using CDT_base = Constrained_Delaunay_triangulation_2; + using CDT = Constrained_triangulation_plus_2; + using CT = typename CDT::Ctr; + + template struct CDT_2_dual_color_map + { + using category = boost::read_write_property_map_tag; + using reference = Color_value_type&; + using value_type = Color_value_type; + using key_type = typename CDT::Face_handle; + + friend reference get(CDT_2_dual_color_map, key_type fh) { return fh->info().*member_ptr; } + friend void put(CDT_2_dual_color_map, key_type fh, value_type value) { fh->info().*member_ptr = value; } + }; + using Color_map_is_outside_the_face = CDT_2_dual_color_map<&Face_info::is_outside_the_face>; + using Color_map_is_in_region = CDT_2_dual_color_map<&Face_info::is_in_region>; + }; // CDT_2_types + using CDT_2 = typename CDT_2_types::CDT; + using CT_2 = typename CDT_2_types::CT; + using CDT_2_traits = typename CDT_2_types::Projection_traits; + using CDT_2_face_handle = typename CDT_2::Face_handle; + using CDT_2_edge = typename CDT_2::Edge; + static_assert(CGAL::cdt_3_msvc_2019_or_older() || CGAL::is_nothrow_movable_v); + +protected: + struct Null_normal_error : Error_exception { + Null_normal_error(std::string msg, std::string file, int line) + : Error_exception("CGAL CDT_3", msg, file, line) + { + } + }; + + struct PLC_error : Error_exception { + int face_index; + int region_index; + + PLC_error(std::string msg, std::string file, int line, int face_index, int region_index) + : Error_exception("CGAL CDT_3", msg, file, line), face_index(face_index), region_index(region_index) + { + } + }; + + using Constraint_hierarchy = typename Conforming_Dt::Constraint_hierarchy; + using Subconstraint = typename Constraint_hierarchy::Subconstraint; +public: + using Constrained_polyline_id = typename Constraint_hierarchy::Constraint_id; + +protected: protected: template @@ -1278,7 +1419,6 @@ class Conforming_constrained_Delaunay_triangulation_3 template void dump_link_mesh_of_vertex_to_ply(Vertex_handle v, const Offsets& component_offsets, const Facets& facets) const { - using Point_3 = typename Traits::Point_3; using Mesh = CGAL::Surface_mesh; using face_descriptor = typename Mesh::Face_index; Mesh link_mesh; @@ -1293,11 +1433,11 @@ class Conforming_constrained_Delaunay_triangulation_3 facet_id < end; ++facet_id) { const auto& [cell, facet_index, _] = facets[facet_id]; - if(triangulation().is_infinite(Facet(cell, facet_index))) { + if(tr().is_infinite(Facet(cell, facet_index))) { continue; } for(int i = 0; i < 3; ++i) { - points.push_back(cell->vertex(Triangulation::vertex_triple_index(facet_index, i))->point()); + points.push_back(cell->vertex(Tds::vertex_triple_index(facet_index, i))->point()); } // end for each vertex of the facet // swap the orientation: our facets are oriented toward the center of the star component // but we want them to be oriented outward @@ -1336,7 +1476,7 @@ class Conforming_constrained_Delaunay_triangulation_3 auto move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { std::cerr << "Moving Steiner vertex " << IO::oformat(v, With_point_and_info_tag{}) << " to the volume\n"; std::size_t nb_of_incident_cells{0}; - triangulation().incident_cells( + tr().incident_cells( v, boost::make_function_output_iterator( [nb_of_incident_cells_ptr = &nb_of_incident_cells](Cell_handle) { ++(*nb_of_incident_cells_ptr); })); std::cerr << " - Number of incident cells: " << nb_of_incident_cells << '\n'; @@ -1412,156 +1552,10 @@ class Conforming_constrained_Delaunay_triangulation_3 std::cerr << " " << "nb of components: " << component_offsets.size() - 1 << '\n'; std::size_t nb_of_new_2d_faces = 0; - cdt_impl.remove_Steiner_vertex_from_all_cdt_2(v, CGAL::Counting_output_iterator{&nb_of_new_2d_faces}); + this->remove_Steiner_vertex_from_all_cdt_2(v, CGAL::Counting_output_iterator{&nb_of_new_2d_faces}); std::cerr << " - number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; } - auto move_Steiner_vertices_to_the_volume() { - for(auto v: triangulation().finite_vertex_handles()) { - auto type = v->ccdt_3_data().vertex_type(); - if(type == CDT_3_vertex_type::STEINER_IN_FACE) - move_one_Steiner_vertex_on_face_to_the_volume(v); - else if (type == CDT_3_vertex_type::STEINER_ON_EDGE) - move_one_Steiner_vertex_to_the_volume(v); - } - } -}; - -#ifndef DOXYGEN_RUNNING -// ---------------------------------------------------------- -// Conforming_constrained_Delaunay_triangulation_3_impl -// ---------------------------------------------------------- -template -class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_Delaunay_triangulation_3 { -public: - using Conforming_Dt = Conforming_Delaunay_triangulation_3; - using Conforming_Dt::tr; - static_assert(Conforming_Dt::t_3_is_not_movable || CGAL::is_nothrow_movable_v); - - using Vertex_handle = typename T_3::Vertex_handle; - using Edge = typename T_3::Edge; - using Facet = typename T_3::Facet; - using Cell_handle = typename T_3::Cell_handle; - - using Geom_traits = typename T_3::Geom_traits; - using Point_3 = typename T_3::Point; - using Segment_3 = typename Geom_traits::Segment_3; - using Vector_3 = typename Geom_traits::Vector_3; - using Locate_type = typename T_3::Locate_type; - using size_type = typename T_3::size_type; - - using Vertex_marker = CDT_3_vertex_marker; - using Cell_marker = CDT_3_cell_marker; - - using Face_index = CDT_3_signed_index; - - using Conforming_Dt::Conforming_Dt; - - using Tds = typename T_3::Triangulation_data_structure; - using Concurrency_tag = typename Tds::Concurrency_tag; - - - static std::string io_signature() { - return Get_io_signature()(); - } - - struct Is_constrained { - const Conforming_constrained_Delaunay_triangulation_3_impl& cdt; - bool operator()(Facet f) const { - return cdt.is_facet_constrained(f); - } - }; - - using Constrained_facets_iterator = boost::filter_iterator; - using Constrained_facets_range = CGAL::Iterator_range; - - Constrained_facets_iterator constrained_facets_begin() const { - return {Is_constrained{*this}, this->all_facets_begin(), this->all_facets_end()}; - } - Constrained_facets_iterator constrained_facets_end() const { - return {Is_constrained{*this}, this->all_facets_end(), this->all_facets_end()}; - } - Constrained_facets_range constrained_facets() const { - return {constrained_facets_begin(), constrained_facets_end()}; - } -private: - struct CDT_2_types - { - struct Projection_traits : public Projection_traits_3 - { - using Projection_traits_3::Projection_traits_3; // inherit cstr - }; - static_assert(CGAL::is_nothrow_movable_v); - - struct Vertex_info - { - Vertex_handle vertex_handle_3d = {}; - }; - - using Color_value_type = std::int8_t; - struct Face_info - { - Color_value_type is_outside_the_face = -1; - Color_value_type is_in_region = 0; - std::bitset<3> is_edge_also_in_3d_triangulation = 0; - bool missing_subface = true; - Facet facet_3d = {}; - }; - using Vb1 = Triangulation_vertex_base_with_info_2; - using Vb = Triangulation_simplex_base_with_time_stamp; - using Fb1 = Triangulation_face_base_with_info_2; - using Fb = Constrained_triangulation_face_base_2; - using TDS = Triangulation_data_structure_2; - using Itag = No_constraint_intersection_requiring_constructions_tag; - using CDT_base = Constrained_Delaunay_triangulation_2; - using CDT = Constrained_triangulation_plus_2; - using CT = typename CDT::Ctr; - - template struct CDT_2_dual_color_map - { - using category = boost::read_write_property_map_tag; - using reference = Color_value_type&; - using value_type = Color_value_type; - using key_type = typename CDT::Face_handle; - - friend reference get(CDT_2_dual_color_map, key_type fh) { return fh->info().*member_ptr; } - friend void put(CDT_2_dual_color_map, key_type fh, value_type value) { fh->info().*member_ptr = value; } - }; - using Color_map_is_outside_the_face = CDT_2_dual_color_map<&Face_info::is_outside_the_face>; - using Color_map_is_in_region = CDT_2_dual_color_map<&Face_info::is_in_region>; - }; // CDT_2_types - using CDT_2 = typename CDT_2_types::CDT; - using CT_2 = typename CDT_2_types::CT; - using CDT_2_traits = typename CDT_2_types::Projection_traits; - using CDT_2_face_handle = typename CDT_2::Face_handle; - using CDT_2_edge = typename CDT_2::Edge; - static_assert(CGAL::cdt_3_msvc_2019_or_older() || CGAL::is_nothrow_movable_v); - -protected: - struct Null_normal_error : Error_exception { - Null_normal_error(std::string msg, std::string file, int line) - : Error_exception("CGAL CDT_3", msg, file, line) - { - } - }; - - struct PLC_error : Error_exception { - int face_index; - int region_index; - - PLC_error(std::string msg, std::string file, int line, int face_index, int region_index) - : Error_exception("CGAL CDT_3", msg, file, line), face_index(face_index), region_index(region_index) - { - } - }; - - using Constraint_hierarchy = typename Conforming_Dt::Constraint_hierarchy; - using Subconstraint = typename Constraint_hierarchy::Subconstraint; -public: - using Constrained_polyline_id = typename Constraint_hierarchy::Constraint_id; - -protected: - void register_facet_to_be_constrained(Cell_handle cell, int facet_index) { const auto face_id = static_cast(cell->ccdt_3_data().face_constraint_index(facet_index)); this->face_constraint_misses_subfaces_set(face_id); @@ -1721,12 +1715,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D switch(tr().dimension()) { case 3: { typename T_3::Conflict_tester_3 tester(p, this); - this->find_conflicts(ch, tester, triple_of_output_iterators); + tr().find_conflicts(ch, tester, triple_of_output_iterators); break; } // dim 3 case 2: { typename T_3::Conflict_tester_2 tester(p, this); - this->find_conflicts(ch, tester, triple_of_output_iterators); + tr().find_conflicts(ch, tester, triple_of_output_iterators); break; } // dim 2 default: CGAL_error(); @@ -1985,6 +1979,16 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return f.first->ccdt_3_data().is_facet_constrained(f.second); } + CDT_3_signed_index face_constraint_index(Cell_handle ch, int i) const + { + return ch->ccdt_3_data().face_constraint_index(i); + } + + CDT_3_signed_index face_constraint_index(const Facet& f) const + { + return face_constraint_index(f.first, f.second); + } + auto number_of_constrained_facets() const { return std::count_if(tr().all_facets_begin(), tr().all_facets_end(), @@ -2354,6 +2358,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } + auto move_Steiner_vertices_to_the_volume() { + for(auto v: this->finite_vertex_handles()) { + auto type = v->ccdt_3_data().vertex_type(); + if(type == CDT_3_vertex_type::STEINER_IN_FACE) + move_one_Steiner_vertex_on_face_to_the_volume(v); + else if (type == CDT_3_vertex_type::STEINER_ON_EDGE) + move_one_Steiner_vertex_to_the_volume(v); + } + } private: @@ -4502,10 +4515,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D template New_CDT_2_face_handles_output_iterator - remove_from_cdt_2(Vertex_handle v, - CDT_3_vertex_type vertex_type, - Face_index face_id, - New_CDT_2_face_handles_output_iterator new_face_handles_out) + remove_Steiner_vertex_from_cdt_2(Vertex_handle v, + CDT_3_vertex_type vertex_type, + Face_index face_id, + New_CDT_2_face_handles_output_iterator new_face_handles_out) { dump_face(face_id); std::cerr << "Remove Steiner vertex " << IO::oformat(v, with_point_and_info) << " from CDT_2 of face F#" << face_id @@ -4650,12 +4663,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto type = v->ccdt_3_data().vertex_type(); if(type == CDT_3_vertex_type::STEINER_IN_FACE) { const auto face_id = v->ccdt_3_data().face_index(); - new_face_handles_out = remove_from_cdt_2(v, type, face_id, new_face_handles_out); + new_face_handles_out = remove_Steiner_vertex_from_cdt_2(v, type, face_id, new_face_handles_out); } else if (type == CDT_3_vertex_type::STEINER_ON_EDGE) { const auto c_id = v->ccdt_3_data().constrained_polyline_id(*this); for(const auto& [_, face_id] : CGAL::make_range(this->incident_faces_to_polyline.equal_range(c_id))) { - new_face_handles_out = remove_from_cdt_2(v, type, face_id, new_face_handles_out); + new_face_handles_out = remove_Steiner_vertex_from_cdt_2(v, type, face_id, new_face_handles_out); } } return new_face_handles_out; From 4a774bfc0dfd9c3a6cbf06af213163bbe685b83c Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Apr 2026 13:57:25 +0200 Subject: [PATCH 018/151] add command line line option --move-Steiner-vertices-to-the-volume --- .../Constrained_triangulation_3/cdt_3_from_off.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 425b49ee7e86..edcf4ac7832e 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -142,6 +142,7 @@ Usage: cdt_3_from_off [options] input.off output.off --debug-restore-faces: debug face restoration process --use-finite-edges-map: use a hash map for finite edges (default: false) + --move-Steiner-vertices-to-the-volume: move Steiner vertices to the volume (default: false) --use-epeck-for-normals/--no-use-epeck-for-normals: use exact kernel for normal computations (default: false) --use-epeck-for-Steiner-points/--no-use-epeck-for-Steiner-points: use exact kernel for Steiner point computations (default: false) @@ -184,6 +185,7 @@ struct CDT_options bool debug_polygon_insertion = false; bool debug_restore_faces = false; bool use_finite_edges_map = false; + bool move_Steiner_vertices_to_the_volume = false; bool use_epeck_for_normals = false; bool use_epeck_for_Steiner_points = false; bool call_is_valid = true; @@ -284,6 +286,8 @@ CDT_options::CDT_options(int argc, char* argv[]) { debug_restore_faces = true; } else if(arg == "--use-finite-edges-map"sv) { use_finite_edges_map = true; + } else if(arg == "--move-Steiner-vertices-to-the-volume"sv) { + move_Steiner_vertices_to_the_volume = true; } else if(arg == "--no-use-epeck-for-normals"sv) { use_epeck_for_normals = false; } else if(arg == "--no-use-epeck-for-Steiner-points"sv) { @@ -735,6 +739,12 @@ int go(Mesh mesh, CDT_options options) { CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); } + // Move Steiner vertices to the volume if requested + if(options.move_Steiner_vertices_to_the_volume) { + auto _ = CGAL::CDT_3_OUTPUT_TASK_guard(); + cdt.move_Steiner_vertices_to_the_volume(); + } + return EXIT_SUCCESS; } From 4e61dfbe050e0ed816aecc07853dcdb2960d16ce Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Apr 2026 13:57:46 +0200 Subject: [PATCH 019/151] restore documentation of `is_valid()` --- .../Conforming_constrained_Delaunay_triangulation_3.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 5ec6437ae351..ab39f8055fb7 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1157,6 +1157,13 @@ class Conforming_constrained_Delaunay_triangulation_3 /// \name Checking /// These methods are mainly a debugging help for the users of advanced features. /// @{ + /*! + \brief returns whether the triangulation is valid. + When `verbose` is set to `true`, messages describing the first invalidity encountered are + printed. + */ + bool is_valid(bool verbose = false) const { return cdt_impl.is_valid(verbose); } + /// @} // ----------------------- @@ -1183,7 +1190,6 @@ class Conforming_constrained_Delaunay_triangulation_3 // Validation helpers bool is_conforming() const { return cdt_impl.is_conforming(); } - bool is_valid(bool verbose = false) const { return cdt_impl.is_valid(verbose); } // IO helpers for debugging bool write_missing_segments_file(std::ostream& out) { return cdt_impl.write_missing_segments_file(out); } From e538997790b5fdb900bef92882238997f73f54c8 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Apr 2026 14:01:54 +0200 Subject: [PATCH 020/151] use the simplified API of make_indenting_guards() --- ...Conforming_constrained_Delaunay_triangulation_3.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index ab39f8055fb7..1dc8ec3bd510 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -3542,10 +3542,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } auto register_internal_constrained_facet = [this](Facet f) { this->register_facet_to_be_constrained(f); }; - std::optional indent_guards; + std::optional indent_guards; if(this->debug().copy_triangulation_into_hole() || this->debug().regions()) { std::cerr << "# upper cavity\n"; - indent_guards.emplace(IO::make_indenting_guards(" ", std::cerr, std::cout, std::clog)); + indent_guards.emplace(IO::make_indenting_guards(" ")); } [[maybe_unused]] const auto [upper_cavity_triangulation, vertices_of_upper_cavity, map_upper_cavity_vertices_to_ambient_vertices, facets_of_upper_cavity, @@ -3558,7 +3558,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().copy_triangulation_into_hole() || this->debug().regions()) { indent_guards.reset(); std::cerr << "# lower cavity\n"; - indent_guards.emplace(IO::make_indenting_guards(" ", std::cerr, std::cout, std::clog)); + indent_guards.emplace(IO::make_indenting_guards(" ")); } [[maybe_unused]] const auto [lower_cavity_triangulation, vertices_of_lower_cavity, map_lower_cavity_vertices_to_ambient_vertices, facets_of_lower_cavity, @@ -4798,6 +4798,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::swap(v0, v1); } if constexpr(cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { + auto indent_guard = CGAL::IO::make_indenting_guards("| "); debug_dump_edge_region_intersection(face_index, region_index, fh_region, region_vertices, border_edges, intersecting_edges.size(), v0, v1, {cell, index_v0, index_v1}, triangle_vertices); } @@ -4826,7 +4827,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; auto test_cell = [&](Cell_handle cell) { - std::optional indent_guards; + std::optional indent_guards; if constexpr(cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format("test_cell #{}\n {}\n {}\n {}\n {}\n", cell->time_stamp(), @@ -4834,7 +4835,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D IO::oformat(cell->vertex(1), with_point_and_info), IO::oformat(cell->vertex(2), with_point_and_info), IO::oformat(cell->vertex(3), with_point_and_info)); - indent_guards.emplace(CGAL::IO::make_indenting_guards("| ", std::cerr, std::cout, std::clog)); + indent_guards.emplace(CGAL::IO::make_indenting_guards("| ")); } bool does_intersect = false; for(int i = 0; i < 4; ++i) { @@ -4937,7 +4938,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return IO::oformat(std::forward(args)..., with_point_and_info); }; - auto indent_guard = CGAL::IO::make_indenting_guards("| ", std::cerr, std::cout, std::clog); std::cerr << cdt_3_format("intersecting edge #{}: ( {} {} )\n" "intersected triangle vertices: {}\n" " {}\n" From 3524bb1f1b11fb8995c756c9a7828c324946c38f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Apr 2026 16:55:26 +0200 Subject: [PATCH 021/151] use the right guard, and display timings for move_Steiner_vertices_to_the_volume --- .../include/CGAL/Conforming_Delaunay_triangulation_3.h | 7 +++++++ .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index e9fbe1b906f9..200b31b41c64 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -185,6 +185,7 @@ inline auto& tasks_manager() { COMPUTE_DISTANCES, CONFORMING, CDT, + MOVE_STEINER_VERTICES, OUTPUT, VALIDATION, NB_TASKS @@ -199,6 +200,7 @@ inline auto& tasks_manager() { __itt_string_handle_create("CDT_3: compute distances"), __itt_string_handle_create("CDT_3: conforming"), __itt_string_handle_create("CDT_3: cdt"), + __itt_string_handle_create("CDT_3: move Steiner vertices"), __itt_string_handle_create("CDT_3: outputs"), __itt_string_handle_create("CDT_3: validation") }; @@ -237,6 +239,7 @@ inline auto& tasks_manager() { Scope_guard COMPUTE_DISTANCES_TASK_guard() { return make_task_scope_guard(COMPUTE_DISTANCES); } Scope_guard CONFORMING_TASK_guard() { return make_task_scope_guard(CONFORMING); } Scope_guard CDT_TASK_guard() { return make_task_scope_guard(CDT); } + Scope_guard MOVE_STEINER_VERTICES_TASK_guard() { return make_task_scope_guard(MOVE_STEINER_VERTICES); } Scope_guard OUTPUT_TASK_guard() { return make_task_scope_guard(OUTPUT); } Scope_guard VALIDATION_TASK_guard() { return make_task_scope_guard(VALIDATION); } @@ -278,6 +281,10 @@ inline auto CDT_3_OUTPUT_TASK_guard() { return CDT_3::internal::tasks_manager().OUTPUT_TASK_guard(); } +inline auto CDT_3_MOVE_STEINER_VERTICES_TASK_guard() { + return CDT_3::internal::tasks_manager().MOVE_STEINER_VERTICES_TASK_guard(); +} + inline auto CDT_3_VALIDATION_TASK_guard() { return CDT_3::internal::tasks_manager().VALIDATION_TASK_guard(); } diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index edcf4ac7832e..25c498420df7 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -741,8 +741,12 @@ int go(Mesh mesh, CDT_options options) { // Move Steiner vertices to the volume if requested if(options.move_Steiner_vertices_to_the_volume) { - auto _ = CGAL::CDT_3_OUTPUT_TASK_guard(); + auto move_vertices_guard = CGAL::CDT_3_MOVE_STEINER_VERTICES_TASK_guard(); cdt.move_Steiner_vertices_to_the_volume(); + if(!options.quiet) { + std::cout << "[timings] moved Steiner vertices to the volume in " + << move_vertices_guard.time_ms() << " ms\n"; + } } return EXIT_SUCCESS; From 09a61d1bfe79f4ece137368ec214ec0457e4e654 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Apr 2026 16:56:42 +0200 Subject: [PATCH 022/151] remove a O(n) assertion (`n` is the number of constrained edges) --- .../include/CGAL/Conforming_Delaunay_triangulation_3.h | 6 ++---- .../internal/Polyline_constraint_hierarchy_2.h | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 200b31b41c64..668ba8e5c202 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -895,16 +895,14 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { } auto constraint_extremities(Constrained_polyline_id c_id) const { - CGAL_assertion(std::find(this->constraint_hierarchy.constraints_begin(), - this->constraint_hierarchy.constraints_end(), c_id) != this->constraint_hierarchy.constraints_end()); - CGAL_assertion(this->constraint_hierarchy.vertices_in_constraint_begin(c_id) != - this->constraint_hierarchy.vertices_in_constraint_end(c_id)); + CGAL_assertion(this->constraint_hierarchy.is_valid_constraint(c_id)); if(debug().constraint_hierarchy()) { std::cerr << "constraint " << static_cast(c_id.vl_ptr()) << " has " << c_id.vl_ptr()->skip_size() << " vertices\n"; } const auto begin = this->constraint_hierarchy.vertices_in_constraint_begin(c_id); const auto end = this->constraint_hierarchy.vertices_in_constraint_end(c_id); + CGAL_assertion(begin != end); const auto c_va = *begin; const auto c_vb = *std::prev(end); return std::make_pair(c_va, c_vb); diff --git a/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h b/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h index 2d0a2fc9c4cc..446c5b61f458 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h @@ -578,6 +578,9 @@ class Polyline_constraint_hierarchy_2 // Query bool is_subconstraint(T va, T vb) const; + bool is_valid_constraint(Constraint_id cid) const { + return priv.constraints_set.find(cid) != priv.constraints_set.end(); + } Vertex_it vertices_in_constraint_begin(Constraint_id cid) const { return cid.begin(); } From ce668570b06493b3eb7c6b076a5245e4b8d6c969 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 13 Apr 2026 15:27:08 +0200 Subject: [PATCH 023/151] move_Steiner_vertices_to_the_volume=true by default --- .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 25c498420df7..21f43e6d7e6b 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -142,7 +142,7 @@ Usage: cdt_3_from_off [options] input.off output.off --debug-restore-faces: debug face restoration process --use-finite-edges-map: use a hash map for finite edges (default: false) - --move-Steiner-vertices-to-the-volume: move Steiner vertices to the volume (default: false) + --move-Steiner-vertices-to-the-volume/--no-move-Steiner-vertices-to-the-volume: move Steiner vertices to the volume (default: true) --use-epeck-for-normals/--no-use-epeck-for-normals: use exact kernel for normal computations (default: false) --use-epeck-for-Steiner-points/--no-use-epeck-for-Steiner-points: use exact kernel for Steiner point computations (default: false) @@ -185,7 +185,7 @@ struct CDT_options bool debug_polygon_insertion = false; bool debug_restore_faces = false; bool use_finite_edges_map = false; - bool move_Steiner_vertices_to_the_volume = false; + bool move_Steiner_vertices_to_the_volume = true; bool use_epeck_for_normals = false; bool use_epeck_for_Steiner_points = false; bool call_is_valid = true; @@ -286,6 +286,8 @@ CDT_options::CDT_options(int argc, char* argv[]) { debug_restore_faces = true; } else if(arg == "--use-finite-edges-map"sv) { use_finite_edges_map = true; + } else if(arg == "--no-move-Steiner-vertices-to-the-volume"sv) { + move_Steiner_vertices_to_the_volume = false; } else if(arg == "--move-Steiner-vertices-to-the-volume"sv) { move_Steiner_vertices_to_the_volume = true; } else if(arg == "--no-use-epeck-for-normals"sv) { From e8b2b7aeaf5977bef4ec829896316dc5323f0df7 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 13 Apr 2026 16:00:10 +0200 Subject: [PATCH 024/151] WIP: refactoring --- ...ing_constrained_Delaunay_triangulation_3.h | 74 ++++++++++++------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index b91651adbb2f..a4d88b2dc846 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1479,14 +1479,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D out.close(); } - auto move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { - std::cerr << "Moving Steiner vertex " << IO::oformat(v, With_point_and_info_tag{}) << " to the volume\n"; - std::size_t nb_of_incident_cells{0}; - tr().incident_cells( - v, boost::make_function_output_iterator( - [nb_of_incident_cells_ptr = &nb_of_incident_cells](Cell_handle) { ++(*nb_of_incident_cells_ptr); })); - std::cerr << " - Number of incident cells: " << nb_of_incident_cells << '\n'; - + struct Star_components_facets { // output type of `collect_star_components_facets()` enum class Type_of_facet { INCIDENT, OPPOSITE @@ -1504,6 +1497,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } }; + boost::container::small_vector facets; + boost::container::small_vector component_offsets = {1}; + }; + + Star_components_facets collect_star_components_facets(Vertex_handle v) const { + Star_components_facets out; + auto register_cell_and_check_if_new = [seen_cells = CGAL::unordered_flat_set{}](Cell_handle c) mutable { @@ -1511,52 +1511,72 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return inserted; }; - boost::container::small_vector facets_of_star_components; - boost::container::small_vector component_offsets(1); - boost::container::small_vector cells_in_other_components; auto first_cell = v->cell(); - facets_of_star_components.emplace_back(first_cell, first_cell->index(v), Type_of_facet::OPPOSITE); + out.facets.emplace_back(first_cell, first_cell->index(v), Star_components_facets::Type_of_facet::OPPOSITE); register_cell_and_check_if_new(first_cell); - auto queue_head=0u; + + boost::container::small_vector cells_in_other_components; + // - `out.facets` is part of the result, and a queue as well, with `queue_head` as the index of + // the head of the queue: popping is done by incrementing `queue_head`, and pushing is done by + // adding new facets at the end of the vector. + // - `cells_in_other_components` is used to keep track of cells that belong to other components: + // when we finish processing a component, we take one of these cells (if not yet processed) to + // start processing the next one component. + auto queue_head = 0u; do { - auto [c, facet_index, facet_type] = facets_of_star_components[queue_head++]; - if(facet_type == Type_of_facet::OPPOSITE) { - for (int i=0; i<4; ++i) { - if (c->vertex(i) == v) continue; + auto [c, facet_index, facet_type] = out.facets[queue_head++]; + if(facet_type == Star_components_facets::Type_of_facet::OPPOSITE) { + for(int i = 0; i < 4; ++i) { + if(c->vertex(i) == v) continue; Cell_handle next = c->neighbor(i); const bool facet_c_to_next_is_constrained = is_facet_constrained(Facet(c, i)); if(facet_c_to_next_is_constrained) { - facets_of_star_components.emplace_back(c, i, Type_of_facet::INCIDENT); + out.facets.emplace_back(c, i, Star_components_facets::Type_of_facet::INCIDENT); cells_in_other_components.push_back(next); } else { auto next_is_a_new_cell = register_cell_and_check_if_new(next); if(next_is_a_new_cell) { - facets_of_star_components.emplace_back(next, next->index(v), Type_of_facet::OPPOSITE); + out.facets.emplace_back(next, next->index(v), Star_components_facets::Type_of_facet::OPPOSITE); } } } } - if(queue_head == facets_of_star_components.size()) { - std::cerr << " - Partition " << component_offsets.size() << " has " - << facets_of_star_components.size() - component_offsets.back() << " facets\n"; - component_offsets.push_back(facets_of_star_components.size()); + + if(queue_head == out.facets.size()) { + std::cerr << " - Partition " << out.component_offsets.size() << " has " + << out.facets.size() - out.component_offsets.back() << " facets\n"; + out.component_offsets.push_back(out.facets.size()); while(!cells_in_other_components.empty()) { auto other_cell = cells_in_other_components.back(); cells_in_other_components.pop_back(); auto other_cell_is_a_new_cell = register_cell_and_check_if_new(other_cell); if(other_cell_is_a_new_cell) { - facets_of_star_components.emplace_back(other_cell, other_cell->index(v), Type_of_facet::OPPOSITE); + out.facets.emplace_back(other_cell, other_cell->index(v), Star_components_facets::Type_of_facet::OPPOSITE); break; } } } - } while(queue_head != facets_of_star_components.size()); + } while(queue_head != out.facets.size()); + + return out; + } + + auto move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { + std::cerr << "Moving Steiner vertex " << IO::oformat(v, With_point_and_info_tag{}) << " to the volume\n"; + const auto nb_of_incident_cells = std::invoke([&]() { + std::size_t nb_of_incident_cells{0}; + tr().incident_cells(v, CGAL::Counting_output_iterator(&nb_of_incident_cells)); + return nb_of_incident_cells; + }); + std::cerr << " - Number of incident cells: " << nb_of_incident_cells << '\n'; + + auto star_components = collect_star_components_facets(v); { // DEBUG, DO NOT COMMIT as it is - dump_link_mesh_of_vertex_to_ply(v, component_offsets, facets_of_star_components); // DO NOT COMMIT AS IT IS: use a debug flag to enable it + dump_link_mesh_of_vertex_to_ply(v, star_components.component_offsets, star_components.facets); // DO NOT COMMIT AS IT IS: use a debug flag to enable it } - std::cerr << " " << "nb of components: " << component_offsets.size() - 1 << '\n'; + std::cerr << " " << "nb of components: " << star_components.component_offsets.size() - 1 << '\n'; std::size_t nb_of_new_2d_faces = 0; this->remove_Steiner_vertex_from_all_cdt_2(v, CGAL::Counting_output_iterator{&nb_of_new_2d_faces}); std::cerr << " - number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; From b727ca07e9062fd9a2d381c7fc746edc4b1a8677 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 13 Apr 2026 19:47:25 +0200 Subject: [PATCH 025/151] WIP: refactoring split `dump_link_mesh_of_vertex_to_ply` into 3 functions --- ...ing_constrained_Delaunay_triangulation_3.h | 89 ++++++++++++------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index a4d88b2dc846..ad237471a2a1 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1424,37 +1424,59 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } template - void dump_link_mesh_of_vertex_to_ply(Vertex_handle v, const Offsets& component_offsets, const Facets& facets) const { + CGAL::Surface_mesh + construct_star_component_mesh(std::size_t component_index, + const Offsets& component_offsets, + const Facets& facets) const + { + CGAL::Surface_mesh component_mesh; + std::vector points; + std::vector> triangles; + std::size_t vertex_index = 0; + for(std::size_t facet_id = component_offsets[component_index - 1], + end = component_offsets[component_index]; + facet_id < end; ++facet_id) + { + const auto& [cell, facet_index, _] = facets[facet_id]; + if(tr().is_infinite(Facet(cell, facet_index))) { + continue; + } + for(int i = 0; i < 3; ++i) { + points.push_back(cell->vertex(Tds::vertex_triple_index(facet_index, i))->point()); + } // end for each vertex of the facet + // swap the orientation: our facets are oriented toward the center of the star component + // but we want them to be oriented outward + triangles.push_back({vertex_index, vertex_index + 2, vertex_index + 1}); + vertex_index += 3; + } // end for each facet in the component + CGAL::Polygon_mesh_processing::merge_duplicate_points_in_polygon_soup(points, triangles); + + std::vector::Face_index>> + triangle_index_to_face_descriptor; + CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh( + points, + triangles, + component_mesh, + CGAL::parameters::polygon_to_face_output_iterator(std::back_inserter(triangle_index_to_face_descriptor))); + return component_mesh; + } + + std::optional compute_center_point_if_possible(const CGAL::Surface_mesh& component_mesh) const { + return CGAL::Polygon_mesh_processing::kernel_point(component_mesh, parameters::allow_open_input(true)); + } + + template + CGAL::Surface_mesh + construct_link_mesh_for_debug(const Offsets& component_offsets, + const Facets& facets) const + { using Mesh = CGAL::Surface_mesh; using face_descriptor = typename Mesh::Face_index; Mesh link_mesh; auto [patch_id_map, _] = link_mesh.template add_property_map("f:patch_id", 0); + for(std::size_t component_index = 1; component_index < component_offsets.size(); ++component_index) { - std::vector points; - std::vector> triangles; - std::size_t vertex_index = 0; - Mesh component_mesh; - for(std::size_t facet_id = component_offsets[component_index - 1], - end = component_offsets[component_index]; - facet_id < end; ++facet_id) - { - const auto& [cell, facet_index, _] = facets[facet_id]; - if(tr().is_infinite(Facet(cell, facet_index))) { - continue; - } - for(int i = 0; i < 3; ++i) { - points.push_back(cell->vertex(Tds::vertex_triple_index(facet_index, i))->point()); - } // end for each vertex of the facet - // swap the orientation: our facets are oriented toward the center of the star component - // but we want them to be oriented outward - triangles.push_back({vertex_index, vertex_index + 2, vertex_index + 1}); - vertex_index += 3; - } // end for each facet in the component - CGAL::Polygon_mesh_processing::merge_duplicate_points_in_polygon_soup(points, triangles); - - std::vector> triangle_index_to_face_descriptor; - CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, triangles, component_mesh, - CGAL::parameters::polygon_to_face_output_iterator(std::back_inserter(triangle_index_to_face_descriptor))); + Mesh component_mesh = construct_star_component_mesh(component_index, component_offsets, facets); std::vector new_faces; auto out_it = boost::make_function_output_iterator( @@ -1463,14 +1485,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(auto fd : new_faces) { put(patch_id_map, fd, static_cast(component_index)); } - auto opt_kernel_center_point = CGAL::Polygon_mesh_processing::kernel_point(component_mesh); - if(opt_kernel_center_point.has_value()) { - auto kernel_center_point = *opt_kernel_center_point; - std::cerr << " - kernel center point of component " << component_index << ": " << kernel_center_point - << " (distance to vertex: " << CGAL::sqrt(CGAL::squared_distance(kernel_center_point, v->point())) << ")" - << "\n"; - } - } // end for each component + } + return link_mesh; + } + + template + void dump_link_mesh_of_vertex_to_ply(Vertex_handle v, const Offsets& component_offsets, const Facets& facets) const { + const auto link_mesh = construct_link_mesh_for_debug(component_offsets, facets); std::stringstream filename; filename << "dump_link_mesh_vertex" << IO::oformat(v, With_offset_tag{}) << ".ply"; From cbc9941e722b56aebb419d3185bec0c1411d8a8e Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 13 Apr 2026 21:43:05 +0200 Subject: [PATCH 026/151] WIP: refactoring - `collect_star_components_facets`` returns more information - and it is called with an incident constrained facet of `v` --- ...ing_constrained_Delaunay_triangulation_3.h | 248 +++++++++++++----- 1 file changed, 180 insertions(+), 68 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index ad237471a2a1..e5c4fc3d9118 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1412,17 +1412,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return std::nullopt; } - auto move_one_Steiner_vertex_on_face_to_the_volume(Vertex_handle v) { - auto is_constrained = [this](Facet f) { return is_facet_constrained(f); }; - - const auto face_id = v->ccdt_3_data().face_index(); - auto incident_constrained_facet_opt = find_in_incident_facets(v, is_constrained); - CGAL_assertion(incident_constrained_facet_opt.has_value()); - auto incident_constrained_facet = *incident_constrained_facet_opt; - CGAL_assertion(face_constraint_index(incident_constrained_facet) == face_id); - move_one_Steiner_vertex_to_the_volume(v); - } - template CGAL::Surface_mesh construct_star_component_mesh(std::size_t component_index, @@ -1502,8 +1491,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D struct Star_components_facets { // output type of `collect_star_components_facets()` enum class Type_of_facet { - INCIDENT, - OPPOSITE + INCIDENT_TO_V, + OPPOSITE_TO_V }; struct Facet_of_star_component { @@ -1518,12 +1507,24 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } }; - boost::container::small_vector facets; - boost::container::small_vector component_offsets = {1}; + template + using small_vector = boost::container::small_vector; + + small_vector facets; + small_vector component_offsets = {0}; + small_vector face_ids_between_consecutive_components; + small_vector> component_incident_constraint_face_ids; }; - Star_components_facets collect_star_components_facets(Vertex_handle v) const { - Star_components_facets out; + Star_components_facets collect_star_components_facets(Vertex_handle v, Facet incident_constrained_facet) const { + const auto [first_cell, first_facet_index] = incident_constrained_facet; + const auto first_facet_face_id = face_constraint_index(incident_constrained_facet); + + const auto vertex_Steiner_type = vertex_type(v); + CGAL_assertion(vertex_Steiner_type == CDT_3_vertex_type::STEINER_IN_FACE || + vertex_Steiner_type == CDT_3_vertex_type::STEINER_ON_EDGE); + + Star_components_facets out; // the returned object, yet to be filled auto register_cell_and_check_if_new = [seen_cells = CGAL::unordered_flat_set{}](Cell_handle c) mutable @@ -1532,75 +1533,181 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return inserted; }; - auto first_cell = v->cell(); - out.facets.emplace_back(first_cell, first_cell->index(v), Star_components_facets::Type_of_facet::OPPOSITE); - register_cell_and_check_if_new(first_cell); - - boost::container::small_vector cells_in_other_components; - // - `out.facets` is part of the result, and a queue as well, with `queue_head` as the index of - // the head of the queue: popping is done by incrementing `queue_head`, and pushing is done by - // adding new facets at the end of the vector. - // - `cells_in_other_components` is used to keep track of cells that belong to other components: - // when we finish processing a component, we take one of these cells (if not yet processed) to - // start processing the next one component. - auto queue_head = 0u; - do { - auto [c, facet_index, facet_type] = out.facets[queue_head++]; - if(facet_type == Star_components_facets::Type_of_facet::OPPOSITE) { - for(int i = 0; i < 4; ++i) { + // `cells_in_other_components` is used to keep track of cells that belong to other components: + // when we finish processing a component, we take one of these cells (if not yet processed) to + // start processing the next component. + // -> Start with one of the facets incident to `v` that is constrained, and explore the star + // of `v` through unconstrained facets as long as possible; + boost::container::small_vector, 128> cells_in_other_components = { + { first_cell, first_facet_face_id } + }; + + while(!cells_in_other_components.empty()) { + auto [other_cell, component_first_incident_face_id] = cells_in_other_components.back(); + cells_in_other_components.pop_back(); + auto other_cell_is_a_new_cell = register_cell_and_check_if_new(other_cell); + if(!other_cell_is_a_new_cell) continue; + + // start a new component from this cell + out.facets.emplace_back(other_cell, other_cell->index(v), Star_components_facets::Type_of_facet::OPPOSITE_TO_V); + + out.face_ids_between_consecutive_components.push_back(component_first_incident_face_id); + out.component_incident_constraint_face_ids.push_back({component_first_incident_face_id, -1}); // placeholder for the next component + auto& current_component_incident_constraint_face_ids = out.component_incident_constraint_face_ids.back(); + cells_in_other_components.clear(); // clear it here, and fill it again with the cells opposite to a constrained + // facet during the exploration of the current component + + std::cerr << " starting a new component through face with id " << component_first_incident_face_id << '\n'; + + // - `out.facets` is part of the result, and a queue as well, with `queue_head` as the index of + // the head of the queue: popping is done by incrementing `queue_head`, and pushing is done by + // adding new facets at the end of the vector. + for(auto queue_head = out.facets.size() - 1; queue_head != out.facets.size(); ++queue_head) { + const auto [c, facet_index, facet_type] = out.facets[queue_head]; + if(facet_type == Star_components_facets::Type_of_facet::INCIDENT_TO_V) continue; + + for(int i = 0; i < 4; ++i) + { + // follow only cells in the star of `v` if(c->vertex(i) == v) continue; - Cell_handle next = c->neighbor(i); - const bool facet_c_to_next_is_constrained = is_facet_constrained(Facet(c, i)); + const Cell_handle next_cell = c->neighbor(i); + + const auto facet_c_to_next_is_constrained = is_facet_constrained(Facet(c, i)); if(facet_c_to_next_is_constrained) { - out.facets.emplace_back(c, i, Star_components_facets::Type_of_facet::INCIDENT); - cells_in_other_components.push_back(next); - } else { - auto next_is_a_new_cell = register_cell_and_check_if_new(next); + out.facets.emplace_back(c, i, Star_components_facets::Type_of_facet::INCIDENT_TO_V); + const auto constrained_facet_face_id = face_constraint_index(c, i); + if(-1 == current_component_incident_constraint_face_ids[1] && + constrained_facet_face_id != current_component_incident_constraint_face_ids[0]) + { + current_component_incident_constraint_face_ids[1] = constrained_facet_face_id; + } + CGAL_assertion(constrained_facet_face_id == current_component_incident_constraint_face_ids[0] || + constrained_facet_face_id == current_component_incident_constraint_face_ids[1]); + + // unless the Steiner vertex is in a face, the component will always be incident to + // two different constraint faces, and therefore we can prefer to push only cells that + // are on the other side a constraint with a different face id. + if(constrained_facet_face_id != component_first_incident_face_id || + vertex_Steiner_type == CDT_3_vertex_type::STEINER_IN_FACE ) + { + cells_in_other_components.push_back({next_cell, constrained_facet_face_id}); + } + } else { // Facet(c, i), between `c` and `next_cell` is not constrained + auto next_is_a_new_cell = register_cell_and_check_if_new(next_cell); if(next_is_a_new_cell) { - out.facets.emplace_back(next, next->index(v), Star_components_facets::Type_of_facet::OPPOSITE); + out.facets.emplace_back(next_cell, next_cell->index(v), Star_components_facets::Type_of_facet::OPPOSITE_TO_V); } } - } - } - - if(queue_head == out.facets.size()) { - std::cerr << " - Partition " << out.component_offsets.size() << " has " - << out.facets.size() - out.component_offsets.back() << " facets\n"; - out.component_offsets.push_back(out.facets.size()); - while(!cells_in_other_components.empty()) { - auto other_cell = cells_in_other_components.back(); - cells_in_other_components.pop_back(); - auto other_cell_is_a_new_cell = register_cell_and_check_if_new(other_cell); - if(other_cell_is_a_new_cell) { - out.facets.emplace_back(other_cell, other_cell->index(v), Star_components_facets::Type_of_facet::OPPOSITE); - break; - } - } - } - } while(queue_head != out.facets.size()); + } // end for each facet of `c` + } // end while there is still a facet in the current component to process + + // end of the current component reached, start a new one if there is any cell in + // another component that we haven't processed yet + + std::cerr << " - Star component #" << out.component_offsets.size() << " has " + << (out.facets.size() - out.component_offsets.back()) << " facets.\n" + << " It is incident to constraint faces with ids: {" + << IO::oformat(current_component_incident_constraint_face_ids) << "}\n"; + + out.component_offsets.push_back(out.facets.size()); + + } // end while there is still a component to process + + CGAL_assertion_code(const auto nb_of_components = out.component_incident_constraint_face_ids.size()); + + CGAL_assertion(false == out.facets.empty()); + CGAL_assertion(nb_of_components > 1); + CGAL_assertion(out.component_offsets.size() == nb_of_components + 1); + + CGAL_assertion(out.component_offsets.front() == 0); + CGAL_assertion(out.component_offsets.back() == out.facets.size()); + CGAL_assertion(out.face_ids_between_consecutive_components.size() == nb_of_components); + + if(vertex_Steiner_type == CDT_3_vertex_type::STEINER_IN_FACE) { + CGAL_assertion(nb_of_components == 2); + CGAL_assertion(std::all_of(out.component_incident_constraint_face_ids.begin(), out.component_incident_constraint_face_ids.end(), + [first_facet_face_id](const std::array& face_ids) + { + return face_ids[0] == first_facet_face_id && face_ids[1] == -1; + })); + CGAL_assertion(std::all_of(out.face_ids_between_consecutive_components.begin(), out.face_ids_between_consecutive_components.end(), + [first_facet_face_id](const Face_index& face_id) + { + return face_id == first_facet_face_id; + })); + } else { + CGAL_assertion(vertex_Steiner_type == CDT_3_vertex_type::STEINER_ON_EDGE); + CGAL_assertion(std::adjacent_find(out.face_ids_between_consecutive_components.begin(), + out.face_ids_between_consecutive_components.end()) == + out.face_ids_between_consecutive_components.end()); + CGAL_assertion(std::all_of(out.component_incident_constraint_face_ids.begin(), out.component_incident_constraint_face_ids.end(), + [](const std::array& face_ids) + { + return face_ids[1] != -1; + })); + } return out; } - auto move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { + bool move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { std::cerr << "Moving Steiner vertex " << IO::oformat(v, With_point_and_info_tag{}) << " to the volume\n"; + + auto is_constrained = [this](Facet f) { return is_facet_constrained(f); }; + const auto incident_constrained_facet_opt = find_in_incident_facets(v, is_constrained); + CGAL_assertion(incident_constrained_facet_opt.has_value()); + + const auto incident_constrained_facet = *incident_constrained_facet_opt; + const auto nb_of_incident_cells = std::invoke([&]() { std::size_t nb_of_incident_cells{0}; tr().incident_cells(v, CGAL::Counting_output_iterator(&nb_of_incident_cells)); return nb_of_incident_cells; }); + std::cerr << " - Number of incident cells: " << nb_of_incident_cells << '\n'; - auto star_components = collect_star_components_facets(v); + const auto star_components = collect_star_components_facets(v, incident_constrained_facet); + const auto nb_of_star_components = star_components.component_incident_constraint_face_ids.size(); + + const auto components_central_points = std::invoke([&]() { + std::optional> central_points{std::in_place}; + central_points->reserve(nb_of_star_components); + for(std::size_t component_index = 1; component_index <= nb_of_star_components; ++component_index) { + auto component_mesh = + construct_star_component_mesh(component_index, star_components.component_offsets, star_components.facets); + auto opt_kernel_center_point = compute_center_point_if_possible(component_mesh); + std::cerr << " - kernel center point of component #" << component_index << ": "; + if(opt_kernel_center_point.has_value()) { + auto kernel_center_point = *opt_kernel_center_point; + central_points->push_back(kernel_center_point); + std::cerr << kernel_center_point + << " (distance to vertex: " << CGAL::sqrt(CGAL::squared_distance(kernel_center_point, v->point())) + << ")" + << "\n"; + } else { + std::cerr << "none (the component is either degenerate or open) ABORT\n"; + central_points.reset(); + return central_points; + } + } + return central_points; + }); // end compute central points for each component { // DEBUG, DO NOT COMMIT as it is dump_link_mesh_of_vertex_to_ply(v, star_components.component_offsets, star_components.facets); // DO NOT COMMIT AS IT IS: use a debug flag to enable it } std::cerr << " " << "nb of components: " << star_components.component_offsets.size() - 1 << '\n'; + + if(!components_central_points.has_value()) { + std::cerr << " - No central point could be computed for at least one of the components, aborting moving the vertex to the volume\n"; + return false; + } std::size_t nb_of_new_2d_faces = 0; this->remove_Steiner_vertex_from_all_cdt_2(v, CGAL::Counting_output_iterator{&nb_of_new_2d_faces}); std::cerr << " - number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; + return true; } void register_facet_to_be_constrained(Cell_handle cell, int facet_index) { @@ -2405,13 +2512,18 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } + auto vertex_type(Vertex_handle v) const { + return v->ccdt_3_data().vertex_type(); + } + + bool is_Steiner_vertex(Vertex_handle v) const { + auto type = vertex_type(v); + return type == CDT_3_vertex_type::STEINER_IN_FACE || type == CDT_3_vertex_type::STEINER_ON_EDGE; + } + auto move_Steiner_vertices_to_the_volume() { - for(auto v: this->finite_vertex_handles()) { - auto type = v->ccdt_3_data().vertex_type(); - if(type == CDT_3_vertex_type::STEINER_IN_FACE) - move_one_Steiner_vertex_on_face_to_the_volume(v); - else if (type == CDT_3_vertex_type::STEINER_ON_EDGE) - move_one_Steiner_vertex_to_the_volume(v); + for(auto v : this->finite_vertex_handles()) { + if(is_Steiner_vertex(v)) move_one_Steiner_vertex_to_the_volume(v); } } @@ -4597,8 +4709,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D info.facet_3d = {}; *new_face_handles_out++ = fh; })); - } else { - CGAL_assertion(hole_boundary.size() == 2); + } else { // hole_boundary.size() <= 2 (degenerated hole) + CGAL_assertion(hole_boundary.size() == 2); // size == 1 is not possible auto [f, i] = hole_boundary.front(); auto [g, j] = hole_boundary.back(); g->set_neighbor(j, f); From 5113305907f8ef89cf90479d6e182b396817b67a Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 13 Apr 2026 22:06:02 +0200 Subject: [PATCH 027/151] new debug flag and command line option Now the debug outputs about moving Steiner vertices are correctly protected and indented. --- .../Conforming_Delaunay_triangulation_3.h | 4 + ...ing_constrained_Delaunay_triangulation_3.h | 89 ++++++++++++------- .../cdt_3_from_off.cpp | 8 +- 3 files changed, 70 insertions(+), 31 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index b979c8c6d10f..622b2deac240 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -94,6 +94,7 @@ struct Debug_options { debug_geometric_errors, debug_polygon_insertion, debug_restore_faces, + debug_move_Steiner_vertices, display_statistics, use_epeck_for_normals, use_epeck_for_Steiner_points, @@ -153,6 +154,9 @@ struct Debug_options { bool restore_faces() const { return get(Flag::debug_restore_faces); } void restore_faces(bool b) { get(Flag::debug_restore_faces) = b; } + bool move_Steiner_vertices() const { return get(Flag::debug_move_Steiner_vertices); } + void move_Steiner_vertices(bool b) { get(Flag::debug_move_Steiner_vertices) = b; } + bool display_statistics() const { return get(Flag::display_statistics); } void display_statistics(bool b) { get(Flag::display_statistics) = b; } diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index e5c4fc3d9118..f91c0c9d0c13 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1557,8 +1557,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D cells_in_other_components.clear(); // clear it here, and fill it again with the cells opposite to a constrained // facet during the exploration of the current component - std::cerr << " starting a new component through face with id " << component_first_incident_face_id << '\n'; - + if(this->debug().move_Steiner_vertices()) { + std::cerr << " (starting a new component through face with id " << component_first_incident_face_id << ")\n"; + } // - `out.facets` is part of the result, and a queue as well, with `queue_head` as the index of // the head of the queue: popping is done by incrementing `queue_head`, and pushing is done by // adding new facets at the end of the vector. @@ -1604,10 +1605,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // end of the current component reached, start a new one if there is any cell in // another component that we haven't processed yet - std::cerr << " - Star component #" << out.component_offsets.size() << " has " - << (out.facets.size() - out.component_offsets.back()) << " facets.\n" - << " It is incident to constraint faces with ids: {" - << IO::oformat(current_component_incident_constraint_face_ids) << "}\n"; + if(this->debug().move_Steiner_vertices()) { + std::cerr << " - Star component #" << out.component_offsets.size() << " has " + << (out.facets.size() - out.component_offsets.back()) << " facets.\n" + << " It is incident to constraint faces with ids: {" + << IO::oformat(current_component_incident_constraint_face_ids) << "}\n"; + } out.component_offsets.push_back(out.facets.size()); @@ -1651,7 +1654,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } bool move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { - std::cerr << "Moving Steiner vertex " << IO::oformat(v, With_point_and_info_tag{}) << " to the volume\n"; + std::optional indent_guards; + if(this->debug().move_Steiner_vertices()) { + std::cerr << "Moving Steiner vertex " << IO::oformat(v, With_point_and_info_tag{}) << " to the volume\n"; + indent_guards.emplace(IO::make_indenting_guards(" |")); + } auto is_constrained = [this](Facet f) { return is_facet_constrained(f); }; const auto incident_constrained_facet_opt = find_in_incident_facets(v, is_constrained); @@ -1665,11 +1672,17 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return nb_of_incident_cells; }); - std::cerr << " - Number of incident cells: " << nb_of_incident_cells << '\n'; + if(this->debug().move_Steiner_vertices()) { + std::cerr << "- Number of incident cells: " << nb_of_incident_cells << '\n'; + } const auto star_components = collect_star_components_facets(v, incident_constrained_facet); const auto nb_of_star_components = star_components.component_incident_constraint_face_ids.size(); + if(this->debug().move_Steiner_vertices()) { + std::cerr << "- Nb of components or the star: " << star_components.component_offsets.size() - 1 << '\n'; + } + const auto components_central_points = std::invoke([&]() { std::optional> central_points{std::in_place}; central_points->reserve(nb_of_star_components); @@ -1677,16 +1690,23 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto component_mesh = construct_star_component_mesh(component_index, star_components.component_offsets, star_components.facets); auto opt_kernel_center_point = compute_center_point_if_possible(component_mesh); - std::cerr << " - kernel center point of component #" << component_index << ": "; + if(this->debug().move_Steiner_vertices()) { + std::cerr << " - kernel center point of component #" << component_index << ": "; + } if(opt_kernel_center_point.has_value()) { auto kernel_center_point = *opt_kernel_center_point; central_points->push_back(kernel_center_point); - std::cerr << kernel_center_point - << " (distance to vertex: " << CGAL::sqrt(CGAL::squared_distance(kernel_center_point, v->point())) + if(this->debug().move_Steiner_vertices()) { + std::cerr << kernel_center_point + << " (distance to vertex: " << CGAL::sqrt(CGAL::squared_distance(kernel_center_point, v->point())) << ")" << "\n"; + } } else { - std::cerr << "none (the component is either degenerate or open) ABORT\n"; + if(this->debug().move_Steiner_vertices()) { + auto _ = IO::make_color_guards(std::cerr); + std::cerr << "none (the component is either degenerate or open) ABORT\n"; + } central_points.reset(); return central_points; } @@ -1694,19 +1714,22 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return central_points; }); // end compute central points for each component - { // DEBUG, DO NOT COMMIT as it is - dump_link_mesh_of_vertex_to_ply(v, star_components.component_offsets, star_components.facets); // DO NOT COMMIT AS IT IS: use a debug flag to enable it + if(this->debug().move_Steiner_vertices()) { + dump_link_mesh_of_vertex_to_ply(v, star_components.component_offsets, star_components.facets); } - std::cerr << " " << "nb of components: " << star_components.component_offsets.size() - 1 << '\n'; - if(!components_central_points.has_value()) { - std::cerr << " - No central point could be computed for at least one of the components, aborting moving the vertex to the volume\n"; + if(this->debug().move_Steiner_vertices()) { + auto _ = IO::make_color_guards(std::cerr); + std::cerr << "- No central point could be computed for at least one of the components, aborting moving the vertex to the volume\n"; + } return false; } std::size_t nb_of_new_2d_faces = 0; this->remove_Steiner_vertex_from_all_cdt_2(v, CGAL::Counting_output_iterator{&nb_of_new_2d_faces}); - std::cerr << " - number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; + if(this->debug().move_Steiner_vertices()) { + std::cerr << "- number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; + } return true; } @@ -4680,14 +4703,17 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D Face_index face_id, New_CDT_2_face_handles_output_iterator new_face_handles_out) { - dump_face(face_id); - std::cerr << "Remove Steiner vertex " << IO::oformat(v, with_point_and_info) << " from CDT_2 of face F#" << face_id - << '\n'; auto& cdt_2 = non_const_face_cdt_2(face_id); - if(!cdt_2.CT_2::is_valid(true, 3)) { - std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << IO::oformat(v, with_point_and_info) - << " from face F#" << face_id << '\n'; - CGAL_assertion(false); + + if(this->debug().move_Steiner_vertices()) { + dump_face(face_id); + std::cerr << " - remove Steiner vertex " << IO::oformat(v, with_point_and_info) << " from CDT_2 of face F#" << face_id + << '\n'; + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << IO::oformat(v, with_point_and_info) + << " from face F#" << face_id << '\n'; + CGAL_assertion(false); + } } typename CDT_2::Locate_type lt; @@ -4806,12 +4832,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D fill_with_delaunay(hole, /*is_outside_the_face=*/false); } cdt_2.delete_vertex(vh_2d); - dump_face(face_id, std::string("dump_face_") + std::to_string(face_id) + "_after_removing_Steiner_vertex_" + std::to_string(vh_2d->time_stamp()) + ".off"); + if(this->debug().move_Steiner_vertices()) { + dump_face(face_id, std::string("dump_face_") + std::to_string(face_id) + "_after_removing_Steiner_vertex_" + + std::to_string(vh_2d->time_stamp()) + ".off"); - if(!cdt_2.CT_2::is_valid(true, 3)) { - std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << IO::oformat(v, with_point_and_info) - << " from face F#" << face_id << '\n'; - CGAL_assertion(false); + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << IO::oformat(v, with_point_and_info) + << " from face F#" << face_id << '\n'; + CGAL_assertion(false); + } } return new_face_handles_out; } diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 21f43e6d7e6b..03079162c44c 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -126,6 +126,7 @@ Usage: cdt_3_from_off [options] input.off output.off --debug-Steiner-points: debug Steiner point insertion --debug-Steiner-points-construction: debug Steiner point construction + --debug-move-Steiner-vertices-to-the-volume: debug moving Steiner vertices to the volume --debug-input-faces: debug input faces --debug-missing-regions: debug missing regions --debug-regions: debug regions @@ -170,6 +171,7 @@ struct CDT_options bool read_mesh_with_operator = false; bool debug_Steiner_points = false; bool debug_Steiner_points_construction = false; + bool debug_move_Steiner_vertices = false; bool debug_input_faces = false; bool debug_missing_regions = false; bool debug_regions = false; @@ -256,6 +258,8 @@ CDT_options::CDT_options(int argc, char* argv[]) { debug_Steiner_points = true; } else if(arg == "--debug-Steiner-points-construction"sv) { debug_Steiner_points_construction = true; + } else if(arg == "--debug-move-Steiner-vertices-to-the-volume"sv) { + debug_move_Steiner_vertices = true; } else if(arg == "--debug-input-faces"sv) { debug_input_faces = true; } else if(arg == "--debug-missing-regions"sv) { @@ -334,6 +338,7 @@ CGAL::CDT_3::Debug_options cdt_debug_options(const CDT_options& options) { cdt_debug.verbose_special_cases(options.debug_verbose_special_cases); cdt_debug.encroaching_vertices(options.debug_encroaching_vertices); cdt_debug.conforming_validation(options.debug_conforming_validation); + cdt_debug.move_Steiner_vertices(options.debug_move_Steiner_vertices); cdt_debug.constraint_hierarchy(options.debug_constraint_hierarchy); cdt_debug.geometric_errors(options.debug_geometric_errors); cdt_debug.polygon_insertion(options.debug_polygon_insertion); @@ -746,8 +751,9 @@ int go(Mesh mesh, CDT_options options) { auto move_vertices_guard = CGAL::CDT_3_MOVE_STEINER_VERTICES_TASK_guard(); cdt.move_Steiner_vertices_to_the_volume(); if(!options.quiet) { - std::cout << "[timings] moved Steiner vertices to the volume in " + std::cout << "\n[timings] moved Steiner vertices to the volume in " << move_vertices_guard.time_ms() << " ms\n"; + std::cout << cdt.statistics() << "\n"; } } From 0540374a0387e505aec567c3a4b5135cdda97a98 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 15 Apr 2026 11:45:41 +0200 Subject: [PATCH 028/151] a bit of refactoring --- ...ing_constrained_Delaunay_triangulation_3.h | 69 +++++++++++++------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 649c1c3749b7..a7890789e773 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -12,9 +12,6 @@ #ifndef CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H #define CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H -#include -#include -#include #include #include @@ -46,8 +43,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -55,8 +54,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -1641,7 +1642,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } return out; - } + } // end collect_star_components_facets(..) bool move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { std::optional indent_guards; @@ -1721,7 +1722,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "- number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; } return true; - } + } // end move_one_Steiner_vertex_to_the_volume(..) void register_facet_to_be_constrained(Cell_handle cell, int facet_index) { const auto face_id = static_cast(cell->ccdt_3_data().face_constraint_index(facet_index)); @@ -2772,12 +2773,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void search_for_missing_subfaces(CDT_3_signed_index polygon_constraint_id) { - const CDT_2& cdt_2 = face_cdt_2(polygon_constraint_id); + const CDT_2& cdt_2 = this->face_cdt_2(polygon_constraint_id); - face_constraint_misses_subfaces_reset(static_cast(polygon_constraint_id)); + this->face_constraint_misses_subfaces_reset(static_cast(polygon_constraint_id)); for(const auto fh: cdt_2.all_face_handles()) { - if(fh->info().is_outside_the_face) continue; + if(fh->info().is_outside_the_face) { + fh->info().missing_subface = false; + continue; + } const auto v0 = fh->vertex(0)->info().vertex_handle_3d; const auto v1 = fh->vertex(1)->info().vertex_handle_3d; const auto v2 = fh->vertex(2)->info().vertex_handle_3d; @@ -2785,7 +2789,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D int i, j, k; if(!tr().is_facet(v0, v1, v2, c, i, j, k)) { fh->info().missing_subface = true; - face_constraint_misses_subfaces_set(static_cast(polygon_constraint_id)); + this->face_constraint_misses_subfaces_set(static_cast(polygon_constraint_id)); #if CGAL_CDT_3_DEBUG_MISSING_TRIANGLES std::cerr << cdt_3_format("Missing triangle in polygon #{}:\n", polygon_constraint_id); write_triangle(std::cerr, v0, v1, v2); @@ -4584,19 +4588,24 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } void recheck_for_missing_subfaces() { - for(int i = 0, end = face_constraint_misses_subfaces.size(); i < end; ++i) { + CGAL::Real_timer timer; + timer.start(); + for(CDT_3_signed_index i = 0, end = signed_number_of_faces(); i < end; ++i) { if(this->face_data[i].skip_face == false) { search_for_missing_subfaces(i); } } + if(this->debug().display_statistics()) { + timer.stop(); + std::cout << "[timings] recheck_for_missing_subfaces in " << timer.time() << " s\n"; + } } template void restore_constrained_Delaunay(Args&&... args) { this->is_Delaunay = false; - for(CDT_3_signed_index i = 0, end = static_cast (face_constraint_misses_subfaces.size()); i < end; - ++i) + for(CDT_3_signed_index i = 0, end = signed_number_of_faces(); i < end; ++i) { CDT_2& cdt_2 = non_const_face_cdt_2(i); try { @@ -4614,7 +4623,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } if(this->debug().input_faces()) { - for(CDT_3_signed_index i = 0, end = static_cast (face_constraint_misses_subfaces.size()); i < end; ++i) { + for(CDT_3_signed_index i = 0, end = signed_number_of_faces(); i < end; ++i) { dump_face(i); } } @@ -4631,7 +4640,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "restore_face(" << i << ") incomplete, back to conforming...\n"; } Conforming_Dt::restore_Delaunay(insert_in_conflict_visitor); - // restart from the beginning, as the state of the triangulation has changed + // and then restart the loop at the beginning, to be sure that all faces are treated i = face_constraint_misses_subfaces_find_first(); } } @@ -4639,7 +4648,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D [](const auto& fd) { return fd.skip_face; })) { std::vector failed_faces; - for(CDT_3_signed_index i = 0, end = static_cast(face_data.size()); i < end; ++i) { + for(CDT_3_signed_index i = 0, end = signed_number_of_faces(); i < end; ++i) { if(face_data[i].skip_face) { failed_faces.push_back(i); } @@ -4661,7 +4670,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D throw Constrained_triangulation_insertion_exception(failed_faces); } CGAL_assertion_code(recheck_for_missing_subfaces()); - CGAL_assertion_msg(face_constraint_misses_subfaces_find_first() == face_constraint_misses_subfaces_npos, + CGAL_assertion_msg(are_there_any_face_constraint_misses_subfaces() == false, "All faces have been restored, but the triangulation is a CDT. This should not happen."); } @@ -5405,10 +5414,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } bool write_missing_subfaces_file(std::ostream& out) { - const auto npos = face_constraint_misses_subfaces_npos; - auto i = face_constraint_misses_subfaces_find_first(); - bool has_missing_subfaces = i != npos; - while(i != npos) { + const bool has_missing_subfaces = are_there_any_face_constraint_misses_subfaces(); + loop_on_face_constraint_misses_subfaces([&](auto i) { const CDT_2& cdt = face_cdt_2(i); for(const auto fh: cdt.finite_face_handles()) { if (false == fh->info().is_outside_the_face && @@ -5417,8 +5424,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D write_2d_triangle(out, fh); } } - i = face_constraint_misses_subfaces_find_next(i); - } + }); return has_missing_subfaces; } @@ -5448,6 +5454,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; protected: + CDT_3_signed_index signed_number_of_faces() const { + return static_cast(face_data.size()); + } + // Accessor functions for Face_data members CDT_2& non_const_face_cdt_2(std::size_t face_id) { return face_data[face_id].cdt_2; @@ -5537,6 +5547,21 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void face_constraint_misses_subfaces_reset(std::size_t pos) { face_constraint_misses_subfaces[pos] = false; } + + template + auto loop_on_face_constraint_misses_subfaces(F f) const { + auto i = face_constraint_misses_subfaces_find_first(); + bool has_any = i != face_constraint_misses_subfaces_npos; + while(i != face_constraint_misses_subfaces_npos) { + f(i); + i = face_constraint_misses_subfaces_find_next(i); + } + return has_any; + } + + auto are_there_any_face_constraint_misses_subfaces() const { + return face_constraint_misses_subfaces_find_first() != face_constraint_misses_subfaces_npos; + } }; // end of Conforming_constrained_Delaunay_triangulation_3_impl #endif // DOXYGEN_RUNNING From 22ca76aa2003b971d106ca803c1c772eea54f7c5 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:34:28 +0200 Subject: [PATCH 029/151] fix create_output_finalizer so that --bisect works as intended --- .../cdt_3_from_off.cpp | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 03079162c44c..c604b31cd071 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -376,6 +377,11 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& return [&cdt, &options]() { auto _ = CGAL::CDT_3_OUTPUT_TASK_guard(); { + if(std::uncaught_exceptions() > 0) { + std::cerr << "create_output_finalizer: an exception is being thrown, the triangulation may be left in an " + "invalid state.\n"; + return; + } auto dump_tets_to_medit = [](std::string fname, const std::vector &points, const std::vector> &indexed_tetra, @@ -425,21 +431,24 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& } std::vector points(cdt.number_of_vertices()); + std::size_t idx = 0; for(auto v: cdt.finite_vertex_handles()) { - points.at(v->time_stamp() -1) = v->point(); + // renumber the vertices: there might be holes in the numbering due to removed vertices + v->set_time_stamp(++idx); + points.at(idx - 1) = v->point(); } std::vector> indexed_tetra; indexed_tetra.reserve(cdt.number_of_cells()); for(auto ch: cdt.finite_cell_handles()) { if(cells_map[ch] > 0) { - indexed_tetra.push_back({ch->vertex(0)->time_stamp() -1, - ch->vertex(1)->time_stamp() -1, - ch->vertex(2)->time_stamp() -1, - ch->vertex(3)->time_stamp() -1}); + indexed_tetra.push_back({ch->vertex(0)->time_stamp() - 1, + ch->vertex(1)->time_stamp() - 1, + ch->vertex(2)->time_stamp() - 1, + ch->vertex(3)->time_stamp() - 1}); } } - std::vector cell_idsl(indexed_tetra.size(), 1); - dump_tets_to_medit(options.output_filename + ".mesh", points, indexed_tetra, cell_idsl); + std::vector cell_ids(indexed_tetra.size(), 1); + dump_tets_to_medit(options.output_filename + ".mesh", points, indexed_tetra, cell_ids); } { std::ofstream dump(options.output_filename); From 84971192c53d3bb894f7b4376c7f8cc80d12089e Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:35:05 +0200 Subject: [PATCH 030/151] rename a variable --- Triangulation_2/include/CGAL/Triangulation_2.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index 70ea13d98b11..d1952044aa4a 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -2106,18 +2106,18 @@ fill_hole_delaunay(Triangulation_2& tr, const auto& p0 = v0->point(); const auto& p1 = v1->point(); - for(auto hit = hole.cbegin(); hit != hole_last_one; ++hit) + for(auto hole_edge_it = hole.cbegin(); hole_edge_it != hole_last_one; ++hole_edge_it) { - const Vertex_handle vv = ccw_vertex(*hit); + const Vertex_handle vv = ccw_vertex(*hole_edge_it); if (tr.is_infinite(vv)) { - if(tr.is_infinite(v2)) cut_after = hit; + if(tr.is_infinite(v2)) cut_after = hole_edge_it; } else { // vv is a finite vertex const auto& p = vv->point(); if (tr.orientation(p0,p1,p) == COUNTERCLOCKWISE) { if (tr.is_infinite(v2) || tr.side_of_oriented_circle(p0,p1,v2->point(),p,true) == ON_POSITIVE_SIDE) { - cut_after = hit; v2 = vv; + cut_after = hole_edge_it; v2 = vv; } } } From 53a3311f59e4ae078f3b7643ce18a8ea44e1a3b8 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:35:15 +0200 Subject: [PATCH 031/151] cosmetic --- Triangulation_2/include/CGAL/Triangulation_2.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index d1952044aa4a..e3a57c42c36e 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -2142,7 +2142,6 @@ fill_hole_delaunay(Triangulation_2& tr, else if (auto [fn, in] = hole.back(); fn->vertex(Tr::cw(in)) == v2) { Face_handle newf = create_face(fn,in,ff,ii); hole.back() = Edge(newf,1); - hole.back() = Edge(newf,1); } else { Face_handle newf = create_face(ff,ii,v2); @@ -2157,7 +2156,7 @@ fill_hole_delaunay(Triangulation_2& tr, hole.push_front(Edge(newf,1)); // close the hole with the edge (newf, 1) } - } + } // while( ! hole_list.empty()) return fit; } From ab79c4d8b93951638331bcdae6637ad80ba4b3b0 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:35:36 +0200 Subject: [PATCH 032/151] remove a comment with a false assertion --- TDS_3/include/CGAL/Triangulation_utils_3.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/TDS_3/include/CGAL/Triangulation_utils_3.h b/TDS_3/include/CGAL/Triangulation_utils_3.h index 58299cf071aa..f9032e5eaf4d 100644 --- a/TDS_3/include/CGAL/Triangulation_utils_3.h +++ b/TDS_3/include/CGAL/Triangulation_utils_3.h @@ -54,10 +54,6 @@ const int Triangulation_utils_base_3::ccw_map[3] = {1, 2, 0}; template < class T > const int Triangulation_utils_base_3::cw_map[3] = {2, 0, 1}; -// We derive from Triangulation_cw_ccw_2 because we still use cw() and ccw() -// in the 2D part of the code. Ideally, this should go away when we reuse -// T2D entirely. - struct Triangulation_utils_3 : public Triangulation_utils_base_3<> { From 5658c9f0aa305bfe417d1a5080d35e709ccf65ee Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:35:56 +0200 Subject: [PATCH 033/151] fix Output_rep of tuple-like --- Stream_support/include/CGAL/IO/io.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Stream_support/include/CGAL/IO/io.h b/Stream_support/include/CGAL/IO/io.h index c19181a122fc..ac490e047221 100644 --- a/Stream_support/include/CGAL/IO/io.h +++ b/Stream_support/include/CGAL/IO/io.h @@ -417,7 +417,7 @@ class Output_rep::type>> std::ostream& operator()( std::ostream& os) const { const auto mode = IO::get_mode(os); - const bool is_pretty = std::is_same_v || mode == IO::PRETTY; + const bool is_pretty = (mode != IO::BINARY && std::is_same_v) || mode == IO::PRETTY; const char* sep = (mode == IO::BINARY) ? "" : (is_pretty ? ", " : " "); if(is_pretty) os << "("; print_tuple_impl(os, sep, std::make_index_sequence::value>{}); From 6a2f0b5784e360b1f312c55c79310ba092543d09 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:37:05 +0200 Subject: [PATCH 034/151] factor pieces of code --- .../include/CGAL/Triangulation_3.h | 75 ++++++++++++------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/Triangulation_3/include/CGAL/Triangulation_3.h b/Triangulation_3/include/CGAL/Triangulation_3.h index 58aea9d0c9dd..3aa2f968cbc8 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3.h +++ b/Triangulation_3/include/CGAL/Triangulation_3.h @@ -1660,16 +1660,42 @@ class Triangulation_3 } protected: - typedef Facet Edge_2D; - typedef std::array Vertex_triple; - typedef typename Base::template Vertex_triple_Facet_map_generator< - Vertex_triple, Facet>::type Vertex_triple_Facet_map; - typedef typename Base::template Vertex_handle_unique_hash_map_generator< - Vertex_handle>::type Vertex_handle_unique_hash_map; + using Edge_2D = Facet; + using Vertex_triple = std::array; + + using Vertex_triple_Facet_map = typename Base::template Vertex_triple_Facet_map_generator::type; + using Vertex_handle_unique_hash_map = typename Base::template Vertex_handle_unique_hash_map_generator::type; static Vertex_triple make_vertex_triple(const Facet& f); static void make_canonical_oriented_triple(Vertex_triple& t); + static Vertex_triple make_canonical_oriented_triple(Vertex_triple&& t) { + make_canonical_oriented_triple(t); + return t; + } + + static Vertex_triple make_canonical_oriented_triple(const Facet& f) { + Vertex_triple t = make_vertex_triple(f); + make_canonical_oriented_triple(t); + return t; + } + + static Vertex_triple make_canonical_oriented_triple(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2) { + Vertex_triple t{v0, v1, v2}; + make_canonical_oriented_triple(t); + return t; + } + + template + static Vertex_triple apply_map(Map& m, const Vertex_triple& t) { + return Vertex_triple{m[t[0]], m[t[1]], m[t[2]]}; + } + + template + static Vertex_triple make_canonical_oriented_mapped_triple(Map& m, const Facet& f) { + return make_canonical_oriented_triple(apply_map(m, make_vertex_triple(f))); + } + template < class VertexRemover > VertexRemover& make_hole_2D(Vertex_handle v, std::list& hole, VertexRemover& remover); @@ -4959,19 +4985,17 @@ create_hole_outer_map(Vertex_handle v, const std::vector& incident_ CGAL_expensive_precondition(! test_dim_down(v)); Vertex_triple_Facet_map outer_map; - for(auto cit = incident_cells.begin(), end = incident_cells.end(); - cit != end; ++cit) - { - int indv = (*cit)->index(v); - Cell_handle opp_cit = (*cit)->neighbor(indv); - Facet f(opp_cit, opp_cit->index(*cit)); - Vertex_triple vt = make_vertex_triple(f); - make_canonical_oriented_triple(vt); + for(auto ch: incident_cells) + { + int indv = ch->index(v); + Cell_handle opp_cit = ch->neighbor(indv); + Facet f(opp_cit, opp_cit->index(ch)); + Vertex_triple vt = make_canonical_oriented_triple(f); outer_map[vt] = f; for(int i=0; i<4; i++) { if(i != indv) - (*cit)->vertex(i)->set_cell(opp_cit); + ch->vertex(i)->set_cell(opp_cit); } } return outer_map; @@ -4988,9 +5012,10 @@ create_triangulation_inner_map(const Triangulation& t, auto create_triangulation_inner_map_aux = [&](const auto& cells_range) { for(auto ch : cells_range) { for(unsigned int index = 0; index < 4; index++) { - Facet f = std::pair(ch, index); - Vertex_triple vt_aux = make_vertex_triple(f); - Vertex_triple vt{vmap[vt_aux[0]], vmap[vt_aux[2]], vmap[vt_aux[1]]}; + const Facet f{ch, index}; + Vertex_triple vt = apply_map(vmap, make_vertex_triple(f)); + using std::swap; + swap(vt[1], vt[2]); // reverse the orientation of the triplet make_canonical_oriented_triple(vt); inner_map[vt] = f; } @@ -5136,8 +5161,7 @@ copy_triangulation_into_hole(const Vertex_handle_unique_hash_map& vmap, if(index != i_i) { Facet f{new_ch, index}; - Vertex_triple vt = make_vertex_triple(f); - make_canonical_oriented_triple(vt); + Vertex_triple vt = make_canonical_oriented_triple(f); std::swap(vt[1], vt[2]); typename Vertex_triple_Facet_map::iterator oit2 = outer_map.find(vt); @@ -6112,13 +6136,7 @@ _make_big_hole_3D(Vertex_handle v, vstates[v0] = PROCESSED; } - int i1 = vertex_triple_index(k, 0); - int i2 = vertex_triple_index(k, 1); - int i3 = vertex_triple_index(k, 2); - - Vertex_handle v1 = c->vertex(i1); - Vertex_handle v2 = c->vertex(i2); - Vertex_handle v3 = c->vertex(i3); + auto [v1, v2, v3] = this->vertices(Facet{c, k}); Cell_handle opp_cit = c->neighbor(k); int opp_i = tds().mirror_index(c, k); @@ -6161,8 +6179,7 @@ _make_big_hole_3D(Vertex_handle v, } Facet f(opp_cit, opp_i); - Vertex_triple vt = make_vertex_triple(f); - make_canonical_oriented_triple(vt); + Vertex_triple vt = make_canonical_oriented_triple(f); outer_map[vt] = f; v1->set_cell(opp_cit); v2->set_cell(opp_cit); From f86a0b993a31d05d82fb61a3f7fe7b4f000b8ac7 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:37:44 +0200 Subject: [PATCH 035/151] avoid a class of names using an enum class --- .../Conforming_Delaunay_triangulation_3.h | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 622b2deac240..e6bd6470339e 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -182,7 +182,7 @@ namespace internal { inline auto& tasks_manager() { struct Tasks_manager { - enum { + enum class Tm { READ_INPUT = 0, MERGE_FACETS, INSERT_VERTICES, @@ -197,7 +197,7 @@ inline auto& tasks_manager() { #if CGAL_USE_ITT __itt_domain* cdt_3_domain = __itt_domain_create("org.cgal.CDT_3"); - const std::array<__itt_string_handle*, NB_TASKS> task_handles = { + const std::array<__itt_string_handle*, static_cast(Tm::NB_TASKS)> task_handles = { __itt_string_handle_create("CDT_3: read input file"), __itt_string_handle_create("CDT_3: merge facets"), __itt_string_handle_create("CDT_3: insert vertices"), @@ -209,7 +209,7 @@ inline auto& tasks_manager() { __itt_string_handle_create("CDT_3: validation") }; #endif - std::array timers{}; + std::array(Tm::NB_TASKS)> timers{}; struct Scope_guard { Tasks_manager *instance = nullptr; int task_id; @@ -233,19 +233,19 @@ inline auto& tasks_manager() { } }; - Scope_guard make_task_scope_guard(int task_id) { - return Scope_guard(this, task_id); + Scope_guard make_task_scope_guard(Tm task_id) { + return Scope_guard(this, static_cast(task_id)); } - Scope_guard READ_INPUT_TASK_guard() { return make_task_scope_guard(READ_INPUT); } - Scope_guard MERGE_FACETS_TASK_guard() { return make_task_scope_guard(MERGE_FACETS); } - Scope_guard INSERT_VERTICES_TASK_guard() { return make_task_scope_guard(INSERT_VERTICES); } - Scope_guard COMPUTE_DISTANCES_TASK_guard() { return make_task_scope_guard(COMPUTE_DISTANCES); } - Scope_guard CONFORMING_TASK_guard() { return make_task_scope_guard(CONFORMING); } - Scope_guard CDT_TASK_guard() { return make_task_scope_guard(CDT); } - Scope_guard MOVE_STEINER_VERTICES_TASK_guard() { return make_task_scope_guard(MOVE_STEINER_VERTICES); } - Scope_guard OUTPUT_TASK_guard() { return make_task_scope_guard(OUTPUT); } - Scope_guard VALIDATION_TASK_guard() { return make_task_scope_guard(VALIDATION); } + Scope_guard READ_INPUT_TASK_guard() { return make_task_scope_guard(Tm::READ_INPUT); } + Scope_guard MERGE_FACETS_TASK_guard() { return make_task_scope_guard(Tm::MERGE_FACETS); } + Scope_guard INSERT_VERTICES_TASK_guard() { return make_task_scope_guard(Tm::INSERT_VERTICES); } + Scope_guard COMPUTE_DISTANCES_TASK_guard() { return make_task_scope_guard(Tm::COMPUTE_DISTANCES); } + Scope_guard CONFORMING_TASK_guard() { return make_task_scope_guard(Tm::CONFORMING); } + Scope_guard CDT_TASK_guard() { return make_task_scope_guard(Tm::CDT); } + Scope_guard MOVE_STEINER_VERTICES_TASK_guard() { return make_task_scope_guard(Tm::MOVE_STEINER_VERTICES); } + Scope_guard OUTPUT_TASK_guard() { return make_task_scope_guard(Tm::OUTPUT); } + Scope_guard VALIDATION_TASK_guard() { return make_task_scope_guard(Tm::VALIDATION); } }; // end struct Intel_OneAPI_ITT_API From e60441bf10846283ab891b29339883c846848a68 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:38:04 +0200 Subject: [PATCH 036/151] turn display_vert into a ostream manipulator --- .../include/CGAL/Conforming_Delaunay_triangulation_3.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index e6bd6470339e..907d4af8ba9a 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -334,10 +334,9 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { using Subconstraint = typename Constraint_hierarchy::Subconstraint; auto display_vert(Vertex_handle v) const{ - std::stringstream os; - os.precision(17); - os << IO::oformat(v, with_point); - return os.str(); + return IO::oformat([&](std::ostream& os) -> auto& { + return os << IO::oformat(v, with_point); + }, IO_manip_tag{}); } auto display_subcstr(Subconstraint subconstraint) const { From 4b9a74b727a46a93f4fc25193f328803885729a8 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:39:42 +0200 Subject: [PATCH 037/151] new type type for CDT: Steiner point in the volume --- ...ing_constrained_Delaunay_triangulation_3.h | 24 +++++++++++++++---- ...ained_Delaunay_triangulation_cell_data_3.h | 2 +- ...ned_Delaunay_triangulation_vertex_data_3.h | 11 ++++++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index a7890789e773..18e6a287dc28 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -594,6 +594,7 @@ struct Output_rep, With_point_and_info_t out << (v_ptr->ccdt_3_data().is_Steiner_vertex_on_edge() ? "(Steiner)" : "") << (v_ptr->ccdt_3_data().is_Steiner_vertex_in_face() ? "(Steiner in face)" : "") + << (v_ptr->ccdt_3_data().is_Steiner_vertex_in_volume() ? "(Steiner in volume)" : "") << "= " << v_ptr->point(); if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_BORDER)) out << " (region border)"; if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_INSIDE)) out << " (inside region)"; @@ -1242,6 +1243,7 @@ class Conforming_constrained_Delaunay_triangulation_3 out << prefix << " - " << nb(CDT_3_vertex_type::INPUT_VERTEX) << " input vertices\n"; out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_ON_EDGE) << " Steiner vertices on edges\n"; out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_IN_FACE) << " Steiner vertices in faces\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_IN_VOLUME) << " Steiner vertices in volume\n"; out << prefix << " - " << nb(CDT_3_vertex_type::FREE) << " free vertices\n"; return out; }, IO_manip_tag{}); @@ -2530,15 +2532,29 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return v->ccdt_3_data().vertex_type(); } - bool is_Steiner_vertex(Vertex_handle v) const { - auto type = vertex_type(v); - return type == CDT_3_vertex_type::STEINER_IN_FACE || type == CDT_3_vertex_type::STEINER_ON_EDGE; + bool is_Steiner_vertex_on_surface(Vertex_handle v) const { + switch(vertex_type(v)) { + case CDT_3_vertex_type::INPUT_VERTEX: + case CDT_3_vertex_type::FREE: + case CDT_3_vertex_type::STEINER_IN_VOLUME: + return false; + case CDT_3_vertex_type::STEINER_IN_FACE: + case CDT_3_vertex_type::STEINER_ON_EDGE: + return true; + default: + CGAL_unreachable(); + } } auto move_Steiner_vertices_to_the_volume() { + std::vector steiner_vertices; for(auto v : this->finite_vertex_handles()) { - if(is_Steiner_vertex(v)) move_one_Steiner_vertex_to_the_volume(v); + if(is_Steiner_vertex_on_surface(v)) steiner_vertices.push_back(v); + } + for(auto v : steiner_vertices) { + move_one_Steiner_vertex_to_the_volume(v); } + CGAL_assertion(tr().tds().is_valid(true)); } private: diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h index 91d9b73044ae..e787a54be970 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h @@ -44,7 +44,7 @@ class Conforming_constrained_Delaunay_triangulation_cell_data_3 { std::array face_id = { -1, -1, -1, -1 }; std::array facet_2d = {nullptr, nullptr, nullptr, nullptr}; - std::bitset(CDT_3_cell_marker::nb_of_markers)> markers; + std::bitset(CDT_3_cell_marker::nb_of_markers)> markers{}; bool is_marked() const { return markers.any(); } bool is_marked(CDT_3_cell_marker m) const { return markers.test(static_cast(m)); } diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h index 1918ae29540d..8e0a99803d83 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h @@ -35,7 +35,15 @@ namespace CGAL { struct Conforming_constrained_Delaunay_triangulation_vertex_data_3 {}; #else // DOXYGEN_RUNNING -enum class CDT_3_vertex_type { FREE, CORNER, INPUT_VERTEX = CORNER, STEINER_ON_EDGE, STEINER_IN_FACE, nb_of_types }; +enum class CDT_3_vertex_type : unsigned char { + FREE, + CORNER, + INPUT_VERTEX = CORNER, + STEINER_ON_EDGE, + STEINER_IN_FACE, + STEINER_IN_VOLUME, + nb_of_types +}; enum class CDT_3_vertex_marker { CLEAR = 0, @@ -143,6 +151,7 @@ class Conforming_constrained_Delaunay_triangulation_vertex_data_3 { void set_vertex_type(CDT_3_vertex_type type) { m_vertex_type = type; } bool is_Steiner_vertex_on_edge() const { return m_vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; } bool is_Steiner_vertex_in_face() const { return m_vertex_type == CDT_3_vertex_type::STEINER_IN_FACE; } + bool is_Steiner_vertex_in_volume() const { return m_vertex_type == CDT_3_vertex_type::STEINER_IN_VOLUME; } }; #endif // DOXYGEN_RUNNING From 34f5af92fb51b42cfaf25566f392bd4a5fbdab71 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:44:34 +0200 Subject: [PATCH 038/151] new constant INVALID_FACE_INDEX instead of -1 --- .../Conforming_constrained_Delaunay_triangulation_3.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 18e6a287dc28..406e24701875 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1277,6 +1277,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Cell_marker = CDT_3_cell_marker; using Face_index = CDT_3_signed_index; + static inline constexpr Face_index INVALID_FACE_INDEX = -1; using Conforming_Dt::Conforming_Dt; @@ -1570,7 +1571,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(facet_c_to_next_is_constrained) { out.facets.emplace_back(c, i, Star_components_facets::Type_of_facet::INCIDENT_TO_V); const auto constrained_facet_face_id = face_constraint_index(c, i); - if(-1 == current_component_incident_constraint_face_ids[1] && + if(INVALID_FACE_INDEX == current_component_incident_constraint_face_ids[1] && constrained_facet_face_id != current_component_incident_constraint_face_ids[0]) { current_component_incident_constraint_face_ids[1] = constrained_facet_face_id; @@ -1639,7 +1640,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(std::all_of(out.component_incident_constraint_face_ids.begin(), out.component_incident_constraint_face_ids.end(), [](const std::array& face_ids) { - return face_ids[1] != -1; + return face_ids[1] != INVALID_FACE_INDEX; })); } @@ -2197,6 +2198,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } + auto set_facet_as_not_constrained(Facet f) { + return set_facet_constrained(f, INVALID_FACE_INDEX, CDT_2_face_handle{}); + } + template ) Polygon> std::optional register_new_constrained_polygon(Polygon&& polygon) { return insert_constrained_polygon(std::forward(polygon), false); From c4d2e10bda83faf8d5ea224e1b1d6b720a82df3f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:48:39 +0200 Subject: [PATCH 039/151] info().is_outside_the_face is now an enum instead of an int --- ...ing_constrained_Delaunay_triangulation_3.h | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 406e24701875..f2b867aa7eae 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1323,9 +1323,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; using Color_value_type = std::int8_t; + enum class Face_is_outside_the_face : Color_value_type { UNKNOWN = -1, INSIDE = 0, OUTSIDE = 1 }; struct Face_info { - Color_value_type is_outside_the_face = -1; + Face_is_outside_the_face is_outside_the_face = Face_is_outside_the_face::UNKNOWN; Color_value_type is_in_region = 0; std::bitset<3> is_edge_also_in_3d_triangulation = 0; bool missing_subface = true; @@ -1359,6 +1360,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using CDT_2_traits = typename CDT_2_types::Projection_traits; using CDT_2_face_handle = typename CDT_2::Face_handle; using CDT_2_edge = typename CDT_2::Edge; + using CDT_2_vertex_handle = typename CDT_2::Vertex_handle; + using Face_is_outside_the_face = typename CDT_2_types::Face_is_outside_the_face; + static inline constexpr Face_is_outside_the_face OUTSIDE = Face_is_outside_the_face::OUTSIDE; + static inline constexpr Face_is_outside_the_face INSIDE = Face_is_outside_the_face::INSIDE; static_assert(CGAL::cdt_3_msvc_2019_or_older() || CGAL::is_nothrow_movable_v); protected: @@ -2722,25 +2727,28 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(auto fh: cdt_2.all_face_handles()) { - fh->info().is_outside_the_face = -1; + fh->info().is_outside_the_face = Face_is_outside_the_face::UNKNOWN; } struct Face_handle_and_outside { CDT_2_face_handle fh; - bool outside; + Face_is_outside_the_face outside; }; const auto d = cdt_2.dimension(); std::stack stack; - stack.push({cdt_2.infinite_face(), true}); + stack.push({cdt_2.infinite_face(), OUTSIDE}); while(!stack.empty()) { const auto [fh, outside] = stack.top(); stack.pop(); - if(fh->info().is_outside_the_face == -1) { + if(fh->info().is_outside_the_face == Face_is_outside_the_face::UNKNOWN) { fh->info().is_outside_the_face = outside; - CGAL_assertion(!cdt_2.is_infinite(fh) || outside == true); + CGAL_assertion(!cdt_2.is_infinite(fh) || outside == OUTSIDE); for(int i = 0; i <= d; ++i) { const auto neighbor = fh->neighbor(i); - const auto new_outside = is_constrained_with_odd_nb_of_constraints(cdt_2, fh, i) ? !outside : outside; - if(neighbor->info().is_outside_the_face == -1) { + const auto new_outside = + is_constrained_with_odd_nb_of_constraints(cdt_2, fh, i) + ? (outside == OUTSIDE ? INSIDE : OUTSIDE) + : outside; + if(neighbor->info().is_outside_the_face == Face_is_outside_the_face::UNKNOWN) { stack.push({neighbor, new_outside}); } } @@ -2750,7 +2758,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().input_faces()) { int counter = 0; for(const auto fh: cdt_2.finite_face_handles()) { - if(!fh->info().is_outside_the_face) ++counter; + if(fh->info().is_outside_the_face == INSIDE) ++counter; } std::cerr << counter << " triangles(s) in the face\n"; } @@ -2834,7 +2842,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto i = unsigned(edge.second); return false == face->info().is_edge_also_in_3d_triangulation.test(i); }, - +[](CDT_2_face_handle face_handle) { return false == face_handle->info().is_outside_the_face; }); + +[](CDT_2_face_handle face_handle) { + return INSIDE == face_handle->info().is_outside_the_face; + }); boost::breadth_first_search(dual, fh, boost::color_map(typename CDT_2_types::Color_map_is_in_region()) .visitor(boost::make_bfs_visitor(boost::write_property( @@ -4334,13 +4344,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D [[maybe_unused]] typename CDT_2::Locate_type lt_2; int i; auto fh = cdt_2.locate(steiner_pt, lt_2, i, fh_2d); - CGAL_assertion(!fh->info().is_outside_the_face); CGAL_USE(fh); + CGAL_assertion(fh->info().is_outside_the_face == INSIDE); CGAL_USE(fh); const auto v_2d = non_const_cdt_2.insert(steiner_pt, fh_2d); v_2d->info().vertex_handle_3d = v; auto f_circ = cdt_2.incident_faces(v_2d); const auto end = f_circ; do { - f_circ->info().is_outside_the_face = false; + f_circ->info().is_outside_the_face = INSIDE; } while(++f_circ != end); search_for_missing_subfaces(face_index); return std::nullopt; @@ -4422,7 +4432,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::set processed_faces; auto& region_index = face_region_number(face_index); for(const CDT_2_face_handle fh : cdt_2.finite_face_handles()) { - if(fh->info().is_outside_the_face) continue; + if(fh->info().is_outside_the_face == OUTSIDE) continue; if(false == fh->info().missing_subface) { continue; } @@ -4794,13 +4804,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto [hole_side_1, hole_side_1_faces_to_delete, side_1_last_incident_edge] = half_hole(vh_2d->face()); const auto [side_1_last_face, side_1_last_edge_index] = side_1_last_incident_edge; - const bool side_1_hole_is_outside_the_face = side_1_last_face->info().is_outside_the_face; + const auto side_1_hole_is_outside_the_face = side_1_last_face->info().is_outside_the_face; const auto side_2_first_face = side_1_last_face->neighbor(side_1_last_edge_index); auto [hole_side_2, hole_side_2_faces_to_delete, side_2_last_incident_edge] = half_hole(side_2_first_face); const auto [side_2_last_face, side_2_last_edge_index] = side_2_last_incident_edge; - const bool side_2_hole_is_outside_the_face = side_2_last_face->info().is_outside_the_face; + const auto side_2_hole_is_outside_the_face = side_2_last_face->info().is_outside_the_face; CGAL_assertion(side_1_last_incident_edge.first != side_2_last_incident_edge.first); @@ -4889,7 +4899,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D static void write_region_to_OFF(std::ostream& out, const CDT_2& cdt_2) { out.precision(17); auto color_fn = [](CDT_2_face_handle fh_2d) -> CGAL::IO::Color { - if(fh_2d->info().is_outside_the_face) return CGAL::IO::gray(); + if(fh_2d->info().is_outside_the_face == OUTSIDE) return CGAL::IO::gray(); if(fh_2d->info().is_in_region) { if(fh_2d->info().is_in_region == 1) return CGAL::IO::violet(); else return CGAL::IO::red(); From a52421895542d790872b8f287c496776f0a7827d Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:56:27 +0200 Subject: [PATCH 040/151] refactor with convenience functions --- ...ing_constrained_Delaunay_triangulation_3.h | 187 ++++++++---------- 1 file changed, 87 insertions(+), 100 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index f2b867aa7eae..d56ca3557fa3 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1352,7 +1352,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D friend reference get(CDT_2_dual_color_map, key_type fh) { return fh->info().*member_ptr; } friend void put(CDT_2_dual_color_map, key_type fh, value_type value) { fh->info().*member_ptr = value; } }; - using Color_map_is_outside_the_face = CDT_2_dual_color_map<&Face_info::is_outside_the_face>; using Color_map_is_in_region = CDT_2_dual_color_map<&Face_info::is_in_region>; }; // CDT_2_types using CDT_2 = typename CDT_2_types::CDT; @@ -1366,6 +1365,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D static inline constexpr Face_is_outside_the_face INSIDE = Face_is_outside_the_face::INSIDE; static_assert(CGAL::cdt_3_msvc_2019_or_older() || CGAL::is_nothrow_movable_v); + static Vertex_handle& vertex_3d(CDT_2_vertex_handle v) { + return v->info().vertex_handle_3d; + } + protected: struct Null_normal_error : Error_exception { Null_normal_error(std::string msg, std::string file, int line) @@ -1380,8 +1383,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Constrained_polyline_id = typename Constraint_hierarchy::Constraint_id; protected: + static auto display_vert(Vertex_handle v) { + return IO::oformat([v](std::ostream& os) -> auto& { + return os << IO::oformat(v, with_point_and_info); + }, IO_manip_tag{}); + } -protected: template std::optional find_in_incident_facets(Vertex_handle v, Pred pred) const @@ -1429,8 +1436,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(tr().is_infinite(Facet(cell, facet_index))) { continue; } - for(int i = 0; i < 3; ++i) { - points.push_back(cell->vertex(Tds::vertex_triple_index(facet_index, i))->point()); + for(auto v: this->vertices(Facet{cell, facet_index})) { + points.push_back(tr().point(v)); } // end for each vertex of the facet // swap the orientation: our facets are oriented toward the center of the star component // but we want them to be oriented outward @@ -1818,7 +1825,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D fh_2d->set_constraint(edge_index, false); mirror_fh_2d->set_constraint(mirror_edge_index, false); const auto v_Steiner_2d = non_const_cdt_2.insert(point, fh_2d); - v_Steiner_2d->info().vertex_handle_3d = v_Steiner; + vertex_3d(v_Steiner_2d) = v_Steiner; non_const_cdt_2.insert_constraint(va_2d, v_Steiner_2d); non_const_cdt_2.insert_constraint(vb_2d, v_Steiner_2d); @@ -1928,8 +1935,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // vertices of the border of the cavity should point to cells outside of it for(auto [c, index]: exterior_border_facets_of_original_cavity) { - for(int i = 0; i < 3; ++i) { - auto v = c->vertex(tr().vertex_triple_index(index, i)); + for(auto v : tr().vertices(Facet{c, index})) { v->set_cell(c); } } @@ -1947,8 +1953,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D typename T_3::Vertex_triple_Facet_map outer_map; for(auto f: facets_of_cavity) { - typename T_3::Vertex_triple vt = this->make_vertex_triple(f); - this->make_canonical_oriented_triple(vt); + auto vt = T_3::make_canonical_oriented_triple(f); outer_map[vt] = f; } @@ -2399,8 +2404,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D static void print_minimum_distance_info(const Min_distance_result& min_dist) { std::cout << "Min distance between vertices: " << min_dist.min_distance << '\n' << " between vertices: : " - << CGAL::IO::oformat(min_dist.vertices_of_min_edge[0], CGAL::With_point_tag{}) << " " - << CGAL::IO::oformat(min_dist.vertices_of_min_edge[1], CGAL::With_point_tag{}) << "\n\n"; + << display_vert(min_dist.vertices_of_min_edge[0]) << " " + << display_vert(min_dist.vertices_of_min_edge[1]) << "\n\n"; } struct Constraint_distance_result { @@ -2640,9 +2645,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto insert_constraint_in_cdt_2 = [&](const auto& va, const auto& vb) { if(this->debug().input_faces()) { std::cerr << "cdt_2.insert_constraint (" - << IO::oformat(va->info().vertex_handle_3d, with_point_and_info) - << " , " - << IO::oformat(vb->info().vertex_handle_3d, with_point_and_info) + << display_vert(vertex_3d(va)) << " , " << display_vert(vertex_3d(vb)) << ")\n"; } auto cstr_id = cdt_2.insert_constraint(va, vb); @@ -2650,7 +2653,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; auto insert_vertex_in_cdt_2 = [&](const auto& v) { auto vh_2d = cdt_2.insert(tr().point(v)); - vh_2d->info().vertex_handle_3d = v; + vertex_3d(vh_2d) = v; return vh_2d; }; @@ -2662,7 +2665,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D it != end; /* incremented in the loop */) { const auto va = *it; - CGAL_assertion(previous_2d->info().vertex_handle_3d == va); + CGAL_assertion(vertex_3d(previous_2d) == va); ++it; const auto vb = *it; const auto c_id = this->constraint_from_extremities(va, vb); @@ -2785,8 +2788,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto [ch2, index2] = cdt_2.mirror_edge({ch, index}); const auto vd = ch2->vertex(index2); CGAL_assertion(!cdt_2.is_edge(vb, vd)); - const auto vb_3d = vb->info().vertex_handle_3d; - const auto vd_3d = vd->info().vertex_handle_3d; + const auto vb_3d = vertex_3d(vb); + const auto vd_3d = vertex_3d(vd); if(this->is_edge(vb_3d, vd_3d)) { // let's insert the diagonal [bd] in the CDT_2 cdt_2.insert_constraint(vb, vd); @@ -2807,7 +2810,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D this->face_constraint_misses_subfaces_reset(static_cast(polygon_constraint_id)); for(const auto fh: cdt_2.all_face_handles()) { - if(fh->info().is_outside_the_face) { + if(fh->info().is_outside_the_face == OUTSIDE) { fh->info().missing_subface = false; continue; } @@ -2820,6 +2823,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D fh->info().missing_subface = true; this->face_constraint_misses_subfaces_set(static_cast(polygon_constraint_id)); #if CGAL_CDT_3_DEBUG_MISSING_TRIANGLES + const auto v0 = vertex_3d(fh->vertex(0)); + const auto v1 = vertex_3d(fh->vertex(1)); + const auto v2 = vertex_3d(fh->vertex(2)); std::cerr << cdt_3_format("Missing triangle in polygon #{}:\n", polygon_constraint_id); write_triangle(std::cerr, v0, v1, v2); #endif // CGAL_CDT_3_DEBUG_MISSING_TRIANGLES @@ -2866,8 +2872,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(int index = 0; index < 3; ++index) { if(fh_region_set.count(fh->neighbor(index)) > 0) continue; // otherwise we have a border edge: fh->neighbor(i) is not in the region - const auto va = fh->vertex(CDT_2::ccw(index))->info().vertex_handle_3d; - const auto vb = fh->vertex(CDT_2:: cw(index))->info().vertex_handle_3d; + const auto va = vertex_3d(fh->vertex(CDT_2::ccw(index))); + const auto vb = vertex_3d(fh->vertex(CDT_2:: cw(index))); Cell_handle c; int i, j; CGAL_assume_code(bool b =) @@ -2905,9 +2911,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto pc = this->point(vc); const auto pd = this->point(vd); for(const auto fh_2d : fh_region) { - const auto v0 = fh_2d->vertex(0)->info().vertex_handle_3d; - const auto v1 = fh_2d->vertex(1)->info().vertex_handle_3d; - const auto v2 = fh_2d->vertex(2)->info().vertex_handle_3d; + const auto v0 = vertex_3d(fh_2d->vertex(0)); + const auto v1 = vertex_3d(fh_2d->vertex(1)); + const auto v2 = vertex_3d(fh_2d->vertex(2)); if(vc == v0 || vc == v1 || vc == v2 || vd == v0 || vd == v1 || vd == v2) { continue; } @@ -3014,14 +3020,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().regions()) { std::cerr << "Edge interior intersects region in face with vertices:\n " - << IO::oformat(fh->vertex(0)->info().vertex_handle_3d, with_point_and_info) << "\n " - << IO::oformat(fh->vertex(1)->info().vertex_handle_3d, with_point_and_info) << "\n " - << IO::oformat(fh->vertex(2)->info().vertex_handle_3d, with_point_and_info) << "\n"; + << display_vert(vertex_3d(fh->vertex(0))) << "\n " + << display_vert(vertex_3d(fh->vertex(1))) << "\n " + << display_vert(vertex_3d(fh->vertex(2))) << "\n"; } - return {1, - {fh->vertex(0)->info().vertex_handle_3d, fh->vertex(1)->info().vertex_handle_3d, - fh->vertex(2)->info().vertex_handle_3d}}; + return {1, {vertex_3d(fh->vertex(0)), vertex_3d(fh->vertex(1)), vertex_3d(fh->vertex(2))}}; } while(false == line_face_circulator->has_vertex(vq_2d) && ++line_face_circulator != start); } // End of if(region_vertices.count(vc) > 0 && region_vertices.count(vd) > 0 && // border_edges_set.count(CGAL::make_sorted_pair(vc, vd)) == 0) @@ -3310,7 +3314,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }); CGAL_assume(vertex_above != Vertex_handle{}); if(this->debug().regions()) { - std::cerr << "The vertex above the region is " << IO::oformat(vertex_above, with_point_and_info) << "\n"; + std::cerr << "The vertex above the region is " << display_vert(vertex_above) << "\n"; } CGAL::unordered_flat_set> remaining_facets_of_border(facets_of_border.begin(), @@ -3327,9 +3331,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto [c, i] = facet; if(this->debug().regions()) { std::cerr << " stack facet " << (above_below == 1 ? "above" : "below") << ": " - << IO::oformat(c->vertex(tr().vertex_triple_index(i, 0)), with_point_and_info) << " " - << IO::oformat(c->vertex(tr().vertex_triple_index(i, 1)), with_point_and_info) << " " - << IO::oformat(c->vertex(tr().vertex_triple_index(i, 2)), with_point_and_info) << "\n"; + << display_vert(c->vertex(tr().vertex_triple_index(i, 0))) << " " + << display_vert(c->vertex(tr().vertex_triple_index(i, 1))) << " " + << display_vert(c->vertex(tr().vertex_triple_index(i, 2))) << "\n"; } if(above_below == 1) { @@ -3386,14 +3390,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(remaining_facets_of_border.erase(neighbor_facet) > 0) { if(this->debug().regions()) { std::cerr << " -> stack push new facet " << (next_facet_is_above_below == 1 ? "above" : "below") << ": " - << IO::oformat(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 0)), - with_point_and_info) + << display_vert(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 0))) << " " - << IO::oformat(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 1)), - with_point_and_info) + << display_vert(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 1))) << " " - << IO::oformat(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 2)), - with_point_and_info) + << display_vert(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 2))) << "\n"; } stack.push({neighbor_facet, next_facet_is_above_below}); @@ -3439,7 +3440,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(is_marked(v, Vertex_marker::CAVITY)) { clear_mark(v, Vertex_marker::CAVITY); if(this->debug().regions()) { - std::cerr << " " << IO::oformat(v, with_point_and_info) << "\n"; + std::cerr << " " << display_vert(v) << "\n"; } } } @@ -3493,7 +3494,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::set vertices; for(const auto fh_2d: fh_region) { for(int i = 0; i < 3; ++i) { - vertices.insert(fh_2d->vertex(i)->info().vertex_handle_3d); + vertices.insert(vertex_3d(fh_2d->vertex(i))); } } return vertices; @@ -3527,7 +3528,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::set diagonal; for(auto fh : fh_region) { for(int i = 0; i < 3; ++i) { - auto [it, new_vertex] = vertices.insert(fh->vertex(i)->info().vertex_handle_3d); + auto [it, new_vertex] = vertices.insert(vertex_3d(fh->vertex(i))); if(!new_vertex) { diagonal.insert(*it); } @@ -3542,10 +3543,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto diagonal_index = fh_region[0]->index(fh_region[1]); CGAL_assertion(diagonal_index >= 0 && diagonal_index < 3); - const auto v0 = fh_region[0]->vertex(diagonal_index)->info().vertex_handle_3d; - const auto v1 = fh_region[0]->vertex(cdt_2.ccw(diagonal_index))->info().vertex_handle_3d; - const auto v2 = fh_region[0]->vertex(cdt_2.cw(diagonal_index))->info().vertex_handle_3d; - const auto v3 = fh_region[1]->vertex(fh_region[1]->index(fh_region[0]))->info().vertex_handle_3d; + const auto v0 = vertex_3d(fh_region[0]->vertex(diagonal_index)); + const auto v1 = vertex_3d(fh_region[0]->vertex(cdt_2.ccw(diagonal_index))); + const auto v2 = vertex_3d(fh_region[0]->vertex(cdt_2.cw(diagonal_index))); + const auto v3 = vertex_3d(fh_region[1]->vertex(fh_region[1]->index(fh_region[0]))) ; if(tr().is_facet(v0, v1, v3) && tr().is_facet(v0, v3, v2)) { if(cdt_2.orientation(v0->point(), v1->point(), v3->point()) == CGAL::POSITIVE && @@ -3719,13 +3720,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << " "; auto vertices = tr().vertices(f); for(int i = 0; i < 3; ++i) { - std::cerr << IO::oformat(vertices[i], with_point_and_info) << " "; + std::cerr << display_vert(vertices[i]) << " "; } std::cerr << "\n"; } std::cerr << " Vertices:\n"; for(const auto& v : std::set(original_vertices_of_lower_cavity.begin(), original_vertices_of_lower_cavity.end())) { - std::cerr << " " << IO::oformat(v, with_point_and_info) << "\n"; + std::cerr << " " << display_vert(v) << "\n"; } std::cerr << "Upper cavity:\n"; std::cerr << " Facets:\n"; @@ -3733,13 +3734,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << " "; auto vertices = tr().vertices(f); for(int i = 0; i < 3; ++i) { - std::cerr << IO::oformat(vertices[i], with_point_and_info) << " "; + std::cerr << display_vert(vertices[i]) << " "; } std::cerr << "\n"; } std::cerr << " Vertices:\n"; for(const auto& v : std::set(original_vertices_of_upper_cavity.begin(), original_vertices_of_upper_cavity.end())) { - std::cerr << " " << IO::oformat(v, with_point_and_info) << "\n"; + std::cerr << " " << display_vert(v) << "\n"; } } auto register_internal_constrained_facet = [this](Facet f) { this->register_facet_to_be_constrained(f); }; @@ -3776,9 +3777,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // the following transform_reduce is like `std::any_of` but without the fast-exit if(std::transform_reduce(fh_region.begin(), fh_region.end(), false, std::logical_or{}, [&](auto fh) { - const auto v0 = fh->vertex(0)->info().vertex_handle_3d; - const auto v1 = fh->vertex(1)->info().vertex_handle_3d; - const auto v2 = fh->vertex(2)->info().vertex_handle_3d; + const auto v0 = vertex_3d(fh->vertex(0)); + const auto v1 = vertex_3d(fh->vertex(1)); + const auto v2 = vertex_3d(fh->vertex(2)); auto is_fh_facet_of = [&](const auto& tr) -> std::optional { return this->vertex_triple_is_facet_of_other_triangulation(*this, v0, v1, v2, tr); }; @@ -3889,8 +3890,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; auto fill_outer_map_of_cavity = [&](const auto&, const auto& facets) { for(auto f : facets) { - typename T_3::Vertex_triple vt = this->make_vertex_triple(f); - this->make_canonical_oriented_triple(vt); + auto vt = T_3::make_canonical_oriented_triple(f); add_to_outer_map(vt, f); } }; @@ -3911,11 +3911,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto [fh_2d, reverse_orientation] = *is_facet; if(this->debug().regions()) facets_of_polygon.push_back(f); const auto vt_aux = this->make_vertex_triple(f); - typename T_3::Vertex_triple vt{map_cavity_vertices_to_ambient_vertices[vt_aux[0]], - map_cavity_vertices_to_ambient_vertices[vt_aux[1]], - map_cavity_vertices_to_ambient_vertices[vt_aux[2]]}; - this->make_canonical_oriented_triple(vt); - if(reverse_orientation == is_upper_cavity) { + auto vt = T_3::make_canonical_oriented_mapped_triple(map_cavity_vertices_to_ambient_vertices, f); std::swap(vt[1], vt[2]); } auto new_cell = this->tds().create_cell(); @@ -4050,11 +4046,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D static auto facet_is_facet_of_cdt_2(const Tr& tr, typename Tr::Facet f, const CDT_2& cdt_2) -> std::optional { - const auto [c, facet_index] = f; - const auto v0 = c->vertex(Tr::vertex_triple_index(facet_index, 0)); - const auto v1 = c->vertex(Tr::vertex_triple_index(facet_index, 1)); - const auto v2 = c->vertex(Tr::vertex_triple_index(facet_index, 2)); - + const auto [v0, v1, v2] = tr.vertices(f); auto v = vertex_of_cdt_2_functor(cdt_2); const auto cdt_2_v0 = v(tr.point(v0)); @@ -4158,9 +4150,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D map_cavity_vertices_to_ambient_vertices[cavity_v] = v; if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format("inserted {}cavity vertex {:.6} -> {:.6}\n", - extra, - IO::oformat(cavity_v, with_point_and_info), - IO::oformat(v, with_point_and_info)); + extra, + display_vert(cavity_v), + display_vert(v)); } return cavity_v; }; @@ -4241,8 +4233,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // IO::oformat(a), IO::oformat(b), IO::oformat(CGAL::angle(a, steiner_pt, b))) // << '\n'; if(CGAL::angle(a, steiner_pt, b) != CGAL::ACUTE) { - const auto va_3d = va->info().vertex_handle_3d; - const auto vb_3d = vb->info().vertex_handle_3d; + const auto va_3d = vertex_3d(va); + const auto vb_3d = vertex_3d(vb); return std::make_pair(va_3d, vb_3d); } } @@ -4346,7 +4338,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto fh = cdt_2.locate(steiner_pt, lt_2, i, fh_2d); CGAL_assertion(fh->info().is_outside_the_face == INSIDE); CGAL_USE(fh); const auto v_2d = non_const_cdt_2.insert(steiner_pt, fh_2d); - v_2d->info().vertex_handle_3d = v; + vertex_3d(v_2d) = v; auto f_circ = cdt_2.incident_faces(v_2d); const auto end = f_circ; do { @@ -4363,8 +4355,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto mid = construct_midpoint(a, b); if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().Steiner_points()) { std::cerr << cdt_3_format("Inserting Steiner (midpoint) point {} of constrained edge ({:.6} , {:.6})\n", - IO::oformat(mid), IO::oformat(va_3d, with_point_and_info), - IO::oformat(vb_3d, with_point_and_info)); + IO::oformat(mid), display_vert(va_3d), display_vert(vb_3d)); } auto&& contexts = this->constraint_hierarchy.contexts(va_3d, vb_3d); if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().verbose_special_cases()) { @@ -4377,8 +4368,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D vh_it != end; ++vh_it) { std::cerr << cdt_3_format(" {} {}\n", - (vh_it == current) ? '>' : '-', - IO::oformat(*vh_it, with_point_and_info)); + (vh_it == current) ? '>' : '-', + display_vert(*vh_it)); } } } @@ -4415,13 +4406,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(const auto& edge : cdt_2.finite_edges()) { const auto fh = edge.first; const auto i = edge.second; - const auto va_3d = fh->vertex(cdt_2.cw(i))->info().vertex_handle_3d; - const auto vb_3d = fh->vertex(cdt_2.ccw(i))->info().vertex_handle_3d; + const auto va_3d = vertex_3d(fh->vertex(cdt_2.cw(i))); + const auto vb_3d = vertex_3d(fh->vertex(cdt_2.ccw(i))); const bool is_3d = this->is_edge(va_3d, vb_3d); if constexpr(cdt_3_can_use_cxx20_format()) { if(this->debug().copy_triangulation_into_hole() || this->debug().conforming_validation()) { - std::cerr << cdt_3_format("Edge is 3D: {:6} ({} , {})\n", is_3d, IO::oformat(va_3d, with_point_and_info), - IO::oformat(vb_3d, with_point_and_info)); + std::cerr << cdt_3_format("Edge is 3D: {:6} ({} , {})\n", is_3d, display_vert(va_3d), + display_vert(vb_3d)); } } CGAL_assertion(is_3d || !cdt_2.is_constrained(edge)); @@ -4464,11 +4455,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(auto [c, index]: cdt_2.constrained_edges()) { const auto va = c->vertex(cdt_2.cw(index)); const auto vb = c->vertex(cdt_2.ccw(index)); - const auto va_3d = va->info().vertex_handle_3d; - const auto vb_3d = vb->info().vertex_handle_3d; - std::cerr << cdt_3_format(" ({:.6} , {:.6})\n", - IO::oformat(va_3d, with_point_and_info), - IO::oformat(vb_3d, with_point_and_info)); + const auto va_3d = vertex_3d(va); + const auto vb_3d = vertex_3d(vb); + std::cerr << cdt_3_format(" ({:.6} , {:.6})\n", display_vert(va_3d), display_vert(vb_3d)); } } const auto encroach_edge_opt = @@ -4529,7 +4518,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D << IO::oformat(v[1], with_point) << "\n " << IO::oformat(v[2], with_point) << "\n " << IO::oformat(v[3], with_point) << "\ncontains:\n " - << IO::oformat(n->vertex(n_index), with_point_and_info) << '\n'; + << display_vert(n->vertex(n_index)) << '\n'; using EK = CGAL::Exact_predicates_exact_constructions_kernel; const auto to_exact = CGAL::Cartesian_converter(); const auto from_exact = CGAL::Cartesian_converter(); @@ -4594,8 +4583,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D test = this->is_edge(v1, v2); result = result && test; if(!test && verbose) { - std::cerr << "edge (" << IO::oformat(v1, with_point_and_info) << ", " - << IO::oformat(v2, with_point_and_info) << ") is not an edge" << std::endl; + std::cerr << "edge (" << display_vert(v1) << ", " << display_vert(v2) << ") is not an edge" << std::endl; } } } @@ -4605,9 +4593,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D test = this->all_finite_edges[v1_index].find(v2)!= this->all_finite_edges[v1_index].end(); result = result && test; if(!test && verbose) { - std::cerr << "finite edge (" << IO::oformat(v1, with_point_and_info) << ", " - << IO::oformat(v2, with_point_and_info) << ") is not in the set all_finite_edges" - << std::endl; + std::cerr << "finite edge (" << display_vert(v1) << ", " << display_vert(v2) + << ") is not in the set all_finite_edges" << std::endl; } } } @@ -5151,9 +5138,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::ofstream intersect_out(ss_filename.str()); intersect_out.precision(17); for(auto fh: fh_region) { - auto v0 = fh->vertex(0)->info().vertex_handle_3d; - auto v1 = fh->vertex(1)->info().vertex_handle_3d; - auto v2 = fh->vertex(2)->info().vertex_handle_3d; + auto v0 = vertex_3d(fh->vertex(0)); + auto v1 = vertex_3d(fh->vertex(1)); + auto v2 = vertex_3d(fh->vertex(2)); auto triangle = typename Geom_traits::Triangle_3{tr().point(v0), tr().point(v1), tr().point(v2)}; auto exact_triangle = to_exact(triangle); if(auto edge_intersection_opt = CGAL::intersection(exact_edge_segment, exact_triangle)) { @@ -5262,9 +5249,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto tetrahedron = tr().tetrahedron(ch); auto exact_tetrahedron = to_exact(tetrahedron); for(auto fh : fh_region) { - auto v0 = fh->vertex(0)->info().vertex_handle_3d; - auto v1 = fh->vertex(1)->info().vertex_handle_3d; - auto v2 = fh->vertex(2)->info().vertex_handle_3d; + auto v0 = vertex_3d(fh->vertex(0)); + auto v1 = vertex_3d(fh->vertex(1)); + auto v2 = vertex_3d(fh->vertex(2)); auto triangle = typename Geom_traits::Triangle_3{tr().point(v0), tr().point(v1), tr().point(v2)}; auto exact_triangle = to_exact(triangle); @@ -5438,9 +5425,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void write_2d_triangle(std::ostream &out, const CDT_2_face_handle fh) { - const auto v0 = fh->vertex(0)->info().vertex_handle_3d; - const auto v1 = fh->vertex(1)->info().vertex_handle_3d; - const auto v2 = fh->vertex(2)->info().vertex_handle_3d; + const auto v0 = vertex_3d(fh->vertex(0)); + const auto v1 = vertex_3d(fh->vertex(1)); + const auto v2 = vertex_3d(fh->vertex(2)); write_triangle(out, v0, v1, v2); } From 6b465aecd23e51a93b92a3f93ce6fc9efe96fea3 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:57:08 +0200 Subject: [PATCH 041/151] replace -1 by a named constant --- ...nforming_constrained_Delaunay_triangulation_3.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index d56ca3557fa3..ec11f5844cf1 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -5695,6 +5695,8 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 using TDS = typename CDT_3::Triangulation::Triangulation_data_structure; static_assert(std::is_same_v>); + static constexpr int INVALID_SUBDOMAIN_INDEX = -1; + if(tr.dimension() < 3) { for(auto ch : tr.all_cell_handles()) { ch->set_subdomain_index(0); @@ -5707,7 +5709,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 } } else { for(auto ch : tr.all_cell_handles()) { - ch->set_subdomain_index(-1); + ch->set_subdomain_index(INVALID_SUBDOMAIN_INDEX); } // Use a flood algorithm to mark constrained connected components. @@ -5721,7 +5723,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 // Function to flood-fill a connected component with a given subdomain index auto flood_component = [&border](typename Tr::Cell_handle start, int subdomain_index) { - if(start->subdomain_index() != -1) + if(start->subdomain_index() != INVALID_SUBDOMAIN_INDEX) return; std::stack queue; @@ -5730,12 +5732,12 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 while(!queue.empty()) { auto ch = queue.top(); queue.pop(); - if(ch->subdomain_index() == -1) { + if(ch->subdomain_index() == INVALID_SUBDOMAIN_INDEX) { ch->set_subdomain_index(subdomain_index); for(int i = 0; i < 4; i++) { typename Tr::Facet f(ch, i); auto n = ch->neighbor(i); - if(n->subdomain_index() == -1) { + if(n->subdomain_index() == INVALID_SUBDOMAIN_INDEX) { if(ch->ccdt_3_data().is_facet_constrained(i)) border.push(f); else @@ -5755,7 +5757,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 auto f = border.top(); border.pop(); auto n = f.first->neighbor(f.second); - if(n->subdomain_index() == -1) { + if(n->subdomain_index() == INVALID_SUBDOMAIN_INDEX) { // If we are coming from an even subdomain, use next odd, and vice versa bool from_even = (f.first->subdomain_index() % 2 == 0); int next_index = from_even ? next_odd_subdomain : next_even_subdomain; @@ -5769,7 +5771,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 bool found_unvisited = false; for(auto ch : tr.finite_cell_handles()) { - if(ch->subdomain_index() == -1) { + if(ch->subdomain_index() == INVALID_SUBDOMAIN_INDEX) { flood_component(ch, next_even_subdomain); next_even_subdomain += 2; found_unvisited = true; From 3e263561752b6aee8a2a6c902df955add86895bd Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:57:40 +0200 Subject: [PATCH 042/151] bug fix --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index ec11f5844cf1..1e95225ba2cf 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -4653,6 +4653,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(restore_face(static_cast (i))) { face_constraint_misses_subfaces_reset(i); i = face_constraint_misses_subfaces_find_next(i); + if(i == face_constraint_misses_subfaces_npos) { + i = face_constraint_misses_subfaces_find_first(); + } } else { if(this->debug().missing_region() || this->debug().Steiner_points()) { std::cerr << "restore_face(" << i << ") incomplete, back to conforming...\n"; From 9065c9d39b42376a478ce07614ebd54efbb4e51f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:58:30 +0200 Subject: [PATCH 043/151] add function is_2d_face_also_a_3d_facet --- ...ing_constrained_Delaunay_triangulation_3.h | 67 ++++++++++++------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 1e95225ba2cf..7dfd9aac8e01 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2814,12 +2814,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D fh->info().missing_subface = false; continue; } - const auto v0 = fh->vertex(0)->info().vertex_handle_3d; - const auto v1 = fh->vertex(1)->info().vertex_handle_3d; - const auto v2 = fh->vertex(2)->info().vertex_handle_3d; - Cell_handle c; - int i, j, k; - if(!tr().is_facet(v0, v1, v2, c, i, j, k)) { + auto is_facet = this->is_2d_face_also_a_3d_facet(fh); + if(!is_facet) { fh->info().missing_subface = true; this->face_constraint_misses_subfaces_set(static_cast(polygon_constraint_id)); #if CGAL_CDT_3_DEBUG_MISSING_TRIANGLES @@ -2830,9 +2826,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D write_triangle(std::cerr, v0, v1, v2); #endif // CGAL_CDT_3_DEBUG_MISSING_TRIANGLES } else { + const auto [facet, orient] = *is_facet; fh->info().missing_subface = false; - const int facet_index = 6 - i - j - k; - set_facet_constrained({c, facet_index}, polygon_constraint_id, fh); + set_facet_constrained(facet, polygon_constraint_id, fh, orient); } } } @@ -3561,14 +3557,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto mirror_edge = cdt_2.mirror_edge({fh, i}); fh->set_constraint(i, mirror_edge.first->is_constrained(mirror_edge.second)); } - int i, j, k; - Cell_handle c; - [[maybe_unused]] bool fh_is_3d_facet = tr().is_facet(fh->vertex(0)->info().vertex_handle_3d, - fh->vertex(1)->info().vertex_handle_3d, - fh->vertex(2)->info().vertex_handle_3d, - c, i, j, k); - CGAL_assertion(fh_is_3d_facet); - set_facet_constrained({c, 6-i-j-k}, face_index, fh); + const auto fh_is_3d_facet = is_2d_face_also_a_3d_facet(fh); + CGAL_assertion(fh_is_3d_facet.has_value()); + const auto [facet, orient] = *fh_is_3d_facet; + set_facet_constrained(facet, face_index, fh, orient); fh->info().missing_subface = false; } return true; @@ -4064,6 +4056,38 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return std::nullopt; } + std::optional> + is_2d_face_also_a_3d_facet(CDT_2_face_handle fh_2d) const + { + const auto v0 = vertex_3d(fh_2d->vertex(0)); + const auto v1 = vertex_3d(fh_2d->vertex(1)); + const auto v2 = vertex_3d(fh_2d->vertex(2)); + typename T_3::Cell_handle c; + std::array indices; + const bool is_facet = tr().is_facet(v0, v1, v2, c, indices[0], indices[1], indices[2]); + if(is_facet) { + const int facet_index = 6 - indices[0] - indices[1] - indices[2]; + auto it = std::min_element(indices.begin(), indices.end()); + std::rotate(indices.begin(), it, indices.end()); + const std::array vertex_triple_index = { tr().vertex_triple_index(facet_index, 0), + tr().vertex_triple_index(facet_index, 1), + tr().vertex_triple_index(facet_index, 2) }; + CGAL_assertion(indices[0] == vertex_triple_index[0]); + CGAL_assertion((indices[1] == vertex_triple_index[1] && indices[2] == vertex_triple_index[2]) || + (indices[1] == vertex_triple_index[2] && indices[2] == vertex_triple_index[1])); + Orientation orient = indices[1] == vertex_triple_index[1] ? CGAL::EQUAL : CGAL::NEGATIVE; + + Facet facet{c, facet_index}; + if(orient == CGAL::NEGATIVE) { + facet = this->mirror_facet(facet); + } + CGAL_assertion(same_triple(facet, fh_2d)); + return std::make_pair(facet, CGAL::EQUAL); + } else { + return std::nullopt; + } + } + auto edge_of_cdt_2(const CDT_2& cdt_2, const Vertex_handle va, const Vertex_handle vb) const -> std::optional { @@ -4427,14 +4451,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(false == fh->info().missing_subface) { continue; } - Cell_handle c; - int i, j, k; - if(tr().is_facet(fh->vertex(0)->info().vertex_handle_3d, - fh->vertex(1)->info().vertex_handle_3d, - fh->vertex(2)->info().vertex_handle_3d, c, i, j, k)) + const auto is_facet_opt = is_2d_face_also_a_3d_facet(fh); + if(is_facet_opt) { - const int facet_index = 6 - i - j - k; - set_facet_constrained({c, facet_index}, face_index, fh); + const auto [facet, orientation] = *is_facet_opt; + set_facet_constrained(facet, face_index, fh, orientation); fh->info().missing_subface = false; continue; } From e0a4cea9aa14ee8bce9a12e60f5e4b89ef19044c Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 10:59:00 +0200 Subject: [PATCH 044/151] fix the display of timeings in `ms` --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 7dfd9aac8e01..babe9025c89e 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -4628,7 +4628,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void recheck_for_missing_subfaces() { CGAL::Real_timer timer; - timer.start(); + if(this->debug().display_statistics()) { + timer.start(); + } for(CDT_3_signed_index i = 0, end = signed_number_of_faces(); i < end; ++i) { if(this->face_data[i].skip_face == false) { search_for_missing_subfaces(i); @@ -4636,7 +4638,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } if(this->debug().display_statistics()) { timer.stop(); - std::cout << "[timings] recheck_for_missing_subfaces in " << timer.time() << " s\n"; + std::cout << "[timings] recheck_for_missing_subfaces in " << 1000 * timer.time() << " ms\n"; } } From f31d8ba3c856bda89332a9df4dbff1ff86093d36 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 11:00:48 +0200 Subject: [PATCH 045/151] add an extended bbox --- ...ing_constrained_Delaunay_triangulation_3.h | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index babe9025c89e..1237fd7b98aa 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2055,7 +2055,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D put(tr_vertex_pmap, v, vh); } - add_bbox_points_if_not_dimension_3(); + add_bbox_points(); if(this->debug().display_statistics()) { @@ -4885,26 +4885,29 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return new_face_handles_out; } - void add_bbox_points_if_not_dimension_3() { - if(this->dimension() != 3) { - const auto bbox = CGAL::bbox_3(this->points_begin(), this->points_end(), this->geom_traits()); - double d_x = bbox.xmax() - bbox.xmin(); - double d_y = bbox.ymax() - bbox.ymin(); - double d_z = bbox.zmax() - bbox.zmin(); + void add_bbox_points() { + const auto bbox = CGAL::bbox_3(this->points_begin(), this->points_end(), this->geom_traits()); + double d_x = bbox.xmax() - bbox.xmin(); + double d_y = bbox.ymax() - bbox.ymin(); + double d_z = bbox.zmax() - bbox.zmin(); - const double d = (std::max)({d_x, d_y, d_z}); + const double d = (std::max)({d_x, d_y, d_z}); - using Point = typename T_3::Point_3; + using Point = typename T_3::Point_3; - this->insert(Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmin() - d)); - this->insert(Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmin() - d)); - this->insert(Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmax() + d)); - this->insert(Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmax() + d)); - this->insert(Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmin() - d)); - this->insert(Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmin() - d)); - this->insert(Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmax() + d)); - this->insert(Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmax() + d)); + this->insert(Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmin() - d)); + this->insert(Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmin() - d)); + this->insert(Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmax() + d)); + this->insert(Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmax() + d)); + this->insert(Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmin() - d)); + this->insert(Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmin() - d)); + this->insert(Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmax() + d)); + this->insert(Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmax() + d)); + } + void add_bbox_points_if_not_dimension_3() { + if(this->dimension() != 3) { + add_bbox_points(); CGAL_assertion(this->dimension() == 3); } } From e6eb201f279513f73083a0b4e807e341dda9fffc Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 16:57:58 +0200 Subject: [PATCH 046/151] cosmetic changes --- ...orming_constrained_Delaunay_triangulation_3.h | 16 ++++++++++++---- .../internal/cdt_debug_io.h | 4 +--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 1237fd7b98aa..b85b74e4257c 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -138,6 +138,7 @@ #include #include #include +#include #include #include #include @@ -589,8 +590,12 @@ struct Output_rep, With_point_and_info_t std::ostream& operator()(std::ostream& out) const { auto v_ptr = this->it.operator->(); + if(v_ptr == nullptr) + return out << "nullptr"; + if(Time_stamper::time_stamp(v_ptr) == 0) + return out << "# = infinite_vertex()"; + out << Time_stamper::display_id(v_ptr, offset); - if(v_ptr == nullptr) return out; out << (v_ptr->ccdt_3_data().is_Steiner_vertex_on_edge() ? "(Steiner)" : "") << (v_ptr->ccdt_3_data().is_Steiner_vertex_in_face() ? "(Steiner in face)" : "") @@ -1662,7 +1667,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D bool move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { std::optional indent_guards; if(this->debug().move_Steiner_vertices()) { - std::cerr << "Moving Steiner vertex " << IO::oformat(v, With_point_and_info_tag{}) << " to the volume\n"; + std::cerr << "Moving Steiner vertex " << display_vert(v) << " to the volume\n"; indent_guards.emplace(IO::make_indenting_guards(" |")); } @@ -1727,7 +1732,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(!components_central_points.has_value()) { if(this->debug().move_Steiner_vertices()) { auto _ = IO::make_color_guards(std::cerr); - std::cerr << "- No central point could be computed for at least one of the components, aborting moving the vertex to the volume\n"; + std::cerr << "- No central point could be computed for at least one of the components, aborting moving the " + "vertex to the volume\n"; } return false; } @@ -1745,7 +1751,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto fh_2 = cell->ccdt_3_data().face_2(this->face_cdt_2(face_id), facet_index); fh_2->info().facet_3d = {}; fh_2->info().missing_subface = true; - this->set_facet_constrained({cell, facet_index}, -1, {}); + this->set_facet_as_not_constrained({cell, facet_index}); } void register_facet_to_be_constrained(Facet f) { @@ -2657,6 +2663,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return vh_2d; }; + vertex_3d(cdt_2.infinite_vertex()) = this->infinite_vertex(); + for(const auto& handles : vec_of_handles) { const auto first_2d = insert_vertex_in_cdt_2(handles.front()); 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 752aaacfec32..e3d3ed247db1 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 @@ -35,9 +35,7 @@ namespace CGAL { std::size_t i = 0; for(const auto& [cell, facet_index] : facets_range) { - const auto v0 = cell->vertex(Tr::vertex_triple_index(facet_index, 0)); - const auto v1 = cell->vertex(Tr::vertex_triple_index(facet_index, 1)); - const auto v2 = cell->vertex(Tr::vertex_triple_index(facet_index, 2)); + const auto [v0, v1, v2] = tr.vertices({cell, facet_index}); points.push_back(tr.point(v0)); points.push_back(tr.point(v1)); points.push_back(tr.point(v2)); From 45559bbbbf2b0ee90120a9dccf4393dc557a053e Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 17:05:05 +0200 Subject: [PATCH 047/151] WIP: moving Steiner vertices seems to work now --- ...ing_constrained_Delaunay_triangulation_3.h | 963 +++++++++++++----- 1 file changed, 727 insertions(+), 236 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index b85b74e4257c..a240a24449c4 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1394,6 +1394,68 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }, IO_manip_tag{}); } + bool same_triple(const std::array f1_vertices, + std::array f2_vertices, + Orientation o = EQUAL) const + { + auto f2_it_equal_to_f_first_vertex = std::find(f2_vertices.begin(), f2_vertices.end(), f1_vertices[0]); + if(f2_it_equal_to_f_first_vertex == f2_vertices.end()) return false; + std::rotate(f2_vertices.begin(), f2_it_equal_to_f_first_vertex, f2_vertices.end()); + CGAL_assertion(f1_vertices[0] == f2_vertices[0]); + if(o == NEGATIVE) { + return (f1_vertices[1] == f2_vertices[2] && f1_vertices[2] == f2_vertices[1]); + } else { + return (f1_vertices[1] == f2_vertices[1] && f1_vertices[2] == f2_vertices[2]); + } + } + + bool same_triple(Facet f, Facet f2, Orientation o = EQUAL) const { + return same_triple(tr().vertices(f), tr().vertices(f2), o); + } + + bool same_triple(Facet f, CDT_2_face_handle fh, Orientation o = EQUAL) const { + auto f_vertices = tr().vertices(f); + std::array fh_vertices{vertex_3d(fh->vertex(0)), + vertex_3d(fh->vertex(1)), + vertex_3d(fh->vertex(2))}; + return same_triple(f_vertices, fh_vertices, o); + } + + bool opposite_triple(Facet f, Facet f2) const { + return same_triple(f, f2, NEGATIVE); + } + + void set_facet_constrained(Facet f, CDT_3_signed_index polygon_constraint_id, + CDT_2_face_handle fh, [[maybe_unused]] Orientation o = EQUAL) + { + CGAL_assertion(fh == CDT_2_face_handle{} || same_triple(f, fh, o)); + CGAL_assertion(o == EQUAL); + + const auto [c, facet_index] = f; + c->ccdt_3_data().set_facet_constraint(facet_index, polygon_constraint_id, fh); + if(tr().dimension() > 2) { + const auto [n, n_index] = tr().mirror_facet(f); + n->ccdt_3_data().set_facet_constraint(n_index, polygon_constraint_id, fh); + } + if(fh != CDT_2_face_handle{}) { + fh->info().facet_3d = f; + } + } + + auto set_facet_as_not_constrained(Facet f) { + return set_facet_constrained(f, INVALID_FACE_INDEX, CDT_2_face_handle{}); + } + + void set_adjacency_between_3d_facets(Facet f1, Facet f2) const { + tr().tds().set_adjacency(f1.first, f1.second, f2.first, f2.second); + if(this->debug().move_Steiner_vertices()) { + std::cerr << cdt_3_format("Set adjacency between facets ({})=[{}] and ({})=[{}]\n", + IO::oformat(f1), IO::oformat(tr().vertices(f1), with_offset), + IO::oformat(f2), IO::oformat(tr().vertices(f2), with_offset)); + CGAL_assertion(opposite_triple(f1, f2)); + } + } + template std::optional find_in_incident_facets(Vertex_handle v, Pred pred) const @@ -1523,14 +1585,32 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D small_vector facets; small_vector component_offsets = {0}; - small_vector face_ids_between_consecutive_components; + small_vector nb_of_incident_facets_for_each_component; + small_vector> face_ids_between_consecutive_components; small_vector> component_incident_constraint_face_ids; + + auto component_facets_range(std::size_t component_index) const { + return CGAL::make_range(facets.begin() + component_offsets[component_index - 1], + facets.begin() + component_offsets[component_index]); + } }; + Orientation constrained_facet_orientation(const Facet& f) const { + const auto& cdt_2 = this->face_cdt_2(face_constraint_index(f)); + const auto fh_2d = f.first->ccdt_3_data().face_2(cdt_2, f.second); + if(fh_2d->info().facet_3d == f) { + return CGAL::EQUAL; + } + CGAL_assertion(fh_2d->info().facet_3d == T_3::mirror_facet(f)); + return CGAL::NEGATIVE; + } + Star_components_facets collect_star_components_facets(Vertex_handle v, Facet incident_constrained_facet) const { const auto [first_cell, first_facet_index] = incident_constrained_facet; const auto first_facet_face_id = face_constraint_index(incident_constrained_facet); + const Orientation first_facet_orientation = constrained_facet_orientation(incident_constrained_facet); + const auto vertex_Steiner_type = vertex_type(v); CGAL_assertion(vertex_Steiner_type == CDT_3_vertex_type::STEINER_IN_FACE || vertex_Steiner_type == CDT_3_vertex_type::STEINER_ON_EDGE); @@ -1549,12 +1629,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // start processing the next component. // -> Start with one of the facets incident to `v` that is constrained, and explore the star // of `v` through unconstrained facets as long as possible; - boost::container::small_vector, 128> cells_in_other_components = { - { first_cell, first_facet_face_id } + boost::container::small_vector, 128> cells_in_other_components = { + { first_cell, first_facet_face_id, first_facet_orientation } }; while(!cells_in_other_components.empty()) { - auto [other_cell, component_first_incident_face_id] = cells_in_other_components.back(); + auto [other_cell, component_first_incident_face_id, component_first_facet_orientation] = + cells_in_other_components.back(); cells_in_other_components.pop_back(); auto other_cell_is_a_new_cell = register_cell_and_check_if_new(other_cell); if(!other_cell_is_a_new_cell) continue; @@ -1562,8 +1643,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // start a new component from this cell out.facets.emplace_back(other_cell, other_cell->index(v), Star_components_facets::Type_of_facet::OPPOSITE_TO_V); - out.face_ids_between_consecutive_components.push_back(component_first_incident_face_id); - out.component_incident_constraint_face_ids.push_back({component_first_incident_face_id, -1}); // placeholder for the next component + out.face_ids_between_consecutive_components.push_back( + {component_first_incident_face_id, component_first_facet_orientation}); + out.component_incident_constraint_face_ids.push_back( + {component_first_incident_face_id, INVALID_FACE_INDEX}); // placeholder for the next component auto& current_component_incident_constraint_face_ids = out.component_incident_constraint_face_ids.back(); cells_in_other_components.clear(); // clear it here, and fill it again with the cells opposite to a constrained // facet during the exploration of the current component @@ -1602,7 +1685,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(constrained_facet_face_id != component_first_incident_face_id || vertex_Steiner_type == CDT_3_vertex_type::STEINER_IN_FACE ) { - cells_in_other_components.push_back({next_cell, constrained_facet_face_id}); + const auto orientation = constrained_facet_orientation(Facet(c, i)); + cells_in_other_components.push_back({next_cell, constrained_facet_face_id, orientation}); } } else { // Facet(c, i), between `c` and `next_cell` is not constrained auto next_is_a_new_cell = register_cell_and_check_if_new(next_cell); @@ -1616,21 +1700,30 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // end of the current component reached, start a new one if there is any cell in // another component that we haven't processed yet + // reorder the component to that incident facets are first + const auto component_offset = out.component_offsets.back(); + const auto begin_it = out.facets.begin() + component_offset; + const auto incident_facets_end = std::partition(begin_it, out.facets.end(), [](const auto& facet_info) { + return facet_info.type == Star_components_facets::Type_of_facet::INCIDENT_TO_V; + }); + + out.nb_of_incident_facets_for_each_component.push_back(std::distance(begin_it, incident_facets_end)); + out.component_offsets.push_back(out.facets.size()); + if(this->debug().move_Steiner_vertices()) { - std::cerr << " - Star component #" << out.component_offsets.size() << " has " - << (out.facets.size() - out.component_offsets.back()) << " facets.\n" + std::cerr << "- Star component #" << out.nb_of_incident_facets_for_each_component.size() + << " has:\n - " + << (out.facets.size() - component_offset) << " total facets,\n - " + << out.nb_of_incident_facets_for_each_component.back() << " facets incident to the vertex.\n" << " It is incident to constraint faces with ids: {" << IO::oformat(current_component_incident_constraint_face_ids) << "}\n"; } - - out.component_offsets.push_back(out.facets.size()); - } // end while there is still a component to process CGAL_assertion_code(const auto nb_of_components = out.component_incident_constraint_face_ids.size()); CGAL_assertion(false == out.facets.empty()); - CGAL_assertion(nb_of_components > 1); + CGAL_assertion(nb_of_components >= 1); CGAL_assertion(out.component_offsets.size() == nb_of_components + 1); CGAL_assertion(out.component_offsets.front() == 0); @@ -1642,19 +1735,20 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(std::all_of(out.component_incident_constraint_face_ids.begin(), out.component_incident_constraint_face_ids.end(), [first_facet_face_id](const std::array& face_ids) { - return face_ids[0] == first_facet_face_id && face_ids[1] == -1; + return face_ids[0] == first_facet_face_id && face_ids[1] == INVALID_FACE_INDEX; })); CGAL_assertion(std::all_of(out.face_ids_between_consecutive_components.begin(), out.face_ids_between_consecutive_components.end(), - [first_facet_face_id](const Face_index& face_id) + [first_facet_face_id](const std::pair& face_id_orientation) { - return face_id == first_facet_face_id; + return face_id_orientation.first == first_facet_face_id; })); } else { CGAL_assertion(vertex_Steiner_type == CDT_3_vertex_type::STEINER_ON_EDGE); CGAL_assertion(std::adjacent_find(out.face_ids_between_consecutive_components.begin(), out.face_ids_between_consecutive_components.end()) == out.face_ids_between_consecutive_components.end()); - CGAL_assertion(std::all_of(out.component_incident_constraint_face_ids.begin(), out.component_incident_constraint_face_ids.end(), + CGAL_assertion(nb_of_components == 1 || + std::all_of(out.component_incident_constraint_face_ids.begin(), out.component_incident_constraint_face_ids.end(), [](const std::array& face_ids) { return face_ids[1] != INVALID_FACE_INDEX; @@ -1664,6 +1758,473 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return out; } // end collect_star_components_facets(..) + template + std::pair + fill_cdt_2_hole_with_delaunay(CDT_2& cdt_2, + Face_index face_index, + CDT_3_vertex_type vertex_type, + std::list& hole_boundary, + const Map_from_hole_edge_to_facet_3d& map_from_hole_edge_to_facet_3d, + Face_is_outside_the_face is_outside_the_face, + New_CDT_2_face_handles_output_iterator new_face_handles_out) + { + const bool is_a_half_hole = vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; + if(this->debug().move_Steiner_vertices()) { + std::cerr << cdt_3_format("Filling {}hole in CDT_2 with {} edges, is_outside_the_face = {}\n", + is_a_half_hole ? "half " : "", hole_boundary.size(), + enum_cast(is_outside_the_face)); + } + const auto at_scope_exit_guard = + make_scope_exit([&cdt_2, hole_edges = std::vector(hole_boundary.begin(), hole_boundary.end())]() { + if(std::uncaught_exceptions() > 0) { + std::cerr << "WARNING: an exception is being thrown while filling the hole, the triangulation may be left " + "in an invalid state.\n"; + return; + } + // restore the constrained/unconstrained status of edges, from a copy of `hole_boundary`. + std::for_each(hole_edges.begin(), hole_edges.end(), [&cdt_2](CDT_2_edge e) { + auto [f, i] = cdt_2.mirror_edge(e); + f->set_constraint(i, cdt_2.is_constrained(e)); + }); + }); + + if(hole_boundary.size() <= 2) { // degenerated hole + CGAL_assertion(hole_boundary.size() == 2); // size == 1 is not possible + auto [f0, i0] = hole_boundary.front(); + auto [f1, i1] = hole_boundary.back(); + cdt_2.tds().set_adjacency(f1, i1, f0, i0); + return {new_face_handles_out, {}}; + } + + if(is_outside_the_face == OUTSIDE) { + CGAL::fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { + auto& info = fh->info(); + info.is_outside_the_face = OUTSIDE; + info.missing_subface = false; + info.is_in_region = 0; + info.facet_3d = {}; + })); + return {new_face_handles_out, {}}; + } + + const CDT_2_face_handle half_hole_neighbor_face = is_a_half_hole ? hole_boundary.back().first : CDT_2_face_handle{}; + Facet half_hole_front_facet{Cell_handle{}, 0}; + + std::for_each(hole_boundary.begin(), hole_boundary.end(), + [](const CDT_2_edge& e) { e.first->info().is_in_region = 0; }); + + const auto [outside_2d_face, outside_2d_index] = hole_boundary.front(); + const auto first_2d_face = outside_2d_face->neighbor(outside_2d_index); + const auto v0 = vertex_3d(outside_2d_face->vertex(cdt_2.cw(outside_2d_index))); + const auto v1 = vertex_3d(outside_2d_face->vertex(cdt_2.ccw(outside_2d_index))); + const auto [front_cell, front_index] = first_2d_face->info().facet_3d; + CGAL_assertion(front_cell != Cell_handle{}); + const auto [back_cell, back_index] = tr().mirror_facet({front_cell, front_index}); + + const Vertex_handle v_front = front_cell->vertex(6 - front_index - front_cell->index(v0) - front_cell->index(v1)); + const Vertex_handle v_back = back_cell->vertex(6 - back_index - back_cell->index(v0) - back_cell->index(v1)); + if(this->debug().move_Steiner_vertices()) { + std::cerr << cdt_3_format(" - first_2d_face has vertices ( {} ,\n" + " {} ,\n" + " {} )\n", + display_vert(vertex_3d(first_2d_face->vertex(0))), + display_vert(vertex_3d(first_2d_face->vertex(1))), + display_vert(vertex_3d(first_2d_face->vertex(2)))); + const auto vertices = tr().vertices(Facet{front_cell, front_index}); + std::cerr << cdt_3_format(" - front_cell is ({}) with vertices:\n" + " - {}\n" + " - {}\n" + " - {}\n" + " - (4th vertex: {})\n", + IO::oformat(front_cell), + display_vert(vertices[0]), + display_vert(vertices[1]), + display_vert(vertices[2]), + display_vert(front_cell->vertex(front_index))); + std::cerr << " - front vertex is " << display_vert(v_front) + << "\n - back vertex is "<< display_vert(v_back) << '\n'; + } + + auto action_for_each_new_2d_face = [&](CDT_2_face_handle fh_2d) { + auto& info = fh_2d->info(); + info.is_outside_the_face = INSIDE; + info.missing_subface = false; + info.is_in_region = 1; + + const auto v1 = vertex_3d(fh_2d->vertex(0)); + const auto v2 = vertex_3d(fh_2d->vertex(1)); + const auto v3 = vertex_3d(fh_2d->vertex(2)); + + if(this->debug().move_Steiner_vertices()) { + std::cerr << cdt_3_format("- new CDT_2 face with 3D vertices ( {} ,\n" + " {} ,\n" + " {} )\n", + display_vert(v1), + display_vert(v2), + display_vert(v3)); + } + + const auto new_front_cell = tr().tds().create_cell(v1, v2, v3, v_front); + CGAL_assertion(tr().orientation(v1->point(), v2->point(), v3->point(), v_front->point()) > 0); // DO NOT COMMIT (expansive assertion) + const auto new_back_cell = tr().tds().create_cell(v1, v3, v2, v_back); + CGAL_assertion(tr().orientation(v1->point(), v3->point(), v2->point(), v_back->point()) > 0); // DO NOT COMMIT (expansive assertion) + set_adjacency_between_3d_facets({new_front_cell, 3}, {new_back_cell, 3}); + set_facet_constrained({new_front_cell, 3}, face_index, fh_2d); + + for(int i = 0; i < 3; ++i) { + const CDT_2_face_handle neighbor_2d = fh_2d->neighbor(i); + + if(neighbor_2d == CDT_2_face_handle{}) { + // this edge of `fh_2d` is not yet connected + continue; + } else if(neighbor_2d == half_hole_neighbor_face) { + // this edge of `fh_2d` is dandling to be connected to the other part of the half hole + CGAL_assertion(half_hole_front_facet.first == Cell_handle{}); // only one edge of the hole can be a half hole + half_hole_front_facet = {new_front_cell, i}; + if(this->debug().move_Steiner_vertices()) { + auto new_front_cell_i_vertices = tr().vertices(Facet{new_front_cell, i}); + std::cerr << cdt_3_format(" - the 3D facet (new_front_cell={}, i={}) is ON THE BORDER of the half hole:\n" + " {}\n" + " {}\n" + " {}\n" + " (fourth vertex of its cell: {})\n" + , IO::oformat(new_front_cell), i + , display_vert(new_front_cell_i_vertices[0]) + , display_vert(new_front_cell_i_vertices[1]) + , display_vert(new_front_cell_i_vertices[2]) + , display_vert(new_front_cell->vertex(i))); + } + continue; + } + // else + CGAL_assertion(new_front_cell->neighbor(i) == Cell_handle{}); // facet (new_front_cell, i) is no yet connected + const bool neighbor_2d_is_in_the_star = neighbor_2d->info().is_in_region; + const auto [neighbor_front_cell_3d, neighbor_front_index_3d] = + neighbor_2d_is_in_the_star ? neighbor_2d->info().facet_3d + : map_from_hole_edge_to_facet_3d.at({neighbor_2d, neighbor_2d->index(fh_2d)}); + CGAL_assertion(neighbor_front_cell_3d != Cell_handle{}); + + const auto neighbor_back_cell_3d = neighbor_front_cell_3d->neighbor(neighbor_front_index_3d); + + const auto fh_index_in_neighbor_2d = + neighbor_2d_is_in_the_star ? neighbor_2d->index(fh_2d) : neighbor_front_index_3d; + + if(this->debug().move_Steiner_vertices()) { + std::cerr << cdt_3_format(" - the 2D face fh_2d->neighbor(i={}) IS{} in region. Its 3D vertices are:\n" + " {}\n" + " {}\n" + " {}\n" + , i, (neighbor_2d_is_in_the_star ? "" : " NOT") + , display_vert(vertex_3d(neighbor_2d->vertex(0))) + , display_vert(vertex_3d(neighbor_2d->vertex(1))) + , display_vert(vertex_3d(neighbor_2d->vertex(2)))); + auto new_front_cell_i_vertices = tr().vertices(Facet{new_front_cell, i}); + std::cerr << cdt_3_format(" - the 3D facet (new_front_cell={}, i={}) is\n" + " {}\n" + " {}\n" + " {}\n" + " (fourth vertex of its cell: {})\n" + , IO::oformat(new_front_cell), i + , display_vert(new_front_cell_i_vertices[0]) + , display_vert(new_front_cell_i_vertices[1]) + , display_vert(new_front_cell_i_vertices[2]) + , display_vert(new_front_cell->vertex(i))); + auto neighbor_front_cell_3d_vertices = tr().vertices(Facet{neighbor_front_cell_3d, fh_index_in_neighbor_2d}); + std::cerr << cdt_3_format(" - the 3D facet (neighbor_front_cell_3d={}, fh_index_in_neighbor_2d={}) is\n" + " {}\n" + " {}\n" + " {}\n" + " (fourth vertex of its cell: {})\n" + , IO::oformat(neighbor_front_cell_3d), fh_index_in_neighbor_2d + , display_vert(neighbor_front_cell_3d_vertices[0]) + , display_vert(neighbor_front_cell_3d_vertices[1]) + , display_vert(neighbor_front_cell_3d_vertices[2]) + , display_vert(neighbor_front_cell_3d->vertex(fh_index_in_neighbor_2d))); + } // end of debug output + + set_adjacency_between_3d_facets({new_front_cell, i}, {neighbor_front_cell_3d, fh_index_in_neighbor_2d}); + + const int back_cell_i = (3 - i) % 3; // the index of the neighbor in `new_back_cell` is deduced from the + // fact that `new_back_cell` was created with vertices in reversed + // order compared to `new_front_cell` + const int back_cell_fh_index_in_neighbor_2d = neighbor_2d_is_in_the_star + ? (3 - fh_index_in_neighbor_2d) % 3 + : neighbor_back_cell_3d->index(neighbor_front_cell_3d); + + if(this->debug().move_Steiner_vertices()) { + auto new_back_cell_vertices = tr().vertices(Facet{new_back_cell, back_cell_i}); + std::cerr << cdt_3_format(" - the 3D facet (new_back_cell={}, back_cell_i={}) is\n" + " {}\n" + " {}\n" + " {}\n" + " (fourth vertex of its cell: {})\n" + , IO::oformat(new_back_cell), back_cell_i + , display_vert(new_back_cell_vertices[0]) + , display_vert(new_back_cell_vertices[1]) + , display_vert(new_back_cell_vertices[2]) + , display_vert(new_back_cell->vertex(back_cell_i))); + auto neighbor_back_cell_3d_vertices = tr().vertices(Facet{neighbor_back_cell_3d, back_cell_fh_index_in_neighbor_2d}); + std::cerr << cdt_3_format(" - the 3D facet (neighbor_back_cell_3d={}, back_cell_fh_index_in_neighbor_2d={}) is\n" + " {}\n" + " {}\n" + " {}\n" + " (fourth vertex of its cell: {})\n" + , IO::oformat(neighbor_back_cell_3d), back_cell_fh_index_in_neighbor_2d + , display_vert(neighbor_back_cell_3d_vertices[0]) + , display_vert(neighbor_back_cell_3d_vertices[1]) + , display_vert(neighbor_back_cell_3d_vertices[2]) + , display_vert(neighbor_back_cell_3d->vertex(back_cell_fh_index_in_neighbor_2d))); + } // end of debug output + + set_adjacency_between_3d_facets({new_back_cell, back_cell_i}, + {neighbor_back_cell_3d, back_cell_fh_index_in_neighbor_2d}); + } + + + *new_face_handles_out++ = fh_2d; + }; // end of `action_for_each_new_2d_face` + CGAL::fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator(action_for_each_new_2d_face)); + return {new_face_handles_out, half_hole_front_facet}; + } + + template + std::tuple + remove_Steiner_vertex_from_cdt_2(Vertex_handle v, + CDT_3_vertex_type vertex_type, + Face_index face_id, + New_CDT_2_face_handles_output_iterator new_face_handles_out) + { + std::tuple result{new_face_handles_out, {}, {}}; + auto& [new_face_handles_out_it, half_hole_front_facet, second_half_hole_front_facet] = result; + auto& mutable_cdt_2 = non_const_face_cdt_2(face_id); + const auto& cdt_2 = mutable_cdt_2; + + typename CDT_2::Locate_type lt; + int i; + const auto fh_2d = cdt_2.locate(v->point(), lt, i); + CGAL_assertion(lt == CDT_2::VERTEX); + + const auto vh_2d = fh_2d->vertex(i); + CGAL_assertion(vertex_3d(vh_2d) == v); + + if(this->debug().move_Steiner_vertices()) { + using std::begin; using std::end; + std::for_each(begin(cdt_2.all_face_handles()), end(cdt_2.all_face_handles()), + [](CDT_2_face_handle fh) { + fh->info().is_in_region = 0; + }); + auto face_circ = cdt_2.incident_faces(vh_2d); + const auto done = face_circ; + auto nb_of_incident_faces = 0u; + do { + face_circ->info().is_in_region = 1; + ++nb_of_incident_faces; + } while(++face_circ != done); + dump_face(face_id); + std::cerr << " - remove Steiner vertex " << display_vert(v) << " from CDT_2 of face F#" << face_id + << " (nb of incident faces: " << nb_of_incident_faces << ")\n"; + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << display_vert(v) + << " from face F#" << face_id << '\n'; + CGAL_error_msg("Aborting due to invalid CDT_2"); + } + } + auto indent_guard = IO::make_indenting_guards(this->debug().move_Steiner_vertices() ? 4 : 0); + + auto map_from_hole_edges_to_incident_3d_facets = [&](const auto& hole_boundary_begin, const auto& hole_boundary_end, + Face_is_outside_the_face is_outside_the_face) { + CGAL::unordered_flat_map> map; + if(is_outside_the_face == OUTSIDE) + return map; + + for(auto it = hole_boundary_begin; it != hole_boundary_end; ++it) { + auto [fh_2d, index] = *it; + CGAL_assertion(cdt_2.tds().is_face(fh_2d)); // DO NOT COMMIT (expansive assertion: O(n) in the size of the cdt_2) + const auto neighbor_2d_inside_star = fh_2d->neighbor(index); + CGAL_assertion(cdt_2.tds().is_face(neighbor_2d_inside_star)); // DO NOT COMMIT (expansive assertion: O(n) in the size of the cdt_2) + const auto facet_3d = neighbor_2d_inside_star->info().facet_3d; + CGAL_assertion(facet_3d.first != Cell_handle{}); + [[maybe_unused]] auto [_, inserted] = map.insert({{fh_2d, index}, facet_3d}); + CGAL_assertion(inserted); + if(this->debug().move_Steiner_vertices()) { + auto vertices = tr().vertices(facet_3d); + std::cerr << cdt_3_format("in the star restricted to the polygon, 3D facet\n" + " {}\n" + " {}\n" + " {}\n", + display_vert(vertices[0]), display_vert(vertices[1]), display_vert(vertices[2])); + } + } + return map; + }; + + auto bore_hole_and_fill_with_delaunay = [&](CDT_3_vertex_type vertex_type, auto& hole_boundary, + const auto& faces_to_delete, + Face_is_outside_the_face is_outside_the_face) { + if(this->debug().move_Steiner_vertices()) { + std::cerr << "The star of " << display_vert(v) << " in the CDT_2 of face F#" << face_id << " has a component " + << (is_outside_the_face == OUTSIDE ? "OUTSIDE the face " : "") << "with " + << hole_boundary.size() << " edges, and " << faces_to_delete.size() << " faces:\n"; + for(const auto& fh_2d : faces_to_delete) { + std::cerr << cdt_3_format("- CDT_2 face with 3D vertices ( {} ,\n" + " {} ,\n" + " {} )\n", + display_vert(vertex_3d(fh_2d->vertex(0))), + display_vert(vertex_3d(fh_2d->vertex(1))), + display_vert(vertex_3d(fh_2d->vertex(2)))); + } + } // end of debug output + + // if the vertex is a Steiner point on an edge, the hole is a half-hole, and the last boundary + // edge is not part of the original hole boundary. + const auto hole_boundary_end = + vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE ? std::prev(hole_boundary.end()) : hole_boundary.end(); + + const auto hole_boundary_map = + map_from_hole_edges_to_incident_3d_facets(hole_boundary.begin(), hole_boundary_end, is_outside_the_face); + std::for_each(faces_to_delete.begin(), faces_to_delete.end(), + [&mutable_cdt_2](CDT_2_face_handle f) { mutable_cdt_2.delete_face(f); }); + return fill_cdt_2_hole_with_delaunay(mutable_cdt_2, face_id, vertex_type, hole_boundary, hole_boundary_map, + is_outside_the_face, new_face_handles_out); + }; + + auto set_constrained = [&](CDT_2_edge e, bool is_constrained) { + auto [f, i] = e; + f->set_constraint(i, is_constrained); + auto [n, ni] = cdt_2.mirror_edge(e); + n->set_constraint(ni, is_constrained); + }; + + if(vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE) { + + auto first_constrained_edge_circulator_before_face = [&](CDT_2_face_handle incident_fh) { + auto edge_circulator = cdt_2.incident_edges(vh_2d, incident_fh); + --edge_circulator; // take the edge before `incident_fh` + CGAL_assertion_code(const auto done = edge_circulator); + while(!cdt_2.is_constrained(*edge_circulator)) { + --edge_circulator; + CGAL_assertion(edge_circulator != done); + } + return edge_circulator; + }; + + auto advance_circulator_until_next_constrained_edge = [&](auto& edge_circulator, auto func) { + CGAL_assertion_code(const auto done = edge_circulator); + do { + ++edge_circulator; + func(*edge_circulator); + CGAL_assertion(edge_circulator != done); + } while(!cdt_2.is_constrained(*edge_circulator)); + return edge_circulator; + }; + + auto half_hole = [&](CDT_2_face_handle incident_fh) { + struct Result + { + std::list hole_side; + boost::container::small_vector to_delete; + CDT_2_edge last_incident_edge; + } result; + auto edge_circulator = first_constrained_edge_circulator_before_face(incident_fh); + result.last_incident_edge = + *advance_circulator_until_next_constrained_edge(edge_circulator, [&](CDT_2_edge current_edge) { + const auto [f, i] = current_edge; + result.to_delete.push_back(f); + const auto edge_opposite_to_v = cdt_2.mirror_edge(CDT_2_edge{f, cdt_2.cw(i)}); + result.hole_side.push_back(edge_opposite_to_v); + const auto [n, ni] = edge_opposite_to_v; + n->vertex(cdt_2.cw(ni))->set_face(n); + n->vertex(cdt_2.ccw(ni))->set_face(n); + }); + return result; + }; + + auto [hole_side_1, hole_side_1_faces_to_delete, side_1_last_incident_edge] = half_hole(vh_2d->face()); + const auto [side_1_last_face, side_1_last_edge_index] = side_1_last_incident_edge; + const auto side_1_hole_is_outside_the_face = side_1_last_face->info().is_outside_the_face; + + const auto side_2_first_face = side_1_last_face->neighbor(side_1_last_edge_index); + + auto [hole_side_2, hole_side_2_faces_to_delete, side_2_last_incident_edge] = half_hole(side_2_first_face); + const auto [side_2_last_face, side_2_last_edge_index] = side_2_last_incident_edge; + const auto side_2_hole_is_outside_the_face = side_2_last_face->info().is_outside_the_face; + + CGAL_assertion(side_1_last_incident_edge.first != side_2_last_incident_edge.first); + + // unmark the two constrained edges + set_constrained(side_1_last_incident_edge, false); + set_constrained(side_2_last_incident_edge, false); + + // start boring and sewing the two half-holes: + // - we start from side 1, + // - `side_2_last_face` will temporarily be modified to close `hole_side_1` + const auto v1 = side_1_last_face->vertex(cdt_2.ccw(side_1_last_edge_index)); + side_2_last_face->set_vertex(cdt_2.cw(side_2_last_edge_index), v1); + hole_side_1.push_back(side_2_last_incident_edge); + + // - bore the hole on side 1, and fill it with CDT triangles + auto [out_it, facet_side_1] = bore_hole_and_fill_with_delaunay( + vertex_type, hole_side_1, hole_side_1_faces_to_delete, side_1_hole_is_outside_the_face); + new_face_handles_out = out_it; + CGAL_assertion(side_1_hole_is_outside_the_face == OUTSIDE || facet_side_1.first != Cell_handle{}); + + // - now the edge opposite to `(side_2_last_face, side_2_last_edge_index)` is on the border of the hole + // `hole_side_2`, so we can start sewing from there + const auto side_1_border_edge = cdt_2.mirror_edge(CDT_2_edge{side_2_last_face, side_2_last_edge_index}); + hole_side_2.push_back(side_1_border_edge); + auto [out_it_2, facet_side_2] = bore_hole_and_fill_with_delaunay( + vertex_type, hole_side_2, hole_side_2_faces_to_delete, side_2_hole_is_outside_the_face); + new_face_handles_out = out_it_2; + CGAL_assertion(side_2_hole_is_outside_the_face == OUTSIDE || facet_side_2.first != Cell_handle{}); + + half_hole_front_facet = facet_side_1; + second_half_hole_front_facet = facet_side_2; + + // - then re-mark the edge between the two holes as a constrained edge + set_constrained(side_1_border_edge, true); + } else { // if vertex_type != CDT_3_vertex_type::STEINER_ON_EDGE + CGAL_assertion(vertex_type == CDT_3_vertex_type::STEINER_IN_FACE); + + auto [hole_boundary, faces_to_delete] = std::invoke([&]() { + struct Result + { + std::list hole_boundary; + boost::container::small_vector to_delete; + } result; + auto face_circulator = cdt_2.incident_faces(vh_2d); + const auto done = face_circulator; + do { + const CDT_2_face_handle fh_2d = face_circulator; + auto v_index = fh_2d->index(vh_2d); + CDT_2_edge opposite_edge = cdt_2.mirror_edge(CDT_2_edge{fh_2d, v_index}); + auto adjacent_vertex = fh_2d->vertex(cdt_2.cw(v_index)); + adjacent_vertex->set_face(opposite_edge.first); + result.hole_boundary.push_back(opposite_edge); + result.to_delete.push_back(fh_2d); + } while(++face_circulator != done); + return result; + }); + + auto [out_it, facet] = + bore_hole_and_fill_with_delaunay(vertex_type, hole_boundary, faces_to_delete, INSIDE); + new_face_handles_out = out_it; + CGAL_assertion(facet.first == Cell_handle{}); + } + mutable_cdt_2.delete_vertex(vh_2d); + if(this->debug().move_Steiner_vertices()) { + dump_face(face_id, std::string("dump_face_") + std::to_string(face_id) + "_after_removing_Steiner_vertex_" + + std::to_string(vh_2d->time_stamp()) + ".off"); + + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << IO::oformat(v, with_point_and_info) + << " from face F#" << face_id << '\n'; + CGAL_assertion(false); + } + } + new_face_handles_out_it = new_face_handles_out; + return result; + } // end of `remove_Steiner_vertex_from_cdt_2` + bool move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { std::optional indent_guards; if(this->debug().move_Steiner_vertices()) { @@ -1671,8 +2232,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D indent_guards.emplace(IO::make_indenting_guards(" |")); } - auto is_constrained = [this](Facet f) { return is_facet_constrained(f); }; - const auto incident_constrained_facet_opt = find_in_incident_facets(v, is_constrained); + const auto incident_constrained_facet_opt = + find_in_incident_facets(v, [this](Facet f) { return is_facet_constrained(f); }); CGAL_assertion(incident_constrained_facet_opt.has_value()); const auto incident_constrained_facet = *incident_constrained_facet_opt; @@ -1693,6 +2254,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().move_Steiner_vertices()) { std::cerr << "- Nb of components or the star: " << star_components.component_offsets.size() - 1 << '\n'; } + if(nb_of_star_components == 1) { + if(this->debug().move_Steiner_vertices()) { + auto _ = IO::make_color_guards(std::cerr); + std::cerr << "- Only one component in the star, cannot move the vertex to the volume, aborting\n"; + } + return false; + } const auto components_central_points = std::invoke([&]() { std::optional> central_points{std::in_place}; @@ -1702,7 +2270,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D construct_star_component_mesh(component_index, star_components.component_offsets, star_components.facets); auto opt_kernel_center_point = compute_center_point_if_possible(component_mesh); if(this->debug().move_Steiner_vertices()) { - std::cerr << " - kernel center point of component #" << component_index << ": "; + std::cerr << " - incident constraint faces ids of component #" << component_index << ": {" + << IO::oformat(star_components.component_incident_constraint_face_ids[component_index - 1]) + << "}\n"; + std::cerr << " kernel center point of component #" << component_index << ": "; } if(opt_kernel_center_point.has_value()) { auto kernel_center_point = *opt_kernel_center_point; @@ -1737,11 +2308,132 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } return false; } + CGAL_assertion(components_central_points->size() == nb_of_star_components); + boost::container::small_vector new_center_vertices; + new_center_vertices.reserve(nb_of_star_components); + + for(std::size_t component_index = 1; component_index <= nb_of_star_components; ++component_index) + { + const auto star_component_new_center_vertex = tr().tds().create_vertex(); + star_component_new_center_vertex->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); + star_component_new_center_vertex->set_point((*components_central_points)[component_index - 1]); + new_center_vertices.push_back(star_component_new_center_vertex); + + const auto& component_facets = star_components.component_facets_range(component_index); + for(const auto& [cell, facet_index, facet_type] : component_facets) { + if(facet_type == Star_components_facets::Type_of_facet::INCIDENT_TO_V) { + continue; + } + cell->set_vertex(facet_index, star_component_new_center_vertex); + + // now unconstrain the facets of `cell` that were incident to `v`, because they are now + // incident to `star_component_new_center_vertex` instead of `v`. + for(int i = 0; i < 3; ++i) { + const auto incident_facet_index = Tds::vertex_triple_index(facet_index, i); + set_facet_as_not_constrained(Facet{cell, incident_facet_index}); + } + } // end for each facet of the current component, which are now all incident to `star_component_new_center_vertex` + using std::begin; + star_component_new_center_vertex->set_cell(begin(component_facets)->cell); // cell of the first facet + } // loop on each component of the star of `v` + + auto other_side_of_cdt_2 = [&](const Facet& facet, Face_index face_id) -> Facet { + const auto [cell, facet_index] = facet; + for(int i = 0; i < 3; ++i) { + const auto other_facet_index = Tds::vertex_triple_index(facet_index, i); + if(face_constraint_index(Facet{cell, other_facet_index}) == face_id) { + const auto opposite_vertex = cell->vertex(facet_index); + const auto neighbor_cell = cell->neighbor(other_facet_index); + return Facet{neighbor_cell, neighbor_cell->index(opposite_vertex)}; + } + } + CGAL_error(); + }; + std::size_t nb_of_new_2d_faces = 0; - this->remove_Steiner_vertex_from_all_cdt_2(v, CGAL::Counting_output_iterator{&nb_of_new_2d_faces}); + CGAL::Counting_output_iterator output_iterator{&nb_of_new_2d_faces}; + + const auto v_type = vertex_type(v); + if(v_type == CDT_3_vertex_type::STEINER_IN_FACE) { + CGAL_assertion(star_components.component_incident_constraint_face_ids.size() == 2); + const auto& [face_id, orientation] = star_components.face_ids_between_consecutive_components.front(); + auto [new_output_iterator, half_hole_front_facet, second_half_hole_front_facet] = + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, output_iterator); + output_iterator = new_output_iterator; + } else { + CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); + std::optional previous_facet_to_reconnect_opt; + std::optional last_facet_to_reconnect_to_opt; + for(std::size_t component_index = 0, end = star_components.face_ids_between_consecutive_components.size(); + component_index < end; ++component_index) + { + const auto& [face_id, orientation] = star_components.face_ids_between_consecutive_components[component_index]; + if(this->debug().move_Steiner_vertices()) { + std::cerr << "Component " << (component_index+1) << " with center " + << display_vert(new_center_vertices[component_index]) << "\n"; + std::cerr << "Now remove the Steiner vertex from the CDT_2 of face with id " << face_id << " (orientation: " + << (orientation == CGAL::NEGATIVE ? "negative" : "positive") << ")\n"; + } + auto [new_output_iterator, half_hole_front_facet, second_half_hole_front_facet] = + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, output_iterator); + output_iterator = new_output_iterator; + + Facet facet_to_reconnect = half_hole_front_facet; + if(facet_to_reconnect.first == Cell_handle{}) { + facet_to_reconnect = second_half_hole_front_facet; + } + CGAL_assertion(facet_to_reconnect.first != Cell_handle{}); + if(true == facet_to_reconnect.first->has_vertex(new_center_vertices[component_index])) { + facet_to_reconnect = other_side_of_cdt_2(facet_to_reconnect, face_id); + } + if(this->debug().move_Steiner_vertices()) { + auto vertices = this->vertices(facet_to_reconnect); + std::cerr << cdt_3_format("The facet ({}) to reconnect in the 3D triangulation is:\n" + " - {}\n" + " - {}\n" + " - {}\n" + " - (4th vertex: {})\n", + IO::oformat(facet_to_reconnect), + display_vert(vertices[0]), + display_vert(vertices[1]), + display_vert(vertices[2]), + display_vert(facet_to_reconnect.first->vertex(facet_to_reconnect.second))); + auto other_facet = other_side_of_cdt_2(facet_to_reconnect, face_id); + vertices = this->vertices(other_facet); + std::cerr << cdt_3_format("The facet ({}) on the other side of the CDT_2 is:\n" + " - {}\n" + " - {}\n" + " - {}\n" + " - (4th vertex: {})\n", + IO::oformat(other_facet), + display_vert(vertices[0]), + display_vert(vertices[1]), + display_vert(vertices[2]), + display_vert(other_facet.first->vertex(other_facet.second))); + } + + if(!last_facet_to_reconnect_to_opt.has_value()) { + last_facet_to_reconnect_to_opt = facet_to_reconnect; + CGAL_assertion(previous_facet_to_reconnect_opt.has_value() == false); + } + if(previous_facet_to_reconnect_opt.has_value()) { + set_adjacency_between_3d_facets(*previous_facet_to_reconnect_opt, facet_to_reconnect); + } + previous_facet_to_reconnect_opt = other_side_of_cdt_2(facet_to_reconnect, face_id); + } + CGAL_assertion(previous_facet_to_reconnect_opt.has_value()); + CGAL_assertion(last_facet_to_reconnect_to_opt.has_value()); + set_adjacency_between_3d_facets(*previous_facet_to_reconnect_opt, *last_facet_to_reconnect_to_opt); + } + if(this->debug().move_Steiner_vertices()) { std::cerr << "- number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; } + + tr().tds().delete_vertex(v); + + CGAL_assertion(this->debug().move_Steiner_vertices() == false || tr().tds().is_valid(true)); // DO NOT COMMIT + return true; } // end move_one_Steiner_vertex_to_the_volume(..) @@ -2182,42 +2874,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D [this](auto f) { return is_facet_constrained(f); }); } - bool same_triangle(Facet f, CDT_2_face_handle fh) const { - return true; - const auto [c, facet_index] = f; - std::array f_vertices{c->vertex(tr().vertex_triple_index(facet_index,0)), - c->vertex(tr().vertex_triple_index(facet_index,1)), - c->vertex(tr().vertex_triple_index(facet_index,2))}; - std::sort(f_vertices.begin(), f_vertices.end()); - std::array fh_vertices{fh->vertex(0)->info().vertex_handle_3d, - fh->vertex(1)->info().vertex_handle_3d, - fh->vertex(2)->info().vertex_handle_3d}; - std::sort(fh_vertices.begin(), fh_vertices.end()); - return (f_vertices == fh_vertices); - } - - void set_facet_constrained(Facet f, CDT_3_signed_index polygon_constraint_id, - CDT_2_face_handle fh) - { - CGAL_assertion(fh == CDT_2_face_handle{} || same_triangle(f, fh)); - - const auto [c, facet_index] = f; - c->ccdt_3_data().set_facet_constraint(facet_index, polygon_constraint_id, fh); - if(tr().dimension() > 2) { - const auto [n, n_index] = tr().mirror_facet({c, facet_index}); - n->ccdt_3_data().set_facet_constraint(n_index, polygon_constraint_id, fh); - } - if(fh == CDT_2_face_handle{}) return; - - if(fh != CDT_2_face_handle{}) { - fh->info().facet_3d = f; - } - } - - auto set_facet_as_not_constrained(Facet f) { - return set_facet_constrained(f, INVALID_FACE_INDEX, CDT_2_face_handle{}); - } - template ) Polygon> std::optional register_new_constrained_polygon(Polygon&& polygon) { return insert_constrained_polygon(std::forward(polygon), false); @@ -3910,15 +4566,17 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D continue; // we might be in a sliver in the plane of the polygon const auto [fh_2d, reverse_orientation] = *is_facet; if(this->debug().regions()) facets_of_polygon.push_back(f); - const auto vt_aux = this->make_vertex_triple(f); auto vt = T_3::make_canonical_oriented_mapped_triple(map_cavity_vertices_to_ambient_vertices, f); + if(reverse_orientation == is_upper_cavity) { std::swap(vt[1], vt[2]); } - auto new_cell = this->tds().create_cell(); + auto new_cell = this->tds().create_cell(vt[0], vt[1], vt[2], this->infinite_vertex()); pseudo_cells.emplace_back(new_cell, fh_2d); - new_cell->set_vertices(vt[0], vt[1], vt[2], this->infinite_vertex()); - CGAL_assertion(static_cast(facet_is_facet_of_cdt_2(*this, {new_cell, 3}, cdt_2))); - add_to_outer_map(vt, {new_cell, 3}, "pseudo "); + Facet new_facet{new_cell, 3}; + CGAL_assertion_code(const auto new_facet_is_2d = facet_is_facet_of_cdt_2(*this, new_facet, cdt_2)); + CGAL_assertion(new_facet_is_2d.has_value() && + new_facet_is_2d->fh == fh_2d && new_facet_is_2d->reversed_orientation != is_upper_cavity); + add_to_outer_map(vt, new_facet, "pseudo "); } if(this->debug().regions()) { std::ofstream out(cdt_3_format("dump_{}_pseudo_cells_region_{}_{}.off", is_upper_cavity ? "upper" : "lower", @@ -3957,12 +4615,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::vector> new_constrained_facets; new_constrained_facets.reserve(pseudo_cells.size()); for(const auto& [c, fh_2d] : pseudo_cells) { - const Facet f = this->mirror_facet({c, 3}); - new_constrained_facets.emplace_back(f, fh_2d); - CGAL_assertion(static_cast(facet_is_facet_of_cdt_2(*this, f, cdt_2))); - auto vt = this->make_vertex_triple(f); - this->make_canonical_oriented_triple(vt); - add_to_outer_map(vt, f, "pseudo "); + const Facet f{c, 3}; + const Facet opposite_f = this->mirror_facet(f); + new_constrained_facets.emplace_back(opposite_f, fh_2d); + CGAL_assertion(static_cast(facet_is_facet_of_cdt_2(*this, opposite_f, cdt_2))); + auto vt = T_3::make_canonical_oriented_triple(opposite_f); + add_to_outer_map(vt, opposite_f, "pseudo "); this->tds().delete_cell(c); } fill_outer_map_of_cavity(lower_cavity_triangulation, facets_of_lower_cavity); @@ -4013,7 +4671,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::for_each(facets_of_upper_cavity.begin(), facets_of_upper_cavity.end(), restore_markers); for(const auto& [f, f2d] : new_constrained_facets) { - set_facet_constrained(f, face_index, f2d); + set_facet_constrained(T_3::mirror_facet(f), face_index, f2d); f2d->info().missing_subface = false; } CGAL_assume(!this->debug().validity() || this->is_valid(true)); @@ -4726,173 +5384,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D "All faces have been restored, but the triangulation is a CDT. This should not happen."); } - template - New_CDT_2_face_handles_output_iterator - remove_Steiner_vertex_from_cdt_2(Vertex_handle v, - CDT_3_vertex_type vertex_type, - Face_index face_id, - New_CDT_2_face_handles_output_iterator new_face_handles_out) - { - auto& cdt_2 = non_const_face_cdt_2(face_id); - - if(this->debug().move_Steiner_vertices()) { - dump_face(face_id); - std::cerr << " - remove Steiner vertex " << IO::oformat(v, with_point_and_info) << " from CDT_2 of face F#" << face_id - << '\n'; - if(!cdt_2.CT_2::is_valid(true, 3)) { - std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << IO::oformat(v, with_point_and_info) - << " from face F#" << face_id << '\n'; - CGAL_assertion(false); - } - } - - typename CDT_2::Locate_type lt; - int i; - const auto fh_2d = cdt_2.locate(v->point(), lt, i); - CGAL_assertion(lt == CDT_2::VERTEX); - - const auto vh_2d = fh_2d->vertex(i); - CGAL_assertion(vh_2d->info().vertex_handle_3d == v); - - auto fill_with_delaunay = [&](auto& hole_boundary, bool is_outside_the_face) { - const std::vector hole_edges(hole_boundary.begin(), hole_boundary.end()); - if(hole_boundary.size() > 2) { - fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { - auto& info = fh->info(); - info.is_outside_the_face = is_outside_the_face; - info.missing_subface = true; - info.is_in_region = 0; - info.facet_3d = {}; - *new_face_handles_out++ = fh; - })); - } else { // hole_boundary.size() <= 2 (degenerated hole) - CGAL_assertion(hole_boundary.size() == 2); // size == 1 is not possible - auto [f, i] = hole_boundary.front(); - auto [g, j] = hole_boundary.back(); - g->set_neighbor(j, f); - f->set_neighbor(i, g); - } - std::for_each(hole_edges.begin(), hole_edges.end(), [&cdt_2](CDT_2_edge e) { - auto [f, i] = cdt_2.mirror_edge(e); - f->set_constraint(i, cdt_2.is_constrained(e)); - }); - }; - auto bore_hole_and_fill_with_delaunay = [&](auto& hole_boundary, const auto& faces_to_delete, - bool is_outside_the_face) { - std::for_each(faces_to_delete.begin(), faces_to_delete.end(), - [&cdt_2](CDT_2_face_handle f) { cdt_2.delete_face(f); }); - fill_with_delaunay(hole_boundary, is_outside_the_face); - }; - - auto set_constrained = [&](CDT_2_edge e, bool is_constrained) { - auto [f, i] = e; - f->set_constraint(i, is_constrained); - auto [n, ni] = cdt_2.mirror_edge(e); - n->set_constraint(ni, is_constrained); - }; - - if(vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE) { - - auto half_hole = [&](CDT_2_face_handle incident_fh) { - struct Result - { - std::list hole_side; - boost::container::small_vector to_delete; - CDT_2_edge last_incident_edge; - } result; - auto edge_circulator = cdt_2.incident_edges(vh_2d, incident_fh); - --edge_circulator; - CGAL_assertion_code(auto done = edge_circulator); - while(!cdt_2.is_constrained(*edge_circulator)) { - --edge_circulator; - CGAL_assertion(edge_circulator != done); - } - CGAL_assertion_code(done = edge_circulator); - do { - ++edge_circulator; - const auto [f, i] = *edge_circulator; - result.to_delete.push_back(f); - const auto edge = cdt_2.mirror_edge(CDT_2_edge{f, cdt_2.cw(i)}); - result.hole_side.push_back(edge); - const auto [n, ni] = edge; - n->vertex(cdt_2.cw(ni))->set_face(n); - n->vertex(cdt_2.ccw(ni))->set_face(n); - CGAL_assertion(edge_circulator != done); - } while(!cdt_2.is_constrained(*edge_circulator)); - result.last_incident_edge = *edge_circulator; - return result; - }; - - auto [hole_side_1, hole_side_1_faces_to_delete, side_1_last_incident_edge] = half_hole(vh_2d->face()); - const auto [side_1_last_face, side_1_last_edge_index] = side_1_last_incident_edge; - const auto side_1_hole_is_outside_the_face = side_1_last_face->info().is_outside_the_face; - - const auto side_2_first_face = side_1_last_face->neighbor(side_1_last_edge_index); - - auto [hole_side_2, hole_side_2_faces_to_delete, side_2_last_incident_edge] = half_hole(side_2_first_face); - const auto [side_2_last_face, side_2_last_edge_index] = side_2_last_incident_edge; - const auto side_2_hole_is_outside_the_face = side_2_last_face->info().is_outside_the_face; - - CGAL_assertion(side_1_last_incident_edge.first != side_2_last_incident_edge.first); - - // unmark the two constrained edges - set_constrained(side_1_last_incident_edge, false); - set_constrained(side_2_last_incident_edge, false); - - // start boring and sewing the two half-holes: - // - we start from side 1, - // - `side_2_last_face` will temporarily be modified to close `hole_side_1` - const auto v1 = side_1_last_face->vertex(cdt_2.ccw(side_1_last_edge_index)); - side_2_last_face->set_vertex(cdt_2.cw(side_2_last_edge_index), v1); - hole_side_1.push_back(side_2_last_incident_edge); - - // - bore the hole on side 1, and fill it with CDT triangles - bore_hole_and_fill_with_delaunay(hole_side_1, hole_side_1_faces_to_delete, side_1_hole_is_outside_the_face); - - // - now the edge opposite to `(side_2_last_face, side_2_last_edge_index)` is on the border of the hole - // `hole_side_2`, so we can start sewing from there - const auto side_1_border_edge = cdt_2.mirror_edge(CDT_2_edge{side_2_last_face, side_2_last_edge_index}); - hole_side_2.push_back(side_1_border_edge); - bore_hole_and_fill_with_delaunay(hole_side_2, hole_side_2_faces_to_delete, side_2_hole_is_outside_the_face); - - // - then re-mark the edge between the two holes as a constrained edge - set_constrained(side_1_border_edge, true); - } else { - auto hole = cdt_2.make_hole(vh_2d); - fill_with_delaunay(hole, /*is_outside_the_face=*/false); - } - cdt_2.delete_vertex(vh_2d); - if(this->debug().move_Steiner_vertices()) { - dump_face(face_id, std::string("dump_face_") + std::to_string(face_id) + "_after_removing_Steiner_vertex_" + - std::to_string(vh_2d->time_stamp()) + ".off"); - - if(!cdt_2.CT_2::is_valid(true, 3)) { - std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << IO::oformat(v, with_point_and_info) - << " from face F#" << face_id << '\n'; - CGAL_assertion(false); - } - } - return new_face_handles_out; - } - - template - auto remove_Steiner_vertex_from_all_cdt_2(Vertex_handle v, - New_CDT_2_face_handles_output_iterator new_face_handles_out) - { - const auto type = v->ccdt_3_data().vertex_type(); - if(type == CDT_3_vertex_type::STEINER_IN_FACE) { - const auto face_id = v->ccdt_3_data().face_index(); - new_face_handles_out = remove_Steiner_vertex_from_cdt_2(v, type, face_id, new_face_handles_out); - } - else if (type == CDT_3_vertex_type::STEINER_ON_EDGE) { - const auto c_id = v->ccdt_3_data().constrained_polyline_id(*this); - for(const auto& [_, face_id] : CGAL::make_range(this->incident_faces_to_polyline.equal_range(c_id))) { - new_face_handles_out = remove_Steiner_vertex_from_cdt_2(v, type, face_id, new_face_handles_out); - } - } - return new_face_handles_out; - } - void add_bbox_points() { const auto bbox = CGAL::bbox_3(this->points_begin(), this->points_end(), this->geom_traits()); double d_x = bbox.xmax() - bbox.xmin(); From 451beba1f30faf1e8be0d7bbce5715156b40347d Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 6 May 2026 17:05:39 +0200 Subject: [PATCH 048/151] rework the parsing of command line options --- .../cdt_3_from_off.cpp | 190 ++++++++++-------- 1 file changed, 103 insertions(+), 87 deletions(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index c604b31cd071..a8e6f9a58942 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -220,94 +220,110 @@ CDT_options::CDT_options(int argc, char* argv[]) { return std::string(*++it); }; std::string_view arg = *it; - if(arg == "--merge-facets"sv) { - merge_facets = true; - } else if(arg == "--no-merge-facets"sv) { - merge_facets = false; - } else if(arg == "--reject-self-intersections"sv) { - reject_self_intersections = true; - } else if(arg == "--no-repair"sv) { - repair_mesh = false; - } else if(arg == "--read-mesh-with-operator"sv) { - read_mesh_with_operator = true; - } else if(arg == "--merge-facets-old"sv) { - merge_facets = true; - merge_facets_old_method = true; - } else if(arg == "--bisect"sv) { - bisect_failures = true; - } else if(arg == "--dump-patches-after-merge"sv) { - dump_patches_after_merge_filename = get_next_arg_or_error_out(); - } else if(arg == "--dump-patches-borders-prefix"sv) { - dump_patches_borders_prefix = get_next_arg_or_error_out(); - } else if(arg == "--dump-surface-mesh-after-merge"sv) { - dump_surface_mesh_after_merge_filename = get_next_arg_or_error_out(); - } else if(arg == "--dump-after-conforming"sv) { - dump_after_conforming_filename = get_next_arg_or_error_out(); - } else if(arg == "--vertex-vertex-epsilon"sv) { - vertex_vertex_epsilon = std::stod(get_next_arg_or_error_out()); - } else if(arg == "--segment-vertex-epsilon"sv) { - segment_vertex_epsilon = std::stod(get_next_arg_or_error_out()); - } else if(arg == "--coplanar-polygon-max-angle"sv) { - coplanar_polygon_max_angle = std::stod(get_next_arg_or_error_out()); - } else if(arg == "--coplanar-polygon-max-distance"sv) { - coplanar_polygon_max_distance = std::stod(get_next_arg_or_error_out()); - } else if(arg == "--quiet"sv) { - quiet = true; - } else if(arg == "--no-is-valid"sv) { - call_is_valid = false; - } else if(arg == "--debug-Steiner-points"sv) { - debug_Steiner_points = true; - } else if(arg == "--debug-Steiner-points-construction"sv) { - debug_Steiner_points_construction = true; - } else if(arg == "--debug-move-Steiner-vertices-to-the-volume"sv) { - debug_move_Steiner_vertices = true; - } else if(arg == "--debug-input-faces"sv) { - debug_input_faces = true; - } else if(arg == "--debug-missing-regions"sv) { - debug_missing_regions = true; - } else if(arg == "--debug-regions"sv) { - debug_regions = true; - } else if(arg == "--debug_copy_triangulation_into_hole"sv) { - debug_copy_triangulation_into_hole = true; - } else if(arg == "--debug-validity"sv) { - debug_validity = true; - } else if(arg == "--debug-finite-edges-map"sv) { - debug_finite_edges_map = true; - } else if(arg == "--debug-subconstraints-to-conform"sv) { - debug_subconstraints_to_conform = true; - } else if(arg == "--debug-verbose-special-cases"sv) { - debug_verbose_special_cases = true; - } else if(arg == "--debug-encroaching-vertices"sv) { - debug_encroaching_vertices = true; - } else if(arg == "--debug-conforming-validation"sv) { - debug_conforming_validation = true; - } else if(arg == "--debug-constraint-hierarchy"sv) { - debug_constraint_hierarchy = true; - } else if(arg == "--debug-geometric-errors"sv) { - debug_geometric_errors = true; - } else if(arg == "--debug-polygon-insertion"sv) { - debug_polygon_insertion = true; - } else if(arg == "--debug-restore-faces"sv) { - debug_restore_faces = true; - } else if(arg == "--use-finite-edges-map"sv) { - use_finite_edges_map = true; - } else if(arg == "--no-move-Steiner-vertices-to-the-volume"sv) { - move_Steiner_vertices_to_the_volume = false; - } else if(arg == "--move-Steiner-vertices-to-the-volume"sv) { - move_Steiner_vertices_to_the_volume = true; - } else if(arg == "--no-use-epeck-for-normals"sv) { - use_epeck_for_normals = false; - } else if(arg == "--no-use-epeck-for-Steiner-points"sv) { - use_epeck_for_Steiner_points = false; - } else if(arg == "--use-epeck-for-normals"sv) { - use_epeck_for_normals = true; - } else if(arg == "--use-epeck-for-Steiner-points"sv) { - use_epeck_for_Steiner_points = true; - } else if(arg == "--verbose"sv || arg == "-V"sv) { + bool enable = true; + bool arg_is_a_long_option = false; + if(arg.starts_with("--no-")) { + enable = false; + arg_is_a_long_option = true; + arg.remove_prefix(5); + } else if(arg.starts_with("--")) { + arg_is_a_long_option = true; + arg.remove_prefix(2); + } + if(arg_is_a_long_option) { + if(arg == "merge-facets"sv) { + merge_facets = enable; + } else if(arg == "no-merge-facets"sv) { + merge_facets = false; + } else if(arg == "reject-self-intersections"sv) { + reject_self_intersections = enable; + } else if(arg == "no-repair"sv) { + repair_mesh = false; + } else if(arg == "read-mesh-with-operator"sv) { + read_mesh_with_operator = enable; + } else if(arg == "merge-facets-old"sv) { + merge_facets = enable; + merge_facets_old_method = enable; + } else if(arg == "bisect"sv) { + bisect_failures = enable; + } else if(arg == "dump-patches-after-merge"sv) { + dump_patches_after_merge_filename = get_next_arg_or_error_out(); + } else if(arg == "dump-patches-borders-prefix"sv) { + dump_patches_borders_prefix = get_next_arg_or_error_out(); + } else if(arg == "dump-surface-mesh-after-merge"sv) { + dump_surface_mesh_after_merge_filename = get_next_arg_or_error_out(); + } else if(arg == "dump-after-conforming"sv) { + dump_after_conforming_filename = get_next_arg_or_error_out(); + } else if(arg == "vertex-vertex-epsilon"sv) { + vertex_vertex_epsilon = std::stod(get_next_arg_or_error_out()); + } else if(arg == "segment-vertex-epsilon"sv) { + segment_vertex_epsilon = std::stod(get_next_arg_or_error_out()); + } else if(arg == "coplanar-polygon-max-angle"sv) { + coplanar_polygon_max_angle = std::stod(get_next_arg_or_error_out()); + } else if(arg == "coplanar-polygon-max-distance"sv) { + coplanar_polygon_max_distance = std::stod(get_next_arg_or_error_out()); + } else if(arg == "quiet"sv) { + quiet = enable; + } else if(arg == "no-is-valid"sv) { + call_is_valid = false; + } else if(arg == "debug-Steiner-points"sv) { + debug_Steiner_points = enable; + } else if(arg == "debug-Steiner-points-construction"sv) { + debug_Steiner_points_construction = enable; + } else if(arg == "debug-move-Steiner-vertices-to-the-volume"sv) { + debug_move_Steiner_vertices = enable; + } else if(arg == "debug-input-faces"sv) { + debug_input_faces = enable; + } else if(arg == "debug-missing-regions"sv) { + debug_missing_regions = enable; + } else if(arg == "debug-regions"sv) { + debug_regions = enable; + } else if(arg == "debug_copy_triangulation_into_hole"sv) { + debug_copy_triangulation_into_hole = enable; + } else if(arg == "debug-validity"sv) { + debug_validity = enable; + } else if(arg == "debug-finite-edges-map"sv) { + debug_finite_edges_map = enable; + } else if(arg == "debug-subconstraints-to-conform"sv) { + debug_subconstraints_to_conform = enable; + } else if(arg == "debug-verbose-special-cases"sv) { + debug_verbose_special_cases = enable; + } else if(arg == "debug-encroaching-vertices"sv) { + debug_encroaching_vertices = enable; + } else if(arg == "debug-conforming-validation"sv) { + debug_conforming_validation = enable; + } else if(arg == "debug-constraint-hierarchy"sv) { + debug_constraint_hierarchy = enable; + } else if(arg == "debug-geometric-errors"sv) { + debug_geometric_errors = enable; + } else if(arg == "debug-polygon-insertion"sv) { + debug_polygon_insertion = enable; + } else if(arg == "debug-restore-faces"sv) { + debug_restore_faces = enable; + } else if(arg == "use-finite-edges-map"sv) { + use_finite_edges_map = enable; + } else if(arg == "no-move-Steiner-vertices-to-the-volume"sv) { + move_Steiner_vertices_to_the_volume = false; + } else if(arg == "move-Steiner-vertices-to-the-volume"sv) { + move_Steiner_vertices_to_the_volume = enable; + } else if(arg == "no-use-epeck-for-normals"sv) { + use_epeck_for_normals = false; + } else if(arg == "no-use-epeck-for-Steiner-points"sv) { + use_epeck_for_Steiner_points = false; + } else if(arg == "use-epeck-for-normals"sv) { + use_epeck_for_normals = enable; + } else if(arg == "use-epeck-for-Steiner-points"sv) { + use_epeck_for_Steiner_points = enable; + } else if(arg == "verbose"sv || arg == "-V"sv) { + ++verbose_level; + } else if(arg == "help"sv || arg == "-h"sv) { + need_help = enable; + } + } else if(arg == "-V"sv) { ++verbose_level; - } else if(arg == "--help"sv || arg == "-h"sv) { - need_help = true; - } else if(arg[0] == '-') { + } else if(arg == "-h"sv) { + need_help = true; + } else if(arg.starts_with('-')) { error("unknown option "sv, arg); } else { switch(positional) { From 2f7d64a5951d6b533579bba6ad88223e2e73c615 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 7 May 2026 09:40:59 +0200 Subject: [PATCH 049/151] remove one trailing space --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index a240a24449c4..ebf6a9cca533 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -590,7 +590,7 @@ struct Output_rep, With_point_and_info_t std::ostream& operator()(std::ostream& out) const { auto v_ptr = this->it.operator->(); - if(v_ptr == nullptr) + if(v_ptr == nullptr) return out << "nullptr"; if(Time_stamper::time_stamp(v_ptr) == 0) return out << "# = infinite_vertex()"; From f8986fd6b83c609d7244cae2fd0e6ff9703e7172 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 11 May 2026 15:08:08 +0200 Subject: [PATCH 050/151] modify the debug output --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 97e2d3df943b..c4e7618227d7 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2049,10 +2049,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(inserted); if(this->debug().move_Steiner_vertices()) { auto vertices = tr().vertices(facet_3d); - std::cerr << cdt_3_format("in the star restricted to the polygon, 3D facet\n" + std::cerr << cdt_3_format("for the hole edge ({}, {}), the 3D facet is\n" " {}\n" " {}\n" " {}\n", + display_vert(vertex_3d(fh_2d->vertex(cdt_2.cw(index)))), + display_vert(vertex_3d(fh_2d->vertex(cdt_2.ccw(index)))), display_vert(vertices[0]), display_vert(vertices[1]), display_vert(vertices[2])); } } From c07f9b48d8800e848ddb4d1a1afce7a8cdeaf7a6 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 11 May 2026 15:08:31 +0200 Subject: [PATCH 051/151] bug fix (for `bug3.off`) --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index c4e7618227d7..7dff331f1b85 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1900,9 +1900,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // else CGAL_assertion(new_front_cell->neighbor(i) == Cell_handle{}); // facet (new_front_cell, i) is no yet connected const bool neighbor_2d_is_in_the_star = neighbor_2d->info().is_in_region; + const auto mirror_index = cdt_2.cw(neighbor_2d->index(fh_2d->vertex(cdt_2.cw(i)))); const auto [neighbor_front_cell_3d, neighbor_front_index_3d] = neighbor_2d_is_in_the_star ? neighbor_2d->info().facet_3d - : map_from_hole_edge_to_facet_3d.at({neighbor_2d, neighbor_2d->index(fh_2d)}); + : map_from_hole_edge_to_facet_3d.at({neighbor_2d, mirror_index}); CGAL_assertion(neighbor_front_cell_3d != Cell_handle{}); const auto neighbor_back_cell_3d = neighbor_front_cell_3d->neighbor(neighbor_front_index_3d); From b53a704ec4d76f84becc6030001ef1a1464a56bf Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 11 May 2026 15:14:37 +0200 Subject: [PATCH 052/151] add bug test cases and ctest --- .../CMakeLists.txt | 3 ++ .../data/bug-fixed.off | 32 +++++++++++++++++++ .../data/bug3-fixed.off | 16 ++++++++++ .../data/bug4-fixed.off | 27 ++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-fixed.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug3-fixed.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug4-fixed.off diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index 78fd80e29d5e..608491476ace 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -95,6 +95,9 @@ function(CGAL_add_cdt3_from_local_off_test data_name) CGAL_add_cdt3_from_off_test_aux(${data_name} ${CMAKE_CURRENT_SOURCE_DIR}/data ${ARGN}) endfunction() +CGAL_add_cdt3_from_local_off_test(bug-fixed --move-Steiner-vertices-to-the-volume) +CGAL_add_cdt3_from_local_off_test(bug3-fixed --move-Steiner-vertices-to-the-volume) +CGAL_add_cdt3_from_local_off_test(bug4-fixed --move-Steiner-vertices-to-the-volume) CGAL_add_cdt3_from_local_off_test(cheese18) CGAL_add_cdt3_from_local_off_test(cheese23) CGAL_add_cdt3_from_local_off_test(cheese28) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-fixed.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-fixed.off new file mode 100644 index 000000000000..12ce52dd9a52 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-fixed.off @@ -0,0 +1,32 @@ +OFF +23 5 0 + +10 10 10 +-6.0240600000000004 -10.012 4.0160400000000003 +6.0144299999999999 -2.0024000000000002 4.00962 +-6.0072000000000001 -2.0024000000000002 4.00962 +-6.0240900000000002 -10.0321 -10.0321 +-6.0192600000000001 -6.0192600000000001 10.0321 +-6.0240900000000002 -6.0240900000000002 -10.0321 +-6.0192600000000001 -6.0192600000000001 -6.0192600000000001 +-6.0240900000000002 -6.0192699999999997 -10.0321 +10.019399999999999 -2.00387 0.33785900000000002 +10.0219 -0.92307300000000003 -0.92307300000000003 +-6.0240499999999999 -9.9999400000000005 10.0321 +6.0130600000000003 -0.86255199999999999 4.0087099999999998 +6.0144299999999999 -2.0018699999999998 5.3379099999999999 +-6.0130499999999998 -0.85634999999999994 4.0087000000000002 +-6.01823 -5.1587800000000001 4.0121500000000001 +-6.0072000000000001 -5.1587699999999996 4.0121500000000001 +-6.0192600000000001 -6.0192600000000001 4.0128399999999997 +-6.0116300000000003 -2.0038800000000001 0.33143699999999998 +10.0093 -2.0018699999999998 5.3355100000000002 +10 6 10 +10.013999999999999 0 2.9999899999999999 +-6.0192600000000001 -6.0192600000000001 5.0160499999999999 +7 1 17 22 15 14 5 11 +4 4 6 8 7 +6 15 16 3 2 12 14 +6 18 9 19 13 2 3 +6 20 21 19 9 10 0 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug3-fixed.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug3-fixed.off new file mode 100644 index 000000000000..cd8ee5797e79 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug3-fixed.off @@ -0,0 +1,16 @@ +OFF +8 4 0 + +0.41289999999999999 0.15625 -0.067400000000000002 +0.44080000000000003 0.14374999999999999 -0.065199999999999994 +0.40029999999999999 0.17394999999999999 -0.070499999999999993 +0.42680000000000001 0.16245000000000001 -0.068500000000000005 +0.40970000000000001 0.18395 -0.072300000000000003 +0.43359999999999999 0.17535000000000001 -0.070800000000000002 +0.43759999999999999 0.19075 -0.073499999999999996 +0.46029999999999999 0.18435000000000001 -0.072400000000000006 +3 0 3 1 +3 2 4 3 +3 3 4 5 +3 5 6 7 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug4-fixed.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug4-fixed.off new file mode 100644 index 000000000000..44dbc9146ab1 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug4-fixed.off @@ -0,0 +1,27 @@ +OFF +16 7 0 + +185.80154797792815 6.314845734395881 -138.37941828626168 +185.87791334370158 3.8848603944873195 -138.37275344014969 +185.8220846343909 -5.6613587297849071 -136.31085013313518 +185.8410656525223 5.0573729395164362 -138.67501807936665 +185.80721796422316 6.1344238450376913 -137.33082227481412 +185.83031684350982 5.3994055881827281 -137.3485146794398 +185.8187674038665 5.7669147166102093 -137.33966847712696 +185.83252986996206 -5.3289859446530263 -137.33266393727405 +185.84186628315314 5.0318964597552469 -137.35736088175264 +185.83609156333148 5.2156510239689879 -137.35293778059622 +185.86496516243977 4.2968782029002828 -137.37505328637826 +185.80160282426402 6.3131004956894499 -137.32714866699001 +185.81299268404484 5.9506692808239503 -137.33524537597054 +185.82558201217887 -5.5500703640873645 -137.33569278130571 +185.82454212368816 5.5831601523964682 -137.34409157828338 +185.85919044261811 4.4806327671140238 -137.37063018522184 +3 0 11 4 +3 0 12 6 +3 2 13 7 +3 14 5 3 +3 3 9 8 +3 10 1 3 +3 3 15 10 + From 78c510b9c6debcc764a8614df97d4f9653d35e74 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 12 May 2026 18:22:31 +0200 Subject: [PATCH 053/151] Basic_indenting_stream_guard: do nothing if the string is empty --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 6 ++---- Stream_support/include/CGAL/IO/Indenting_ostream.h | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 7dff331f1b85..456108480477 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2230,11 +2230,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } // end of `remove_Steiner_vertex_from_cdt_2` bool move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { - std::optional indent_guards; if(this->debug().move_Steiner_vertices()) { std::cerr << "Moving Steiner vertex " << display_vert(v) << " to the volume\n"; - indent_guards.emplace(IO::make_indenting_guards(" |")); } + auto indent_guards = CGAL::IO::make_indenting_guards(this->debug().move_Steiner_vertices() ? "| " : ""); const auto incident_constrained_facet_opt = find_in_incident_facets(v, [this](Facet f) { return is_facet_constrained(f); }); @@ -5586,7 +5585,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; auto test_cell = [&](Cell_handle cell) { - std::optional indent_guards; if constexpr(cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format("test_cell #{}\n {}\n {}\n {}\n {}\n", cell->time_stamp(), @@ -5594,8 +5592,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D IO::oformat(cell->vertex(1), with_point_and_info), IO::oformat(cell->vertex(2), with_point_and_info), IO::oformat(cell->vertex(3), with_point_and_info)); - indent_guards.emplace(CGAL::IO::make_indenting_guards("| ")); } + auto indent_guards = CGAL::IO::make_indenting_guards(this->debug().regions() ? "| " : ""); bool does_intersect = false; for(int i = 0; i < 4; ++i) { const auto v0 = cell->vertex(i); diff --git a/Stream_support/include/CGAL/IO/Indenting_ostream.h b/Stream_support/include/CGAL/IO/Indenting_ostream.h index c4afdc3ca79e..59a01ff325f9 100644 --- a/Stream_support/include/CGAL/IO/Indenting_ostream.h +++ b/Stream_support/include/CGAL/IO/Indenting_ostream.h @@ -237,6 +237,10 @@ class Basic_indenting_stream_guard , original_buf_(stream.rdbuf()) , indenting_buf_(*original_buf_, indent_string) { + if(indent_string.empty()) { + // No indentation, just use the original buffer + return; + } if(auto old_buf = dynamic_cast(original_buf_)) { original_indent_string_ = old_buf->indent_string(); old_buf->set_indent_string(original_indent_string_ + indent_string); From 30faf583cc0b98d2dce840dde70685a89c4bf94f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 12 May 2026 18:25:15 +0200 Subject: [PATCH 054/151] WIP: fix the sub-stars (with a retriangulation) --- ...ing_constrained_Delaunay_triangulation_3.h | 133 +++++++++++++----- 1 file changed, 98 insertions(+), 35 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 456108480477..3463963d7de9 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1274,6 +1274,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Geom_traits = typename T_3::Geom_traits; using Point_3 = typename T_3::Point; using Segment_3 = typename Geom_traits::Segment_3; + using Triangle_3 = typename Geom_traits::Triangle_3; using Vector_3 = typename Geom_traits::Vector_3; using Locate_type = typename T_3::Locate_type; using size_type = typename T_3::size_type; @@ -1486,21 +1487,24 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return std::nullopt; } - template + template CGAL::Surface_mesh construct_star_component_mesh(std::size_t component_index, - const Offsets& component_offsets, - const Facets& facets) const + const Components& components, + const Map& map_from_incident_constraint_face_id_to_new_triangles) const { CGAL::Surface_mesh component_mesh; std::vector points; std::vector> triangles; std::size_t vertex_index = 0; - for(std::size_t facet_id = component_offsets[component_index - 1], - end = component_offsets[component_index]; + for(std::size_t facet_id = components.component_offsets[component_index - 1], + end = components.component_offsets[component_index]; facet_id < end; ++facet_id) { - const auto& [cell, facet_index, _] = facets[facet_id]; + const auto& [cell, facet_index, type] = components.facets[facet_id]; + if(type == Star_components_facets::Type_of_facet::INCIDENT_TO_V) { + continue; + } if(tr().is_infinite(Facet(cell, facet_index))) { continue; } @@ -1512,15 +1516,20 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D triangles.push_back({vertex_index, vertex_index + 2, vertex_index + 1}); vertex_index += 3; } // end for each facet in the component + const auto& incident_face_ids = components.component_incident_constraint_face_ids[component_index - 1]; + for(auto face_id : incident_face_ids) { + const auto& new_triangles = map_from_incident_constraint_face_id_to_new_triangles.at(face_id); + for(const auto& triangle : new_triangles) { + points.push_back(triangle[0]); + points.push_back(triangle[1]); + points.push_back(triangle[2]); + triangles.push_back({vertex_index, vertex_index + 1, vertex_index + 2}); + vertex_index += 3; + } + } CGAL::Polygon_mesh_processing::merge_duplicate_points_in_polygon_soup(points, triangles); - - std::vector::Face_index>> - triangle_index_to_face_descriptor; - CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh( - points, - triangles, - component_mesh, - CGAL::parameters::polygon_to_face_output_iterator(std::back_inserter(triangle_index_to_face_descriptor))); + CGAL::Polygon_mesh_processing::orient_polygon_soup(points, triangles); + CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, triangles, component_mesh); return component_mesh; } @@ -1528,18 +1537,19 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return CGAL::Polygon_mesh_processing::kernel_point(component_mesh, parameters::allow_open_input(true)); } - template + template CGAL::Surface_mesh - construct_link_mesh_for_debug(const Offsets& component_offsets, - const Facets& facets) const + construct_link_mesh_for_debug(const Components& components, + const Map& map_from_incident_constraint_face_id_to_new_triangles) const { using Mesh = CGAL::Surface_mesh; using face_descriptor = typename Mesh::Face_index; Mesh link_mesh; auto [patch_id_map, _] = link_mesh.template add_property_map("f:patch_id", 0); - for(std::size_t component_index = 1; component_index < component_offsets.size(); ++component_index) { - Mesh component_mesh = construct_star_component_mesh(component_index, component_offsets, facets); + for(std::size_t component_index = 1; component_index < components.component_offsets.size(); ++component_index) { + Mesh component_mesh = construct_star_component_mesh(component_index, components, + map_from_incident_constraint_face_id_to_new_triangles); std::vector new_faces; auto out_it = boost::make_function_output_iterator( @@ -1552,9 +1562,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return link_mesh; } - template - void dump_link_mesh_of_vertex_to_ply(Vertex_handle v, const Offsets& component_offsets, const Facets& facets) const { - const auto link_mesh = construct_link_mesh_for_debug(component_offsets, facets); + template + void dump_link_mesh_of_vertex_to_ply(Vertex_handle v, + const Components& components, + const Map& map_from_incident_constraint_face_id_to_new_triangles) const { + const auto link_mesh = + construct_link_mesh_for_debug(components, map_from_incident_constraint_face_id_to_new_triangles); std::stringstream filename; filename << "dump_link_mesh_vertex" << IO::oformat(v, With_offset_tag{}) << ".ply"; @@ -1759,7 +1772,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return out; } // end collect_star_components_facets(..) - template + enum Run_mode { NORMAL_RUN, DRY_RUN }; + + template std::pair fill_cdt_2_hole_with_delaunay(CDT_2& cdt_2, Face_index face_index, @@ -1767,7 +1782,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::list& hole_boundary, const Map_from_hole_edge_to_facet_3d& map_from_hole_edge_to_facet_3d, Face_is_outside_the_face is_outside_the_face, - New_CDT_2_face_handles_output_iterator new_face_handles_out) + New_CDT_2_face_handles_output_iterator new_face_handles_out, + Run_mode run_mode = NORMAL_RUN) { const bool is_a_half_hole = vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; if(this->debug().move_Steiner_vertices()) { @@ -1985,7 +2001,16 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D *new_face_handles_out++ = fh_2d; }; // end of `action_for_each_new_2d_face` - CGAL::fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator(action_for_each_new_2d_face)); + switch(run_mode) { + case NORMAL_RUN: + CGAL::fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator(action_for_each_new_2d_face)); + break; + case DRY_RUN: + CGAL::fill_hole_delaunay(cdt_2, hole_boundary, new_face_handles_out); + break; + default: + CGAL_unreachable(); + } return {new_face_handles_out, half_hole_front_facet}; } @@ -1994,12 +2019,20 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D remove_Steiner_vertex_from_cdt_2(Vertex_handle v, CDT_3_vertex_type vertex_type, Face_index face_id, - New_CDT_2_face_handles_output_iterator new_face_handles_out) + New_CDT_2_face_handles_output_iterator new_face_handles_out, + Run_mode run_mode = NORMAL_RUN) { std::tuple result{new_face_handles_out, {}, {}}; auto& [new_face_handles_out_it, half_hole_front_facet, second_half_hole_front_facet] = result; - auto& mutable_cdt_2 = non_const_face_cdt_2(face_id); - const auto& cdt_2 = mutable_cdt_2; + + using CDT_2_base = typename CDT_2::Triangulation; + auto& original_mutable_cdt_2 = static_cast(non_const_face_cdt_2(face_id)); + std::unique_ptr cdt_2_copy_ptr{}; + if(run_mode == DRY_RUN) { + cdt_2_copy_ptr.reset(new CDT_2_base(original_mutable_cdt_2)); + } + auto& mutable_cdt_2 = (run_mode == DRY_RUN) ? *cdt_2_copy_ptr : original_mutable_cdt_2; + const auto & cdt_2 = mutable_cdt_2; typename CDT_2::Locate_type lt; int i; @@ -2089,7 +2122,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::for_each(faces_to_delete.begin(), faces_to_delete.end(), [&mutable_cdt_2](CDT_2_face_handle f) { mutable_cdt_2.delete_face(f); }); return fill_cdt_2_hole_with_delaunay(mutable_cdt_2, face_id, vertex_type, hole_boundary, hole_boundary_map, - is_outside_the_face, new_face_handles_out); + is_outside_the_face, new_face_handles_out, run_mode); }; auto set_constrained = [&](CDT_2_edge e, bool is_constrained) { @@ -2170,7 +2203,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto [out_it, facet_side_1] = bore_hole_and_fill_with_delaunay( vertex_type, hole_side_1, hole_side_1_faces_to_delete, side_1_hole_is_outside_the_face); new_face_handles_out = out_it; - CGAL_assertion(side_1_hole_is_outside_the_face == OUTSIDE || facet_side_1.first != Cell_handle{}); + CGAL_assertion(run_mode == DRY_RUN || + side_1_hole_is_outside_the_face == OUTSIDE || facet_side_1.first != Cell_handle{}); // - now the edge opposite to `(side_2_last_face, side_2_last_edge_index)` is on the border of the hole // `hole_side_2`, so we can start sewing from there @@ -2179,7 +2213,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto [out_it_2, facet_side_2] = bore_hole_and_fill_with_delaunay( vertex_type, hole_side_2, hole_side_2_faces_to_delete, side_2_hole_is_outside_the_face); new_face_handles_out = out_it_2; - CGAL_assertion(side_2_hole_is_outside_the_face == OUTSIDE || facet_side_2.first != Cell_handle{}); + CGAL_assertion(run_mode == DRY_RUN || + side_2_hole_is_outside_the_face == OUTSIDE || facet_side_2.first != Cell_handle{}); half_hole_front_facet = facet_side_1; second_half_hole_front_facet = facet_side_2; @@ -2264,13 +2299,42 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } return false; } + const auto v_type = vertex_type(v); + + std::map> map_from_incident_constraint_face_id_to_new_triangles; + + auto dry_run_output_iterator = [&](Face_index face_id) { + auto* triangles_for_this_face_ptr = &map_from_incident_constraint_face_id_to_new_triangles[face_id]; + std::function func = [&,triangles_for_this_face_ptr](CDT_2_face_handle fh_2d) { + const auto v1 = vertex_3d(fh_2d->vertex(0)); + const auto v2 = vertex_3d(fh_2d->vertex(1)); + const auto v3 = vertex_3d(fh_2d->vertex(2)); + triangles_for_this_face_ptr->emplace_back(v1->point(), v2->point(), v3->point()); + }; + return boost::make_function_output_iterator(func); + }; + + if(v_type == CDT_3_vertex_type::STEINER_IN_FACE) { + CGAL_assertion(star_components.component_incident_constraint_face_ids.size() == 2); + [[maybe_unused]] const auto& [face_id, _] = star_components.face_ids_between_consecutive_components.front(); + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, dry_run_output_iterator(face_id), DRY_RUN); + } else { + CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); + for(std::size_t component_index = 0, end = star_components.face_ids_between_consecutive_components.size(); + component_index < end; ++component_index) + { + const auto& [face_id, _] = star_components.face_ids_between_consecutive_components[component_index]; + + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, dry_run_output_iterator(face_id), DRY_RUN); + } + } const auto components_central_points = std::invoke([&]() { std::optional> central_points{std::in_place}; central_points->reserve(nb_of_star_components); for(std::size_t component_index = 1; component_index <= nb_of_star_components; ++component_index) { - auto component_mesh = - construct_star_component_mesh(component_index, star_components.component_offsets, star_components.facets); + auto component_mesh = construct_star_component_mesh(component_index, star_components, + map_from_incident_constraint_face_id_to_new_triangles); auto opt_kernel_center_point = compute_center_point_if_possible(component_mesh); if(this->debug().move_Steiner_vertices()) { std::cerr << " - incident constraint faces ids of component #" << component_index << ": {" @@ -2300,7 +2364,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }); // end compute central points for each component if(this->debug().move_Steiner_vertices()) { - dump_link_mesh_of_vertex_to_ply(v, star_components.component_offsets, star_components.facets); + dump_link_mesh_of_vertex_to_ply(v, star_components, map_from_incident_constraint_face_id_to_new_triangles); } if(!components_central_points.has_value()) { @@ -2356,7 +2420,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::size_t nb_of_new_2d_faces = 0; CGAL::Counting_output_iterator output_iterator{&nb_of_new_2d_faces}; - const auto v_type = vertex_type(v); if(v_type == CDT_3_vertex_type::STEINER_IN_FACE) { CGAL_assertion(star_components.component_incident_constraint_face_ids.size() == 2); const auto& [face_id, orientation] = star_components.face_ids_between_consecutive_components.front(); From 21e5ebf154c6267d14236f5eb6025065320da2a5 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 13 May 2026 11:18:07 +0200 Subject: [PATCH 055/151] the component can no longer be open Now that I have added an extended bbox, Steiner vertices cannot be on the convex hull. --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 3463963d7de9..97e7c1b2aed9 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1534,7 +1534,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } std::optional compute_center_point_if_possible(const CGAL::Surface_mesh& component_mesh) const { - return CGAL::Polygon_mesh_processing::kernel_point(component_mesh, parameters::allow_open_input(true)); + return CGAL::Polygon_mesh_processing::kernel_point(component_mesh); } template From eddd278797f2f826d5b1393b92cd56d6eb708f97 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 13 May 2026 11:18:31 +0200 Subject: [PATCH 056/151] debug: dump component meshes --- .../Conforming_constrained_Delaunay_triangulation_3.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 97e7c1b2aed9..e3ead6865901 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2335,6 +2335,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(std::size_t component_index = 1; component_index <= nb_of_star_components; ++component_index) { auto component_mesh = construct_star_component_mesh(component_index, star_components, map_from_incident_constraint_face_id_to_new_triangles); + if(this->debug().move_Steiner_vertices()) { + std::ostringstream ss; + ss << "dump_vertex_" << IO::oformat(v) << "_component_" << component_index << ".off"; + const std::string filename = ss.str(); + std::ofstream out(filename); + out.precision(17); + out << component_mesh; + } auto opt_kernel_center_point = compute_center_point_if_possible(component_mesh); if(this->debug().move_Steiner_vertices()) { std::cerr << " - incident constraint faces ids of component #" << component_index << ": {" From df838f917fd51732dfc410188cd226fd39348c63 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 26 May 2026 15:05:05 +0200 Subject: [PATCH 057/151] fix debug messages --- ...Conforming_constrained_Delaunay_triangulation_3.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index e3ead6865901..0d4cb5a5aefc 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2337,7 +2337,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D map_from_incident_constraint_face_id_to_new_triangles); if(this->debug().move_Steiner_vertices()) { std::ostringstream ss; - ss << "dump_vertex_" << IO::oformat(v) << "_component_" << component_index << ".off"; + ss << "dump_vertex_" << IO::oformat(v, with_offset) << "_component_" << component_index << ".off"; const std::string filename = ss.str(); std::ofstream out(filename); out.precision(17); @@ -3300,6 +3300,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } auto move_Steiner_vertices_to_the_volume() { + if(this->debug().move_Steiner_vertices()) { + std::cerr << "move_Steiner_vertices_to_the_volume()\n"; + dump_constrained_facets_to_off("dump_before_moving_steiner_vertices.off"); + } std::vector steiner_vertices; for(auto v : this->finite_vertex_handles()) { if(is_Steiner_vertex_on_surface(v)) steiner_vertices.push_back(v); @@ -5958,6 +5962,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D write_3d_triangulation_to_OFF(dump, *this); } + void dump_constrained_facets_to_off(std::string filename) const { + std::ofstream dump(filename); + dump.precision(17); + write_facets(dump, *this, this->constrained_facets()); + } + void dump_face_polygons( const std::vector>& vec_of_handles, CDT_3_signed_index polygon_constraint_id, From 428fb5649c93661cc5a23e4ceb6e2e96154185c6 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 26 May 2026 15:07:51 +0200 Subject: [PATCH 058/151] fix dependencies The code now uses ``. --- .../package_info/Constrained_triangulation_3/dependencies | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Constrained_triangulation_3/package_info/Constrained_triangulation_3/dependencies b/Constrained_triangulation_3/package_info/Constrained_triangulation_3/dependencies index 854e686ee58b..722c7f611507 100644 --- a/Constrained_triangulation_3/package_info/Constrained_triangulation_3/dependencies +++ b/Constrained_triangulation_3/package_info/Constrained_triangulation_3/dependencies @@ -1,3 +1,4 @@ +AABB_tree Algebraic_foundations Arithmetic_kernel BGL @@ -10,6 +11,7 @@ Constrained_triangulation_3 Distance_2 Distance_3 Filtered_kernel +Generator Hash_map Homogeneous_kernel Installation @@ -21,6 +23,7 @@ Kernel_d Mesh_3 Modular_arithmetic Number_types +PMP_Boolean_operations PMP_Mesh_repair PMP_Remeshing Polygon @@ -32,6 +35,7 @@ Random_numbers SMDS_3 STL_Extension Solver_interface +Spatial_searching Spatial_sorting Stream_support Surface_mesh From 645e36254a6faea84d47f399172287648513bb78 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 2 Jun 2026 13:54:41 +0200 Subject: [PATCH 059/151] new command line options --- .../include/CGAL/Conforming_Delaunay_triangulation_3.h | 4 ++++ .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 907d4af8ba9a..3957c7104089 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -98,6 +98,7 @@ struct Debug_options { display_statistics, use_epeck_for_normals, use_epeck_for_Steiner_points, + move_Steiner_vertices_allow_negative_tets, nb_of_flags }; @@ -157,6 +158,9 @@ struct Debug_options { bool move_Steiner_vertices() const { return get(Flag::debug_move_Steiner_vertices); } void move_Steiner_vertices(bool b) { get(Flag::debug_move_Steiner_vertices) = b; } + bool move_Steiner_vertices_allow_negative_tets() const { return get(Flag::move_Steiner_vertices_allow_negative_tets); } + void move_Steiner_vertices_allow_negative_tets(bool b) { get(Flag::move_Steiner_vertices_allow_negative_tets) = b; } + bool display_statistics() const { return get(Flag::display_statistics); } void display_statistics(bool b) { get(Flag::display_statistics) = b; } diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 87884b02de47..1a5d44a9bbfd 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -141,6 +141,7 @@ Usage: cdt_3_from_off [options] input.off output.off --use-finite-edges-map: use a hash map for finite edges (default: false) --move-Steiner-vertices-to-the-volume/--no-move-Steiner-vertices-to-the-volume: move Steiner vertices to the volume (default: true) + --allow-moving-Steiner-vertices-to-create-negative-tets/--no-allow-moving-Steiner-vertices-to-create-negative-tets: allow moving Steiner vertices to create negative volume tetrahedra (default: false) --use-epeck-for-normals/--no-use-epeck-for-normals: use exact kernel for normal computations (default: false) --use-epeck-for-Steiner-points/--no-use-epeck-for-Steiner-points: use exact kernel for Steiner point computations (default: false) @@ -168,7 +169,7 @@ struct CDT_options bool read_mesh_with_operator = false; bool debug_Steiner_points = false; bool debug_Steiner_points_construction = false; - bool debug_move_Steiner_vertices = false; + bool debug_move_Steiner_vertices = true; bool debug_input_faces = false; bool debug_missing_regions = false; bool debug_regions = false; @@ -185,6 +186,7 @@ struct CDT_options bool debug_restore_faces = false; bool use_finite_edges_map = false; bool move_Steiner_vertices_to_the_volume = true; + bool allow_moving_Steiner_vertices_to_create_negative_tets = false; bool use_epeck_for_normals = false; bool use_epeck_for_Steiner_points = false; bool call_is_valid = true; @@ -298,10 +300,10 @@ CDT_options::CDT_options(int argc, char* argv[]) { debug_restore_faces = enable; } else if(arg == "use-finite-edges-map"sv) { use_finite_edges_map = enable; - } else if(arg == "no-move-Steiner-vertices-to-the-volume"sv) { - move_Steiner_vertices_to_the_volume = false; } else if(arg == "move-Steiner-vertices-to-the-volume"sv) { move_Steiner_vertices_to_the_volume = enable; + } else if(arg == "allow-moving-Steiner-vertices-to-create-negative-tets"sv) { + allow_moving_Steiner_vertices_to_create_negative_tets = enable; } else if(arg == "no-use-epeck-for-normals"sv) { use_epeck_for_normals = false; } else if(arg == "no-use-epeck-for-Steiner-points"sv) { @@ -352,6 +354,7 @@ CGAL::CDT_3::Debug_options cdt_debug_options(const CDT_options& options) { cdt_debug.encroaching_vertices(options.debug_encroaching_vertices); cdt_debug.conforming_validation(options.debug_conforming_validation); cdt_debug.move_Steiner_vertices(options.debug_move_Steiner_vertices); + cdt_debug.move_Steiner_vertices_allow_negative_tets(options.allow_moving_Steiner_vertices_to_create_negative_tets); cdt_debug.constraint_hierarchy(options.debug_constraint_hierarchy); cdt_debug.geometric_errors(options.debug_geometric_errors); cdt_debug.polygon_insertion(options.debug_polygon_insertion); From 262261e1f760bafbf4c43b9301baa31e67783e92 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 2 Jun 2026 13:57:37 +0200 Subject: [PATCH 060/151] add constrained facets to the medit output --- ...ing_constrained_Delaunay_triangulation_3.h | 8 ++- .../cdt_3_from_off.cpp | 72 +++++++++++++------ 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 0d4cb5a5aefc..6a0c816ceb40 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1201,6 +1201,10 @@ class Conforming_constrained_Delaunay_triangulation_3 // IO helpers for debugging bool write_missing_segments_file(std::ostream& out) { return cdt_impl.write_missing_segments_file(out); } void write_all_segments_file(std::ostream& out) { cdt_impl.write_all_segments_file(out); } + void dump_constrained_facets_to_off(std::string filename) const { cdt_impl.dump_constrained_facets_to_off(filename); } + void save_binary_file(std::string filename = "dump.binary.cgal") const { + cdt_impl.dump_triangulation(filename); + } // Post-processing helpers /// \brief Moves Steiner vertices on the boundary to the volume. @@ -5950,8 +5954,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D write_3d_triangulation_to_OFF(dump, tr); } - void dump_triangulation() const { - std::ofstream dump("dump.binary.cgal", std::ios::binary); + void dump_triangulation(std::string filename = "dump.binary.cgal") const { + std::ofstream dump(filename, std::ios::binary); CGAL::IO::save_binary_file(dump, *this); } diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 1a5d44a9bbfd..09b0f5ac5072 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -397,8 +398,9 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& "invalid state.\n"; return; } - auto dump_tets_to_medit = [](std::string fname, + auto dump_to_medit = [](std::string fname, const std::vector &points, + const std::vector> &indexed_triangles, const std::vector> &indexed_tetra, const std::vector &cell_ids) { @@ -408,19 +410,23 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& out << points.size() << "\n"; for (const K::Point_3& p : points) out << p << " 0\n"; - out << "Triangles\n0\nTetrahedra\n"; + out << "Triangles\n" << indexed_triangles.size() << "\n"; + for (const auto& tri : indexed_triangles) + out << tri[0]+1 << " " << tri[1]+1 << " " << tri[2]+1 << " " << tri[3] << "\n"; + out << "Tetrahedra\n"; out << indexed_tetra.size() << "\n"; for (std::size_t k=0;k cells_map; + std::unordered_map cells_map; for(auto ch : tr.all_cell_handles()) { cells_map[ch] = 1; @@ -445,6 +451,15 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& } } + const bool export_all_the_bbox = + std::all_of(cells_map.begin(), cells_map.end(), [](const auto& kv) { return kv.second == 0; }); + if (export_all_the_bbox) { + for(auto ch : tr.finite_cell_handles()) + { + cells_map[ch] = 1; + } + } + std::vector points(cdt.number_of_vertices()); std::size_t idx = 0; for(auto v: cdt.finite_vertex_handles()) { @@ -452,6 +467,30 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& v->set_time_stamp(++idx); points.at(idx - 1) = v->point(); } + std::vector> indexed_triangles; + std::size_t max_polygon_id = 0; + for(auto f : cdt.constrained_facets()) { + const auto verts = tr.vertices(f); + const auto polygon_id = static_cast(cdt.face_constraint_index(f)); + indexed_triangles.push_back({verts[0]->time_stamp() - 1, + verts[1]->time_stamp() - 1, + verts[2]->time_stamp() - 1, + polygon_id}); + if (polygon_id > max_polygon_id) { + max_polygon_id = polygon_id; + } + } + if(export_all_the_bbox) { + auto inf_v = tr.infinite_vertex(); + tr.incident_cells(inf_v, boost::make_function_output_iterator([&](Tr::Cell_handle ch) { + auto facet_index = ch->index(inf_v); + const auto verts = tr.vertices(Tr::Facet{ch, facet_index}); + indexed_triangles.push_back({verts[0]->time_stamp() - 1, + verts[1]->time_stamp() - 1, + verts[2]->time_stamp() - 1, + max_polygon_id + 1 /*a new polygon id for the bbox*/}); + })); + } std::vector> indexed_tetra; indexed_tetra.reserve(cdt.number_of_cells()); for(auto ch: cdt.finite_cell_handles()) { @@ -463,24 +502,11 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& } } std::vector cell_ids(indexed_tetra.size(), 1); - dump_tets_to_medit(options.output_filename + ".mesh", points, indexed_tetra, cell_ids); + dump_to_medit(options.output_filename + ".mesh", points, indexed_triangles, indexed_tetra, cell_ids); + cdt.dump_constrained_facets_to_off(options.output_filename); } { - std::ofstream dump(options.output_filename); - dump.precision(17); -#if CGAL_CDT_3_CAN_USE_CXX20_RANGES - cdt.write_facets(dump, cdt.triangulation(), std::views::filter(cdt.finite_facets(), [&](auto f) { - return cdt.is_facet_constrained(f); - })); -#else // If C++20 ranges are not available, we use boost::make_filter_iterator to filter the facets. - auto is_facet_constrained = [&](auto f) { return cdt.is_facet_constrained(f); }; - const auto& tr = cdt.triangulation(); - auto it_begin = tr.finite_facets_begin(); - auto it_end = tr.finite_facets_end(); - auto filtered_it_begin = boost::make_filter_iterator(is_facet_constrained,it_begin, it_end); - auto filtered_it_end = boost::make_filter_iterator(is_facet_constrained,it_end, it_end); - cdt.write_facets(dump, tr, CGAL::make_range(filtered_it_begin, filtered_it_end)); -#endif // CGAL_CDT_3_CAN_USE_CXX20_RANGES + cdt.save_binary_file(options.output_filename + ".binary.cgal"); } }; } From cd937e4a9d51388a3fac39b100e37b065e60f534 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 2 Jun 2026 14:23:22 +0200 Subject: [PATCH 061/151] WIP: allow negative tetrahedra --- ...ing_constrained_Delaunay_triangulation_3.h | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 6a0c816ceb40..b68ad1cf1138 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -130,6 +130,7 @@ #include #include #include +#include #include #include #include @@ -1789,8 +1790,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D New_CDT_2_face_handles_output_iterator new_face_handles_out, Run_mode run_mode = NORMAL_RUN) { + const bool debug = this->debug().move_Steiner_vertices() && run_mode == NORMAL_RUN; const bool is_a_half_hole = vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; - if(this->debug().move_Steiner_vertices()) { + if(debug) { std::cerr << cdt_3_format("Filling {}hole in CDT_2 with {} edges, is_outside_the_face = {}\n", is_a_half_hole ? "half " : "", hole_boundary.size(), enum_cast(is_outside_the_face)); @@ -1844,7 +1846,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const Vertex_handle v_front = front_cell->vertex(6 - front_index - front_cell->index(v0) - front_cell->index(v1)); const Vertex_handle v_back = back_cell->vertex(6 - back_index - back_cell->index(v0) - back_cell->index(v1)); - if(this->debug().move_Steiner_vertices()) { + if(debug) { std::cerr << cdt_3_format(" - first_2d_face has vertices ( {} ,\n" " {} ,\n" " {} )\n", @@ -1876,7 +1878,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto v2 = vertex_3d(fh_2d->vertex(1)); const auto v3 = vertex_3d(fh_2d->vertex(2)); - if(this->debug().move_Steiner_vertices()) { + if(debug) { std::cerr << cdt_3_format("- new CDT_2 face with 3D vertices ( {} ,\n" " {} ,\n" " {} )\n", @@ -1886,9 +1888,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } const auto new_front_cell = tr().tds().create_cell(v1, v2, v3, v_front); - CGAL_assertion(tr().orientation(v1->point(), v2->point(), v3->point(), v_front->point()) > 0); // DO NOT COMMIT (expansive assertion) + CGAL_assertion(this->debug().move_Steiner_vertices_allow_negative_tets() || + tr().orientation(v1->point(), v2->point(), v3->point(), v_front->point()) > 0); // DO NOT COMMIT (expansive assertion) const auto new_back_cell = tr().tds().create_cell(v1, v3, v2, v_back); - CGAL_assertion(tr().orientation(v1->point(), v3->point(), v2->point(), v_back->point()) > 0); // DO NOT COMMIT (expansive assertion) + CGAL_assertion(this->debug().move_Steiner_vertices_allow_negative_tets() || + tr().orientation(v1->point(), v3->point(), v2->point(), v_back->point()) > 0); // DO NOT COMMIT (expansive assertion) set_adjacency_between_3d_facets({new_front_cell, 3}, {new_back_cell, 3}); set_facet_constrained({new_front_cell, 3}, face_index, fh_2d); @@ -1902,7 +1906,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // this edge of `fh_2d` is dandling to be connected to the other part of the half hole CGAL_assertion(half_hole_front_facet.first == Cell_handle{}); // only one edge of the hole can be a half hole half_hole_front_facet = {new_front_cell, i}; - if(this->debug().move_Steiner_vertices()) { + if(debug) { auto new_front_cell_i_vertices = tr().vertices(Facet{new_front_cell, i}); std::cerr << cdt_3_format(" - the 3D facet (new_front_cell={}, i={}) is ON THE BORDER of the half hole:\n" " {}\n" @@ -1931,7 +1935,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto fh_index_in_neighbor_2d = neighbor_2d_is_in_the_star ? neighbor_2d->index(fh_2d) : neighbor_front_index_3d; - if(this->debug().move_Steiner_vertices()) { + if(debug) { std::cerr << cdt_3_format(" - the 2D face fh_2d->neighbor(i={}) IS{} in region. Its 3D vertices are:\n" " {}\n" " {}\n" @@ -1973,7 +1977,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D ? (3 - fh_index_in_neighbor_2d) % 3 : neighbor_back_cell_3d->index(neighbor_front_cell_3d); - if(this->debug().move_Steiner_vertices()) { + if(debug) { auto new_back_cell_vertices = tr().vertices(Facet{new_back_cell, back_cell_i}); std::cerr << cdt_3_format(" - the 3D facet (new_back_cell={}, back_cell_i={}) is\n" " {}\n" @@ -2026,6 +2030,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D New_CDT_2_face_handles_output_iterator new_face_handles_out, Run_mode run_mode = NORMAL_RUN) { + const bool debug = this->debug().move_Steiner_vertices() || run_mode == NORMAL_RUN; std::tuple result{new_face_handles_out, {}, {}}; auto& [new_face_handles_out_it, half_hole_front_facet, second_half_hole_front_facet] = result; @@ -2046,7 +2051,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto vh_2d = fh_2d->vertex(i); CGAL_assertion(vertex_3d(vh_2d) == v); - if(this->debug().move_Steiner_vertices()) { + if(debug) { using std::begin; using std::end; std::for_each(begin(cdt_2.all_face_handles()), end(cdt_2.all_face_handles()), [](CDT_2_face_handle fh) { @@ -2068,7 +2073,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_error_msg("Aborting due to invalid CDT_2"); } } - auto indent_guard = IO::make_indenting_guards(this->debug().move_Steiner_vertices() ? 4 : 0); + auto indent_guard = IO::make_indenting_guards(debug ? 4 : 0); auto map_from_hole_edges_to_incident_3d_facets = [&](const auto& hole_boundary_begin, const auto& hole_boundary_end, Face_is_outside_the_face is_outside_the_face) { @@ -2085,7 +2090,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(facet_3d.first != Cell_handle{}); [[maybe_unused]] auto [_, inserted] = map.insert({{fh_2d, index}, facet_3d}); CGAL_assertion(inserted); - if(this->debug().move_Steiner_vertices()) { + if(debug) { auto vertices = tr().vertices(facet_3d); std::cerr << cdt_3_format("for the hole edge ({}, {}), the 3D facet is\n" " {}\n" @@ -2102,7 +2107,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto bore_hole_and_fill_with_delaunay = [&](CDT_3_vertex_type vertex_type, auto& hole_boundary, const auto& faces_to_delete, Face_is_outside_the_face is_outside_the_face) { - if(this->debug().move_Steiner_vertices()) { + if(debug) { std::cerr << "The star of " << display_vert(v) << " in the CDT_2 of face F#" << face_id << " has a component " << (is_outside_the_face == OUTSIDE ? "OUTSIDE the face " : "") << "with " << hole_boundary.size() << " edges, and " << faces_to_delete.size() << " faces:\n"; @@ -2254,7 +2259,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(facet.first == Cell_handle{}); } mutable_cdt_2.delete_vertex(vh_2d); - if(this->debug().move_Steiner_vertices()) { + if(debug) { dump_face(face_id, std::string("dump_face_") + std::to_string(face_id) + "_after_removing_Steiner_vertex_" + std::to_string(vh_2d->time_stamp()) + ".off"); @@ -2294,7 +2299,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto nb_of_star_components = star_components.component_incident_constraint_face_ids.size(); if(this->debug().move_Steiner_vertices()) { - std::cerr << "- Nb of components or the star: " << star_components.component_offsets.size() - 1 << '\n'; + std::cerr << "- Nb of components of the star: " << nb_of_star_components << '\n'; } if(nb_of_star_components == 1) { if(this->debug().move_Steiner_vertices()) { @@ -2341,7 +2346,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D map_from_incident_constraint_face_id_to_new_triangles); if(this->debug().move_Steiner_vertices()) { std::ostringstream ss; - ss << "dump_vertex_" << IO::oformat(v, with_offset) << "_component_" << component_index << ".off"; + ss << "dump_vertex_" << IO::oformat(v, with_offset) << "_component_" << component_index << ".off"; const std::string filename = ss.str(); std::ofstream out(filename); out.precision(17); @@ -2364,12 +2369,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D << "\n"; } } else { - if(this->debug().move_Steiner_vertices()) { + if(true || this->debug().move_Steiner_vertices()) { auto _ = IO::make_color_guards(std::cerr); std::cerr << "none (the component is either degenerate or open) ABORT\n"; } - central_points.reset(); - return central_points; + if(!this->debug().move_Steiner_vertices_allow_negative_tets()) { + central_points.reset(); + return central_points; + } + central_points->push_back(tr().point(v)); // push the current position of the vertex as a fallback } } return central_points; @@ -2380,10 +2388,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } if(!components_central_points.has_value()) { - if(this->debug().move_Steiner_vertices()) { + if(true || this->debug().move_Steiner_vertices()) { auto _ = IO::make_color_guards(std::cerr); std::cerr << "- No central point could be computed for at least one of the components, aborting moving the " "vertex to the volume\n"; + // CGAL_error(); } return false; } From a74367d8297e4434f36990e890445d6da86e7ba7 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 5 Jun 2026 12:46:22 +0200 Subject: [PATCH 062/151] use CLI11 instead of parsing options by hand --- .../Conforming_Delaunay_triangulation_3.h | 17 +- .../cdt_3_from_off.cpp | 313 +++++++----------- 2 files changed, 128 insertions(+), 202 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 3957c7104089..faf10f1440a3 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -94,7 +94,8 @@ struct Debug_options { debug_geometric_errors, debug_polygon_insertion, debug_restore_faces, - debug_move_Steiner_vertices, + debug_move_Steiner_vertices_bit1, + debug_move_Steiner_vertices_bit2, display_statistics, use_epeck_for_normals, use_epeck_for_Steiner_points, @@ -155,8 +156,18 @@ struct Debug_options { bool restore_faces() const { return get(Flag::debug_restore_faces); } void restore_faces(bool b) { get(Flag::debug_restore_faces) = b; } - bool move_Steiner_vertices() const { return get(Flag::debug_move_Steiner_vertices); } - void move_Steiner_vertices(bool b) { get(Flag::debug_move_Steiner_vertices) = b; } + bool move_Steiner_vertices() const { + return get(Flag::debug_move_Steiner_vertices_bit1) || get(Flag::debug_move_Steiner_vertices_bit2); + } + + unsigned int move_Steiner_vertices_level() const { + return get(Flag::debug_move_Steiner_vertices_bit1) + 2 * get(Flag::debug_move_Steiner_vertices_bit2); + } + void move_Steiner_vertices(unsigned int level) { + CGAL_assertion(level <= 3); + get(Flag::debug_move_Steiner_vertices_bit1) = level & 1; + get(Flag::debug_move_Steiner_vertices_bit2) = (level >> 1) & 1; + } bool move_Steiner_vertices_allow_negative_tets() const { return get(Flag::move_Steiner_vertices_allow_negative_tets); } void move_Steiner_vertices_allow_negative_tets(bool b) { get(Flag::move_Steiner_vertices_allow_negative_tets) = b; } diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 09b0f5ac5072..b00615d611b2 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,6 @@ #include #include #include -#include #include #include #include @@ -97,71 +97,9 @@ auto milliseconds_since(std::chrono::time_point start_time) { using clock = std::chrono::high_resolution_clock; -void help(std::ostream& out) { - out << R"!!!!!( -Usage: cdt_3_from_off [options] input.off output.off - - input.off: input mesh - output.off: output mesh - - --merge-facets/--no-merge-facets: merge facets into patches (set by default) - --merge-facets-old: merge facets using the old method - --bisect: bisect failures - --vertex-vertex-epsilon : epsilon for vertex-vertex min distance (default: 1e-6) - --segment-vertex-epsilon : epsilon for segment-vertex min distance (default: 0) - --coplanar-polygon-max-angle : max angle for coplanar polygons (default: 1) - --coplanar-polygon-max-distance : max distance for coplanar polygons (default: 1e-6) - - --dump-patches-after-merge : dump patches after merging facets in PLY - --dump-surface-mesh-after-merge : dump surface mesh after merging facets in OFF - --dump-patches-borders-prefix : dump patches borders - --dump-after-conforming : dump mesh after conforming in OFF - - --no-repair: do not repair the mesh - --read-mesh-with-operator: read the mesh with operator>> - --reject-self-intersections: reject self-intersecting polygon soups - --no-is-valid: do not call is_valid checks - - --debug-Steiner-points: debug Steiner point insertion - --debug-Steiner-points-construction: debug Steiner point construction - --debug-move-Steiner-vertices-to-the-volume: debug moving Steiner vertices to the volume - --debug-input-faces: debug input faces - --debug-missing-regions: debug missing regions - --debug-regions: debug regions - --debug_copy_triangulation_into_hole: debug copy_triangulation_into_hole - --debug-validity: add is_valid checks after modifications to the TDS - --debug-finite-edges-map: debug the use of a hash map for finite edges - --debug-subconstraints-to-conform: debug subconstraints to conform - --debug-verbose-special-cases: debug verbose output for special cases - --debug-encroaching-vertices: debug encroaching vertices computation - --debug-conforming-validation: debug edge conforming validation - --debug-constraint-hierarchy: debug constraint hierarchy operations - --debug-geometric-errors: debug geometric error handling - --debug-polygon-insertion: debug polygon insertion process - --debug-restore-faces: debug face restoration process - - --use-finite-edges-map: use a hash map for finite edges (default: false) - --move-Steiner-vertices-to-the-volume/--no-move-Steiner-vertices-to-the-volume: move Steiner vertices to the volume (default: true) - --allow-moving-Steiner-vertices-to-create-negative-tets/--no-allow-moving-Steiner-vertices-to-create-negative-tets: allow moving Steiner vertices to create negative volume tetrahedra (default: false) - --use-epeck-for-normals/--no-use-epeck-for-normals: use exact kernel for normal computations (default: false) - --use-epeck-for-Steiner-points/--no-use-epeck-for-Steiner-points: use exact kernel for Steiner point computations (default: false) - - --verbose/-V: verbose (can be used several times) - --quiet: do not print anything - --help/-h: print this help -)!!!!!"; -} - -[[noreturn]] void error(std::string_view message, std::string_view extra = "") { - std::cerr << "Error: " << message << extra << '\n'; - help(std::cerr); - std::exit(EXIT_FAILURE); -} - struct CDT_options { int verbose_level = 0; - bool need_help = false; bool quiet = false; bool merge_facets = true; bool merge_facets_old_method = false; @@ -170,7 +108,7 @@ struct CDT_options bool read_mesh_with_operator = false; bool debug_Steiner_points = false; bool debug_Steiner_points_construction = false; - bool debug_move_Steiner_vertices = true; + unsigned debug_move_Steiner_vertices = 0; bool debug_input_faces = false; bool debug_missing_regions = false; bool debug_regions = false; @@ -207,137 +145,118 @@ struct CDT_options }; CDT_options::CDT_options(int argc, char* argv[]) { - const std::vector args(argv + 1, argv + argc); - int positional = 0; - - using namespace std::literals::string_view_literals; - for (auto it = args.begin(); it != args.end(); ++it) { - auto get_next_arg_or_error_out = [&it, &args]() -> std::string { - if(it + 1 == args.end()) { - error("extra argument required after "sv, *it); - } - return std::string(*++it); - }; - std::string_view arg = *it; - bool enable = true; - bool arg_is_a_long_option = false; - if(arg.starts_with("--no-")) { - enable = false; - arg_is_a_long_option = true; - arg.remove_prefix(5); - } else if(arg.starts_with("--")) { - arg_is_a_long_option = true; - arg.remove_prefix(2); - } - if(arg_is_a_long_option) { - if(arg == "merge-facets"sv) { - merge_facets = enable; - } else if(arg == "no-merge-facets"sv) { - merge_facets = false; - } else if(arg == "reject-self-intersections"sv) { - reject_self_intersections = enable; - } else if(arg == "no-repair"sv) { - repair_mesh = false; - } else if(arg == "read-mesh-with-operator"sv) { - read_mesh_with_operator = enable; - } else if(arg == "merge-facets-old"sv) { - merge_facets = enable; - merge_facets_old_method = enable; - } else if(arg == "bisect"sv) { - bisect_failures = enable; - } else if(arg == "dump-patches-after-merge"sv) { - dump_patches_after_merge_filename = get_next_arg_or_error_out(); - } else if(arg == "dump-patches-borders-prefix"sv) { - dump_patches_borders_prefix = get_next_arg_or_error_out(); - } else if(arg == "dump-surface-mesh-after-merge"sv) { - dump_surface_mesh_after_merge_filename = get_next_arg_or_error_out(); - } else if(arg == "dump-after-conforming"sv) { - dump_after_conforming_filename = get_next_arg_or_error_out(); - } else if(arg == "vertex-vertex-epsilon"sv) { - vertex_vertex_epsilon = std::stod(get_next_arg_or_error_out()); - } else if(arg == "segment-vertex-epsilon"sv) { - segment_vertex_epsilon = std::stod(get_next_arg_or_error_out()); - } else if(arg == "coplanar-polygon-max-angle"sv) { - coplanar_polygon_max_angle = std::stod(get_next_arg_or_error_out()); - } else if(arg == "coplanar-polygon-max-distance"sv) { - coplanar_polygon_max_distance = std::stod(get_next_arg_or_error_out()); - } else if(arg == "quiet"sv) { - quiet = enable; - } else if(arg == "no-is-valid"sv) { - call_is_valid = false; - } else if(arg == "debug-Steiner-points"sv) { - debug_Steiner_points = enable; - } else if(arg == "debug-Steiner-points-construction"sv) { - debug_Steiner_points_construction = enable; - } else if(arg == "debug-move-Steiner-vertices-to-the-volume"sv) { - debug_move_Steiner_vertices = enable; - } else if(arg == "debug-input-faces"sv) { - debug_input_faces = enable; - } else if(arg == "debug-missing-regions"sv) { - debug_missing_regions = enable; - } else if(arg == "debug-regions"sv) { - debug_regions = enable; - } else if(arg == "debug_copy_triangulation_into_hole"sv) { - debug_copy_triangulation_into_hole = enable; - } else if(arg == "debug-validity"sv) { - debug_validity = enable; - } else if(arg == "debug-finite-edges-map"sv) { - debug_finite_edges_map = enable; - } else if(arg == "debug-subconstraints-to-conform"sv) { - debug_subconstraints_to_conform = enable; - } else if(arg == "debug-verbose-special-cases"sv) { - debug_verbose_special_cases = enable; - } else if(arg == "debug-encroaching-vertices"sv) { - debug_encroaching_vertices = enable; - } else if(arg == "debug-conforming-validation"sv) { - debug_conforming_validation = enable; - } else if(arg == "debug-constraint-hierarchy"sv) { - debug_constraint_hierarchy = enable; - } else if(arg == "debug-geometric-errors"sv) { - debug_geometric_errors = enable; - } else if(arg == "debug-polygon-insertion"sv) { - debug_polygon_insertion = enable; - } else if(arg == "debug-restore-faces"sv) { - debug_restore_faces = enable; - } else if(arg == "use-finite-edges-map"sv) { - use_finite_edges_map = enable; - } else if(arg == "move-Steiner-vertices-to-the-volume"sv) { - move_Steiner_vertices_to_the_volume = enable; - } else if(arg == "allow-moving-Steiner-vertices-to-create-negative-tets"sv) { - allow_moving_Steiner_vertices_to_create_negative_tets = enable; - } else if(arg == "no-use-epeck-for-normals"sv) { - use_epeck_for_normals = false; - } else if(arg == "no-use-epeck-for-Steiner-points"sv) { - use_epeck_for_Steiner_points = false; - } else if(arg == "use-epeck-for-normals"sv) { - use_epeck_for_normals = enable; - } else if(arg == "use-epeck-for-Steiner-points"sv) { - use_epeck_for_Steiner_points = enable; - } else if(arg == "verbose"sv || arg == "-V"sv) { - ++verbose_level; - } else if(arg == "help"sv || arg == "-h"sv) { - need_help = enable; - } - } else if(arg == "-V"sv) { - ++verbose_level; - } else if(arg == "-h"sv) { - need_help = true; - } else if(arg.starts_with('-')) { - error("unknown option "sv, arg); - } else { - switch(positional) { - case 0: - input_filename = arg; - ++positional; - break; - case 1: - output_filename = arg; - ++positional; - break; - default: - error("too many arguments"sv); - } - } + CLI::App app{"Build and process a constrained Delaunay triangulation from an OFF mesh"}; + app.set_help_flag("-h,--help", "print this help"); + app.allow_extras(false); + + app.add_flag("-V,--verbose", verbose_level, + "verbose (can be used several times)") + ->multi_option_policy(CLI::MultiOptionPolicy::Sum); + + app.add_flag("--quiet", quiet, "do not print anything"); + + app.add_flag("--merge-facets,!--no-merge-facets", merge_facets, + "merge facets into patches"); + app.add_flag("--merge-facets-old", merge_facets_old_method, + "merge facets using the old method"); + app.add_flag("--bisect", bisect_failures, "bisect failures"); + app.add_flag("--reject-self-intersections", reject_self_intersections, + "reject self-intersecting polygon soups"); + app.add_flag("--read-mesh-with-operator", read_mesh_with_operator, + "read the mesh with operator>>"); + app.add_flag("--repair,!--no-repair", repair_mesh, + "repair the mesh while reading polygon soup"); + app.add_flag("--call-is-valid,!--no-is-valid", call_is_valid, + "call is_valid checks"); + + app.add_option("--vertex-vertex-epsilon", vertex_vertex_epsilon, + "epsilon for vertex-vertex min distance"); + app.add_option("--segment-vertex-epsilon", segment_vertex_epsilon, + "epsilon for segment-vertex min distance"); + app.add_option("--coplanar-polygon-max-angle", coplanar_polygon_max_angle, + "max angle for coplanar polygons"); + app.add_option("--coplanar-polygon-max-distance", coplanar_polygon_max_distance, + "max distance for coplanar polygons"); + + app.add_option("--dump-patches-after-merge", dump_patches_after_merge_filename, + "dump patches after merging facets in PLY"); + app.add_option("--dump-surface-mesh-after-merge", dump_surface_mesh_after_merge_filename, + "dump surface mesh after merging facets in OFF"); + app.add_option("--dump-patches-borders-prefix", dump_patches_borders_prefix, + "dump patches borders"); + app.add_option("--dump-after-conforming", dump_after_conforming_filename, + "dump mesh after conforming in OFF"); + + app.add_flag("--debug-Steiner-points", debug_Steiner_points, + "debug Steiner point insertion"); + app.add_flag("--debug-Steiner-points-construction", debug_Steiner_points_construction, + "debug Steiner point construction"); + auto debug_move_Steiner_vertices_option = + app.add_option("--debug-move-Steiner-vertices-to-the-volume", debug_move_Steiner_vertices, + "debug moving Steiner vertices to the volume (level 0..3)") + ->check(CLI::Range(0, 3)) + ->expected(0, 1); + app.add_flag("--debug-input-faces", debug_input_faces, "debug input faces"); + app.add_flag("--debug-missing-regions", debug_missing_regions, "debug missing regions"); + app.add_flag("--debug-regions", debug_regions, "debug regions"); + app.add_flag("--debug_copy_triangulation_into_hole", debug_copy_triangulation_into_hole, + "debug copy_triangulation_into_hole"); + app.add_flag("--debug-validity", debug_validity, + "add is_valid checks after modifications to the TDS"); + app.add_flag("--debug-finite-edges-map", debug_finite_edges_map, + "debug the use of a hash map for finite edges"); + app.add_flag("--debug-subconstraints-to-conform", debug_subconstraints_to_conform, + "debug subconstraints to conform"); + app.add_flag("--debug-verbose-special-cases", debug_verbose_special_cases, + "debug verbose output for special cases"); + app.add_flag("--debug-encroaching-vertices", debug_encroaching_vertices, + "debug encroaching vertices computation"); + app.add_flag("--debug-conforming-validation", debug_conforming_validation, + "debug edge conforming validation"); + app.add_flag("--debug-constraint-hierarchy", debug_constraint_hierarchy, + "debug constraint hierarchy operations"); + app.add_flag("--debug-geometric-errors", debug_geometric_errors, + "debug geometric error handling"); + app.add_flag("--debug-polygon-insertion", debug_polygon_insertion, + "debug polygon insertion process"); + app.add_flag("--debug-restore-faces", debug_restore_faces, + "debug face restoration process"); + + app.add_flag("--use-finite-edges-map", use_finite_edges_map, + "use a hash map for finite edges"); + app.add_flag("--move-Steiner-vertices-to-the-volume,!--no-move-Steiner-vertices-to-the-volume", + move_Steiner_vertices_to_the_volume, + "move Steiner vertices to the volume"); + app.add_flag("--allow-moving-Steiner-vertices-to-create-negative-tets", + allow_moving_Steiner_vertices_to_create_negative_tets, + "allow moving Steiner vertices to create negative volume tetrahedra"); + app.add_flag("--use-epeck-for-normals,!--no-use-epeck-for-normals", use_epeck_for_normals, + "use exact kernel for normal computations"); + app.add_flag("--use-epeck-for-Steiner-points,!--no-use-epeck-for-Steiner-points", + use_epeck_for_Steiner_points, + "use exact kernel for Steiner point computations"); + + app.add_option("input.off", input_filename, "input mesh"); + app.add_option("output.off", output_filename, "output mesh"); + + for(auto* option : app.get_options()) { + option->always_capture_default(true); + } + + app.get_formatter()->enable_default_flag_values(true); + + try { + app.parse(argc, argv); + } catch(const CLI::ParseError& e) { + std::exit(app.exit(e)); + } + + if(*debug_move_Steiner_vertices_option && debug_move_Steiner_vertices == 0) { + debug_move_Steiner_vertices = 1; + } + + if(merge_facets_old_method) { + merge_facets = true; } } @@ -874,10 +793,6 @@ int main(int argc, char* argv[]) { CGAL::get_default_random() = CGAL::Random(42); CDT_options options(argc, argv); - if(options.need_help) { - help(std::cout); - return 0; - } CGAL::CDT_3_read_polygon_mesh_output read_mesh_result; auto start_time = clock::now(); From babee5fb36042c464c01ab41290cd8630fda7e63 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 9 Jun 2026 15:22:24 +0200 Subject: [PATCH 063/151] WIP: allow negative tetrahedra --- .gitignore | 13 +- .../Conforming_Delaunay_triangulation_3.h | 39 +- ...ing_constrained_Delaunay_triangulation_3.h | 570 ++++++++++-------- ...ned_Delaunay_triangulation_vertex_data_3.h | 3 +- 4 files changed, 369 insertions(+), 256 deletions(-) diff --git a/.gitignore b/.gitignore index 74e4fdc1f4a0..e6ecde661dcd 100644 --- a/.gitignore +++ b/.gitignore @@ -1077,6 +1077,12 @@ Makefile *.sln *.depends +# Output files of tetgen +*.edge +*.cell +*.node +*.smesh + # The rest: "various" things that I do not know how to classify # -- Laurent Rineau, 2013/01/11 *.ncb @@ -1163,10 +1169,11 @@ Data/data/meshes/*.*.vtk Data/data/meshes/*.log Data/data/meshes/*.off-cdt-output.off dump_*.off +dump_*.ply dump_*.txt -dump-*.binary.cgal -dump-*.polylines.txt -dump-*.xyz +dump*.binary.cgal +dump*.polylines.txt +dump*.xyz dump.off.mesh log.txt patches_after_merge.ply diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index faf10f1440a3..8f8377dac707 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -322,9 +322,24 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { using Line = typename T_3::Geom_traits::Line_3; using Locate_type = typename T_3::Locate_type; - inline static With_offset_tag with_offset{ -1 }; - inline static With_point_tag with_point{ {-1} }; - inline static With_point_and_info_tag with_point_and_info{ { {-1} } }; + inline static With_offset_tag with_offset_tag{ -1 }; + inline static With_point_tag with_point_tag{ {-1} }; + inline static With_point_and_info_tag with_point_and_info_tag{ { {-1} } }; + + template + static auto with_offset(T&& arg) { + return IO::oformat(std::forward(arg), with_offset_tag); + }; + + template + static auto with_point(T&& arg) { + return IO::oformat(std::forward(arg), with_point_tag); + }; + + template + static auto with_point_and_info(T&& arg) { + return IO::oformat(std::forward(arg), with_point_and_info_tag); + }; Conforming_Delaunay_triangulation_3(const Geom_traits& gt = Geom_traits()) : T_3(gt) @@ -349,15 +364,13 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { using Subconstraint = typename Constraint_hierarchy::Subconstraint; auto display_vert(Vertex_handle v) const{ - return IO::oformat([&](std::ostream& os) -> auto& { - return os << IO::oformat(v, with_point); - }, IO_manip_tag{}); + return IO::oformat(v, with_point_tag); } auto display_subcstr(Subconstraint subconstraint) const { auto [va, vb] = subconstraint; std::stringstream os; - os << "(" << IO::oformat(va, with_offset) << ", " << IO::oformat(vb, with_offset) << ")" + os << "(" << with_offset(va) << ", " << with_offset(vb) << ")" << ": [ " << display_vert(va) << " - " << display_vert(vb) << " ]"; return os.str(); } @@ -594,6 +607,8 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { public: CDT_3::Debug_options& debug() { return debug_options_; } const CDT_3::Debug_options& debug() const { return debug_options_; } + CDT_3::Debug_options& options() { return debug_options_; } + const CDT_3::Debug_options& options() const { return debug_options_; } // Backward compatibility wrappers (deprecated, use debug().method() instead) bool use_finite_edges_map() const { return update_all_finite_edges_ && debug_options_.use_finite_edges_map_flag(); } @@ -654,9 +669,9 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { const auto is_edge = this->is_edge(va, vb); if constexpr (cdt_3_can_use_cxx20_format()) if(debug().conforming_validation()) { std::cerr << cdt_3_format("is_conforming>> Edge is 3D: {} ({} , {})\n", - is_edge, - CGAL::IO::oformat(va, with_point_and_info), - CGAL::IO::oformat(vb, with_point_and_info)); + is_edge, + with_point_and_info(va), + with_point_and_info(vb)); } return is_edge; }); @@ -840,7 +855,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { ss << "insert_Steiner_point_on_subconstraint: Steiner point coincides with an existing vertex\n"; ss << " -> Steiner point: " << steiner_pt << '\n'; ss << " on constraint: " << display_vert(c_va) << " - " << display_vert(c_vb) << '\n'; - ss << " -> existing vertex: " << IO::oformat(other_v, with_point_and_info) << '\n'; + ss << " -> existing vertex: " << with_point_and_info(other_v) << '\n'; if(other_v->ccdt_3_data().number_of_incident_constraints() > 0) { const auto c_id = other_v->ccdt_3_data().constrained_polyline_id(*this); const auto [c_va, c_vb] = constraint_extremities(c_id); @@ -880,7 +895,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { insert_Steiner_point_on_subconstraint(steiner_pt, hint, subconstraint, constraint, visitor); if(debug().Steiner_points()) { const auto [c_start, c_end] = constraint_extremities(constraint); - std::cerr << "(" << IO::oformat(va, with_offset) << ", " << IO::oformat(vb, with_offset) << ")"; + std::cerr << "(" << with_offset(va) << ", " << with_offset(vb) << ")"; std::cerr << ": [ " << display_vert(c_start) << " - " << display_vert(c_end) << " ] "; std::cerr << " new vertex " << display_vert(v) << '\n'; } diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index b68ad1cf1138..54aedb6793d0 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -667,6 +667,7 @@ class Conforming_constrained_Delaunay_triangulation_3 /// \cond SKIP_IN_MANUAL CDT_3_impl& impl() { return cdt_impl; } + const CDT_3_impl& impl() const { return cdt_impl; } /// \endcond public: /** \name Constructors @@ -1255,6 +1256,27 @@ class Conforming_constrained_Delaunay_triangulation_3 out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_IN_FACE) << " Steiner vertices in faces\n"; out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_IN_VOLUME) << " Steiner vertices in volume\n"; out << prefix << " - " << nb(CDT_3_vertex_type::FREE) << " free vertices\n"; + if(impl().options().move_Steiner_vertices_allow_negative_tets()) { + std::size_t nb_of_negative_tets = 0; + auto point = [this](typename Triangulation::Vertex_handle vh) { + return triangulation().point(vh); + }; + CGAL::unordered_flat_set vertices_with_negative_tets; + for(auto c: triangulation().finite_cell_handles()) { + const auto verts + = triangulation().vertices(c); + if(triangulation().orientation(point(verts[0]), point(verts[1]), point(verts[2]), point(verts[3])) < 0) { + ++nb_of_negative_tets; + for(auto v : verts) { + if(v->ccdt_3_data().vertex_type() == CDT_3_vertex_type::STEINER_IN_VOLUME) { + vertices_with_negative_tets.insert(v); + } + } + } + } + out << prefix << " - " << nb_of_negative_tets << " negative tetrahedra\n"; + out << prefix << " - " << vertices_with_negative_tets.size() << " Steiner vertices with negative tetrahedra around\n"; + } return out; }, IO_manip_tag{}); } @@ -1390,16 +1412,35 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Constraint_hierarchy = typename Conforming_Dt::Constraint_hierarchy; using Subconstraint = typename Constraint_hierarchy::Subconstraint; + + using Conforming_Dt::with_offset; + using Conforming_Dt::with_point; + using Conforming_Dt::with_point_and_info; + public: using Constrained_polyline_id = typename Constraint_hierarchy::Constraint_id; protected: - static auto display_vert(Vertex_handle v) { - return IO::oformat([v](std::ostream& os) -> auto& { - return os << IO::oformat(v, with_point_and_info); + auto display_vert(Vertex_handle v) const { + return with_point_and_info(v); + } + + auto display_facet(std::string_view prefix, Facet f) const { + return IO::oformat([&,prefix,f](auto& out) -> auto& { + const auto vertices = tr().vertices(f); + return out << cdt_3_format("\n{}{}" + "\n{}{}" + "\n{}{}" + "\n{}(fourth vertex of its cell: {})", + prefix, with_offset(vertices[0]), + prefix, with_offset(vertices[1]), + prefix, with_offset(vertices[2]), + prefix, with_offset(f.first->vertex((f.second)))); }, IO_manip_tag{}); } + const auto& dbg() const { return this->debug(); } + bool same_triple(const std::array f1_vertices, std::array f2_vertices, Orientation o = EQUAL) const @@ -1455,12 +1496,20 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void set_adjacency_between_3d_facets(Facet f1, Facet f2) const { tr().tds().set_adjacency(f1.first, f1.second, f2.first, f2.second); - if(this->debug().move_Steiner_vertices()) { - std::cerr << cdt_3_format("Set adjacency between facets ({})=[{}] and ({})=[{}]\n", - IO::oformat(f1), IO::oformat(tr().vertices(f1), with_offset), - IO::oformat(f2), IO::oformat(tr().vertices(f2), with_offset)); + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << cdt_3_format("-> set adjacency between facets ({})=[{}]+{} and ({})=[{}]+{}\n", + with_offset(f1), with_offset(tr().vertices(f1)), + with_offset(f1.first->vertex(f1.second)), + with_offset(f2), with_offset(tr().vertices(f2)), + with_offset(f2.first->vertex(f2.second))); CGAL_assertion(opposite_triple(f1, f2)); } + CGAL_assertion(std::all_of(boost::counting_iterator(1), boost::counting_iterator(4), + [f1, f2](int i) { + return f1.first->neighbor((f1.second + i) % 4) != f2.first && + f2.first->neighbor((f2.second + i) % 4) != f1.first; + } + )); } template @@ -1507,7 +1556,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D facet_id < end; ++facet_id) { const auto& [cell, facet_index, type] = components.facets[facet_id]; - if(type == Star_components_facets::Type_of_facet::INCIDENT_TO_V) { + if(!this->debug().move_Steiner_vertices_allow_negative_tets() && + type == Star_components_facets::Type_of_facet::INCIDENT_TO_V) + { continue; } if(tr().is_infinite(Facet(cell, facet_index))) { @@ -1521,15 +1572,20 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D triangles.push_back({vertex_index, vertex_index + 2, vertex_index + 1}); vertex_index += 3; } // end for each facet in the component - const auto& incident_face_ids = components.component_incident_constraint_face_ids[component_index - 1]; - for(auto face_id : incident_face_ids) { - const auto& new_triangles = map_from_incident_constraint_face_id_to_new_triangles.at(face_id); - for(const auto& triangle : new_triangles) { - points.push_back(triangle[0]); - points.push_back(triangle[1]); - points.push_back(triangle[2]); - triangles.push_back({vertex_index, vertex_index + 1, vertex_index + 2}); - vertex_index += 3; + if(!this->debug().move_Steiner_vertices_allow_negative_tets()) { + const auto& incident_face_ids = components.component_incident_constraint_face_ids[component_index - 1]; + for(auto face_id : incident_face_ids) { + if(face_id == INVALID_FACE_INDEX) { + continue; + } + const auto& new_triangles = map_from_incident_constraint_face_id_to_new_triangles.at(face_id); + for(const auto& triangle : new_triangles) { + points.push_back(triangle[0]); + points.push_back(triangle[1]); + points.push_back(triangle[2]); + triangles.push_back({vertex_index, vertex_index + 1, vertex_index + 2}); + vertex_index += 3; + } } } CGAL::Polygon_mesh_processing::merge_duplicate_points_in_polygon_soup(points, triangles); @@ -1670,7 +1726,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D cells_in_other_components.clear(); // clear it here, and fill it again with the cells opposite to a constrained // facet during the exploration of the current component - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << " (starting a new component through face with id " << component_first_incident_face_id << ")\n"; } // - `out.facets` is part of the result, and a queue as well, with `queue_head` as the index of @@ -1729,7 +1785,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D out.nb_of_incident_facets_for_each_component.push_back(std::distance(begin_it, incident_facets_end)); out.component_offsets.push_back(out.facets.size()); - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << "- Star component #" << out.nb_of_incident_facets_for_each_component.size() << " has:\n - " << (out.facets.size() - component_offset) << " total facets,\n - " @@ -1790,13 +1846,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D New_CDT_2_face_handles_output_iterator new_face_handles_out, Run_mode run_mode = NORMAL_RUN) { - const bool debug = this->debug().move_Steiner_vertices() && run_mode == NORMAL_RUN; + const bool debug = dbg().move_Steiner_vertices_level() > 1 && run_mode == NORMAL_RUN; const bool is_a_half_hole = vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; if(debug) { std::cerr << cdt_3_format("Filling {}hole in CDT_2 with {} edges, is_outside_the_face = {}\n", is_a_half_hole ? "half " : "", hole_boundary.size(), enum_cast(is_outside_the_face)); } + auto indent_guard = IO::make_indenting_guards(debug ? "# " : "", std::cerr); const auto at_scope_exit_guard = make_scope_exit([&cdt_2, hole_edges = std::vector(hole_boundary.begin(), hole_boundary.end())]() { if(std::uncaught_exceptions() > 0) { @@ -1847,25 +1904,16 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const Vertex_handle v_front = front_cell->vertex(6 - front_index - front_cell->index(v0) - front_cell->index(v1)); const Vertex_handle v_back = back_cell->vertex(6 - back_index - back_cell->index(v0) - back_cell->index(v1)); if(debug) { - std::cerr << cdt_3_format(" - first_2d_face has vertices ( {} ,\n" - " {} ,\n" - " {} )\n", + std::cerr << cdt_3_format("- first_2d_face has vertices ( {} ,\n" + " {} ,\n" + " {} )\n", display_vert(vertex_3d(first_2d_face->vertex(0))), display_vert(vertex_3d(first_2d_face->vertex(1))), display_vert(vertex_3d(first_2d_face->vertex(2)))); - const auto vertices = tr().vertices(Facet{front_cell, front_index}); - std::cerr << cdt_3_format(" - front_cell is ({}) with vertices:\n" - " - {}\n" - " - {}\n" - " - {}\n" - " - (4th vertex: {})\n", + std::cerr << cdt_3_format("- front_cell is ({}) with vertices:{}\n", IO::oformat(front_cell), - display_vert(vertices[0]), - display_vert(vertices[1]), - display_vert(vertices[2]), - display_vert(front_cell->vertex(front_index))); - std::cerr << " - front vertex is " << display_vert(v_front) - << "\n - back vertex is "<< display_vert(v_back) << '\n'; + display_facet(" - {}\n", Facet{front_cell, front_index})) + << "\n- back vertex is "<< display_vert(v_back) << '\n'; } auto action_for_each_new_2d_face = [&](CDT_2_face_handle fh_2d) { @@ -1879,27 +1927,37 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto v3 = vertex_3d(fh_2d->vertex(2)); if(debug) { - std::cerr << cdt_3_format("- new CDT_2 face with 3D vertices ( {} ,\n" - " {} ,\n" - " {} )\n", + std::cerr << cdt_3_format("New CDT_2 face with 3D vertices ( {} ,\n" + " {} ,\n" + " {} )\n", display_vert(v1), display_vert(v2), display_vert(v3)); } + auto indent_guard = IO::make_indenting_guards(debug ? " " : "", std::cerr); - const auto new_front_cell = tr().tds().create_cell(v1, v2, v3, v_front); - CGAL_assertion(this->debug().move_Steiner_vertices_allow_negative_tets() || - tr().orientation(v1->point(), v2->point(), v3->point(), v_front->point()) > 0); // DO NOT COMMIT (expansive assertion) - const auto new_back_cell = tr().tds().create_cell(v1, v3, v2, v_back); - CGAL_assertion(this->debug().move_Steiner_vertices_allow_negative_tets() || - tr().orientation(v1->point(), v3->point(), v2->point(), v_back->point()) > 0); // DO NOT COMMIT (expansive assertion) + auto new_cell = [&](Vertex_handle v_a, Vertex_handle v_b, Vertex_handle v_c, Vertex_handle v_d) { + auto cell = tr().tds().create_cell(v_a, v_b, v_c, v_d); + CGAL_assertion(!debug || this->debug().move_Steiner_vertices_allow_negative_tets() || + tr().orientation(v_a->point(), v_b->point(), v_c->point(), v_d->point()) > 0); + return cell; + }; + + const auto new_front_cell = new_cell(v1, v2, v3, v_front); + const auto new_back_cell = new_cell(v1, v3, v2, v_back); set_adjacency_between_3d_facets({new_front_cell, 3}, {new_back_cell, 3}); set_facet_constrained({new_front_cell, 3}, face_index, fh_2d); for(int i = 0; i < 3; ++i) { const CDT_2_face_handle neighbor_2d = fh_2d->neighbor(i); + if(debug) { + std::cerr << "-- explore neighbor(" << i << ") --\n"; + } if(neighbor_2d == CDT_2_face_handle{}) { + if(debug) { + std::cerr << "(neighbor not yet connected, continue)\n"; + } // this edge of `fh_2d` is not yet connected continue; } else if(neighbor_2d == half_hole_neighbor_face) { @@ -1907,17 +1965,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(half_hole_front_facet.first == Cell_handle{}); // only one edge of the hole can be a half hole half_hole_front_facet = {new_front_cell, i}; if(debug) { - auto new_front_cell_i_vertices = tr().vertices(Facet{new_front_cell, i}); - std::cerr << cdt_3_format(" - the 3D facet (new_front_cell={}, i={}) is ON THE BORDER of the half hole:\n" - " {}\n" - " {}\n" - " {}\n" - " (fourth vertex of its cell: {})\n" + std::cerr << cdt_3_format("- the 3D facet (new_front_cell={}, i={}) is ON THE BORDER of the half hole:{}\n" , IO::oformat(new_front_cell), i - , display_vert(new_front_cell_i_vertices[0]) - , display_vert(new_front_cell_i_vertices[1]) - , display_vert(new_front_cell_i_vertices[2]) - , display_vert(new_front_cell->vertex(i))); + , display_facet(" ", Facet{new_front_cell, i})); } continue; } @@ -1936,36 +1986,20 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D neighbor_2d_is_in_the_star ? neighbor_2d->index(fh_2d) : neighbor_front_index_3d; if(debug) { - std::cerr << cdt_3_format(" - the 2D face fh_2d->neighbor(i={}) IS{} in region. Its 3D vertices are:\n" - " {}\n" - " {}\n" - " {}\n" + std::cerr << cdt_3_format("- the 2D face fh_2d->neighbor(i={}) IS{} in region. Its 3D vertices are:\n" + " {}\n" + " {}\n" + " {}\n" , i, (neighbor_2d_is_in_the_star ? "" : " NOT") , display_vert(vertex_3d(neighbor_2d->vertex(0))) , display_vert(vertex_3d(neighbor_2d->vertex(1))) , display_vert(vertex_3d(neighbor_2d->vertex(2)))); - auto new_front_cell_i_vertices = tr().vertices(Facet{new_front_cell, i}); - std::cerr << cdt_3_format(" - the 3D facet (new_front_cell={}, i={}) is\n" - " {}\n" - " {}\n" - " {}\n" - " (fourth vertex of its cell: {})\n" + std::cerr << cdt_3_format(" * the 3D facet (new_front_cell={}, i={}) is{}\n" , IO::oformat(new_front_cell), i - , display_vert(new_front_cell_i_vertices[0]) - , display_vert(new_front_cell_i_vertices[1]) - , display_vert(new_front_cell_i_vertices[2]) - , display_vert(new_front_cell->vertex(i))); - auto neighbor_front_cell_3d_vertices = tr().vertices(Facet{neighbor_front_cell_3d, fh_index_in_neighbor_2d}); - std::cerr << cdt_3_format(" - the 3D facet (neighbor_front_cell_3d={}, fh_index_in_neighbor_2d={}) is\n" - " {}\n" - " {}\n" - " {}\n" - " (fourth vertex of its cell: {})\n" + , display_facet(" ", Facet{new_front_cell, i})); + std::cerr << cdt_3_format(" * the 3D facet (neighbor_front_cell_3d={}, fh_index_in_neighbor_2d={}) is{}\n" , IO::oformat(neighbor_front_cell_3d), fh_index_in_neighbor_2d - , display_vert(neighbor_front_cell_3d_vertices[0]) - , display_vert(neighbor_front_cell_3d_vertices[1]) - , display_vert(neighbor_front_cell_3d_vertices[2]) - , display_vert(neighbor_front_cell_3d->vertex(fh_index_in_neighbor_2d))); + , display_facet(" ", Facet{neighbor_front_cell_3d, fh_index_in_neighbor_2d})); } // end of debug output set_adjacency_between_3d_facets({new_front_cell, i}, {neighbor_front_cell_3d, fh_index_in_neighbor_2d}); @@ -1978,28 +2012,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D : neighbor_back_cell_3d->index(neighbor_front_cell_3d); if(debug) { - auto new_back_cell_vertices = tr().vertices(Facet{new_back_cell, back_cell_i}); - std::cerr << cdt_3_format(" - the 3D facet (new_back_cell={}, back_cell_i={}) is\n" - " {}\n" - " {}\n" - " {}\n" - " (fourth vertex of its cell: {})\n" + std::cerr << cdt_3_format(" * the 3D facet (new_back_cell={}, back_cell_i={}) is{}\n" , IO::oformat(new_back_cell), back_cell_i - , display_vert(new_back_cell_vertices[0]) - , display_vert(new_back_cell_vertices[1]) - , display_vert(new_back_cell_vertices[2]) - , display_vert(new_back_cell->vertex(back_cell_i))); - auto neighbor_back_cell_3d_vertices = tr().vertices(Facet{neighbor_back_cell_3d, back_cell_fh_index_in_neighbor_2d}); - std::cerr << cdt_3_format(" - the 3D facet (neighbor_back_cell_3d={}, back_cell_fh_index_in_neighbor_2d={}) is\n" - " {}\n" - " {}\n" - " {}\n" - " (fourth vertex of its cell: {})\n" + , display_facet(" ", Facet{new_back_cell, back_cell_i})); + std::cerr << cdt_3_format(" * the 3D facet (neighbor_back_cell_3d={}, back_cell_fh_index_in_neighbor_2d={}) is{}\n" , IO::oformat(neighbor_back_cell_3d), back_cell_fh_index_in_neighbor_2d - , display_vert(neighbor_back_cell_3d_vertices[0]) - , display_vert(neighbor_back_cell_3d_vertices[1]) - , display_vert(neighbor_back_cell_3d_vertices[2]) - , display_vert(neighbor_back_cell_3d->vertex(back_cell_fh_index_in_neighbor_2d))); + , display_facet(" ", Facet{neighbor_back_cell_3d, back_cell_fh_index_in_neighbor_2d})); } // end of debug output set_adjacency_between_3d_facets({new_back_cell, back_cell_i}, @@ -2022,6 +2040,81 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return {new_face_handles_out, half_hole_front_facet}; } + bool maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(Vertex_handle v, Face_index face_id) { + auto& mutable_cdt_2 = non_const_face_cdt_2(face_id); + typename CDT_2::Locate_type lt; + int i; + const auto fh_2d = mutable_cdt_2.locate(v->point(), lt, i); + CGAL_assertion(lt == CDT_2::VERTEX); + + const auto vh_2d = fh_2d->vertex(i); + CGAL_assertion(vertex_3d(vh_2d) == v); + auto face_circ = mutable_cdt_2.incident_faces(vh_2d); + const auto done = face_circ; + do { + CDT_2_face_handle current = face_circ; + CDT_2_face_handle next = ++face_circ; + CDT_2_edge current_edge = {current, CDT_2::ccw(current->index(vh_2d))}; + CGAL_assertion(current_edge.first->neighbor(current_edge.second) == next); + if(current->info().is_outside_the_face == OUTSIDE || next->info().is_outside_the_face == OUTSIDE) { + continue; + } + auto current_facet_3d = current->info().facet_3d; + auto current_back_facet_3d = tr().mirror_facet(current_facet_3d); + auto next_facet_3d = next->info().facet_3d; + auto next_back_facet_3d = tr().mirror_facet(next_facet_3d); + + std::optional>> + common_cell_incident_to_current_and_next{}; + + if(current_facet_3d.first == next_facet_3d.first) { + common_cell_incident_to_current_and_next.emplace( + current_facet_3d.first, NEGATIVE, + std::array{next_facet_3d.second, current_facet_3d.second}); + } + if(current_back_facet_3d.first == next_back_facet_3d.first) { + common_cell_incident_to_current_and_next.emplace( + current_back_facet_3d.first, EQUAL, + std::array{current_back_facet_3d.second, next_back_facet_3d.second}); + } + if(common_cell_incident_to_current_and_next) { + auto [common_cell, orientation, common_cell_opposite_vertices] = *common_cell_incident_to_current_and_next; + auto [index_va, index_vb] = common_cell_opposite_vertices; + auto index_vc = tr().next_around_edge(index_va, index_vb); + auto index_vd = tr().next_around_edge(index_vb, index_va); + CGAL_assertion(common_cell->vertex(index_vd) == v); + if(dbg().move_Steiner_vertices_level() > 0) { + const auto v = tr().vertices(common_cell); + std::cerr << cdt_3_format( + "The edge (from \n" + " {} to\n" + " {})\n" + "in the CDT_2 of face F#{} must be flipped, because its incident facets \n" + "are incident to the same cell {}, with the two other vertices\n" + " {},\n" + " {})\n", + display_vert(v[index_vc]), display_vert(v[index_vd]), + face_id, + IO::oformat(common_cell), + display_vert(v[index_va]), display_vert(v[index_vb])); + } + mutable_cdt_2.flip(current_edge.first, current_edge.second); + set_facet_as_not_constrained(current_facet_3d); + set_facet_as_not_constrained(next_facet_3d); + Facet facet_c{common_cell, index_vc}; + Facet facet_d{common_cell, index_vd}; + if(orientation == NEGATIVE) { + facet_c = tr().mirror_facet(facet_c); + facet_d = tr().mirror_facet(facet_d); + } + set_facet_constrained(facet_c, face_id, next); + set_facet_constrained(facet_d, face_id, current); + return true; + } + } while(face_circ != done); + return false; + } + template std::tuple remove_Steiner_vertex_from_cdt_2(Vertex_handle v, @@ -2030,7 +2123,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D New_CDT_2_face_handles_output_iterator new_face_handles_out, Run_mode run_mode = NORMAL_RUN) { - const bool debug = this->debug().move_Steiner_vertices() || run_mode == NORMAL_RUN; + const bool debug = dbg().move_Steiner_vertices_level() > 1 && run_mode == NORMAL_RUN; std::tuple result{new_face_handles_out, {}, {}}; auto& [new_face_handles_out_it, half_hole_front_facet, second_half_hole_front_facet] = result; @@ -2065,7 +2158,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D ++nb_of_incident_faces; } while(++face_circ != done); dump_face(face_id); - std::cerr << " - remove Steiner vertex " << display_vert(v) << " from CDT_2 of face F#" << face_id + std::cerr << " -> remove Steiner vertex " << display_vert(v) << " from CDT_2 of face F#" << face_id << " (nb of incident faces: " << nb_of_incident_faces << ")\n"; if(!cdt_2.CT_2::is_valid(true, 3)) { std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << display_vert(v) @@ -2073,7 +2166,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_error_msg("Aborting due to invalid CDT_2"); } } - auto indent_guard = IO::make_indenting_guards(debug ? 4 : 0); + auto indent_guard = IO::make_indenting_guards(debug ? " |" : "", std::cerr); auto map_from_hole_edges_to_incident_3d_facets = [&](const auto& hole_boundary_begin, const auto& hole_boundary_end, Face_is_outside_the_face is_outside_the_face) { @@ -2083,22 +2176,24 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(auto it = hole_boundary_begin; it != hole_boundary_end; ++it) { auto [fh_2d, index] = *it; - CGAL_assertion(cdt_2.tds().is_face(fh_2d)); // DO NOT COMMIT (expansive assertion: O(n) in the size of the cdt_2) + CGAL_assertion(!debug || cdt_2.tds().is_face(fh_2d)); const auto neighbor_2d_inside_star = fh_2d->neighbor(index); - CGAL_assertion(cdt_2.tds().is_face(neighbor_2d_inside_star)); // DO NOT COMMIT (expansive assertion: O(n) in the size of the cdt_2) + CGAL_assertion(!debug || cdt_2.tds().is_face(neighbor_2d_inside_star)); const auto facet_3d = neighbor_2d_inside_star->info().facet_3d; CGAL_assertion(facet_3d.first != Cell_handle{}); [[maybe_unused]] auto [_, inserted] = map.insert({{fh_2d, index}, facet_3d}); CGAL_assertion(inserted); if(debug) { - auto vertices = tr().vertices(facet_3d); - std::cerr << cdt_3_format("for the hole edge ({}, {}), the 3D facet is\n" - " {}\n" - " {}\n" - " {}\n", + auto back_facet_3d = tr().mirror_facet(facet_3d); + std::cerr << cdt_3_format("for the hole edge ({}, {}), the 3D facet is ({}){}\n" + " the opposite facet is ({})\n" + " opposite vertex: {}\n", display_vert(vertex_3d(fh_2d->vertex(cdt_2.cw(index)))), display_vert(vertex_3d(fh_2d->vertex(cdt_2.ccw(index)))), - display_vert(vertices[0]), display_vert(vertices[1]), display_vert(vertices[2])); + IO::oformat(facet_3d), + display_facet(" ", facet_3d), + IO::oformat(back_facet_3d), + display_vert(back_facet_3d.first->vertex(back_facet_3d.second))); } } return map; @@ -2111,6 +2206,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "The star of " << display_vert(v) << " in the CDT_2 of face F#" << face_id << " has a component " << (is_outside_the_face == OUTSIDE ? "OUTSIDE the face " : "") << "with " << hole_boundary.size() << " edges, and " << faces_to_delete.size() << " faces:\n"; + } + auto indent_guard = IO::make_indenting_guards(debug ? " >" : "", std::cerr); + if(debug) { for(const auto& fh_2d : faces_to_delete) { std::cerr << cdt_3_format("- CDT_2 face with 3D vertices ( {} ,\n" " {} ,\n" @@ -2264,9 +2362,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::to_string(vh_2d->time_stamp()) + ".off"); if(!cdt_2.CT_2::is_valid(true, 3)) { - std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << IO::oformat(v, with_point_and_info) + std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << with_point_and_info(v) << " from face F#" << face_id << '\n'; - CGAL_assertion(false); + CGAL_error_msg("Aborting due to invalid CDT_2"); } } new_face_handles_out_it = new_face_handles_out; @@ -2274,10 +2372,24 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } // end of `remove_Steiner_vertex_from_cdt_2` bool move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices()) { std::cerr << "Moving Steiner vertex " << display_vert(v) << " to the volume\n"; } - auto indent_guards = CGAL::IO::make_indenting_guards(this->debug().move_Steiner_vertices() ? "| " : ""); + auto indent_guards = CGAL::IO::make_indenting_guards(dbg().move_Steiner_vertices_level() > 1 ? "| " : "", std::cerr); + + const auto v_type = vertex_type(v); + if(v_type == CDT_3_vertex_type::STEINER_IN_FACE) { + const auto incident_face_id = v->ccdt_3_data().face_index(); + while(maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(v, incident_face_id)) {} + } else { + CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); + const auto incident_polyline_constraint_id = v->ccdt_3_data().constrained_polyline_id(*this); + for(const auto& [_, incident_face_id] : + CGAL::make_range(this->incident_faces_to_polyline.equal_range(incident_polyline_constraint_id))) + { + while(maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(v, incident_face_id)) {} + } + } const auto incident_constrained_facet_opt = find_in_incident_facets(v, [this](Facet f) { return is_facet_constrained(f); }); @@ -2285,31 +2397,29 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto incident_constrained_facet = *incident_constrained_facet_opt; - const auto nb_of_incident_cells = std::invoke([&]() { - std::size_t nb_of_incident_cells{0}; - tr().incident_cells(v, CGAL::Counting_output_iterator(&nb_of_incident_cells)); - return nb_of_incident_cells; - }); + if(dbg().move_Steiner_vertices_level() > 1) { + const auto nb_of_incident_cells = std::invoke([&]() { + std::size_t nb_of_incident_cells{0}; + tr().incident_cells(v, CGAL::Counting_output_iterator(&nb_of_incident_cells)); + return nb_of_incident_cells; + }); - if(this->debug().move_Steiner_vertices()) { std::cerr << "- Number of incident cells: " << nb_of_incident_cells << '\n'; } const auto star_components = collect_star_components_facets(v, incident_constrained_facet); const auto nb_of_star_components = star_components.component_incident_constraint_face_ids.size(); - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << "- Nb of components of the star: " << nb_of_star_components << '\n'; } if(nb_of_star_components == 1) { - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { auto _ = IO::make_color_guards(std::cerr); std::cerr << "- Only one component in the star, cannot move the vertex to the volume, aborting\n"; } return false; } - const auto v_type = vertex_type(v); - std::map> map_from_incident_constraint_face_id_to_new_triangles; auto dry_run_output_iterator = [&](Face_index face_id) { @@ -2329,13 +2439,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, dry_run_output_iterator(face_id), DRY_RUN); } else { CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); - for(std::size_t component_index = 0, end = star_components.face_ids_between_consecutive_components.size(); - component_index < end; ++component_index) - { - const auto& [face_id, _] = star_components.face_ids_between_consecutive_components[component_index]; + for(std::size_t component_index = 0, end = star_components.face_ids_between_consecutive_components.size(); + component_index < end; ++component_index) + { + const auto& [face_id, _] = star_components.face_ids_between_consecutive_components[component_index]; - remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, dry_run_output_iterator(face_id), DRY_RUN); - } + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, dry_run_output_iterator(face_id), DRY_RUN); + } } const auto components_central_points = std::invoke([&]() { @@ -2344,16 +2454,16 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(std::size_t component_index = 1; component_index <= nb_of_star_components; ++component_index) { auto component_mesh = construct_star_component_mesh(component_index, star_components, map_from_incident_constraint_face_id_to_new_triangles); - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { std::ostringstream ss; - ss << "dump_vertex_" << IO::oformat(v, with_offset) << "_component_" << component_index << ".off"; + ss << "dump_vertex_" << with_offset(v) << "_component_" << component_index << ".off"; const std::string filename = ss.str(); std::ofstream out(filename); out.precision(17); out << component_mesh; } auto opt_kernel_center_point = compute_center_point_if_possible(component_mesh); - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << " - incident constraint faces ids of component #" << component_index << ": {" << IO::oformat(star_components.component_incident_constraint_face_ids[component_index - 1]) << "}\n"; @@ -2362,33 +2472,34 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(opt_kernel_center_point.has_value()) { auto kernel_center_point = *opt_kernel_center_point; central_points->push_back(kernel_center_point); - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << kernel_center_point << " (distance to vertex: " << CGAL::sqrt(CGAL::squared_distance(kernel_center_point, v->point())) << ")" << "\n"; } } else { - if(true || this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { auto _ = IO::make_color_guards(std::cerr); std::cerr << "none (the component is either degenerate or open) ABORT\n"; } if(!this->debug().move_Steiner_vertices_allow_negative_tets()) { central_points.reset(); return central_points; + } else { + central_points->push_back(tr().point(v)); // push the current position of the vertex as a fallback } - central_points->push_back(tr().point(v)); // push the current position of the vertex as a fallback } } return central_points; }); // end compute central points for each component - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { dump_link_mesh_of_vertex_to_ply(v, star_components, map_from_incident_constraint_face_id_to_new_triangles); } if(!components_central_points.has_value()) { - if(true || this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 0) { auto _ = IO::make_color_guards(std::cerr); std::cerr << "- No central point could be computed for at least one of the components, aborting moving the " "vertex to the volume\n"; @@ -2455,9 +2566,17 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D component_index < end; ++component_index) { const auto& [face_id, orientation] = star_components.face_ids_between_consecutive_components[component_index]; - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << "Component " << (component_index+1) << " with center " << display_vert(new_center_vertices[component_index]) << "\n"; + if(this->debug().move_Steiner_vertices_level() > 2) { + const auto& component_facets = star_components.component_facets_range(component_index+1); + for(const auto& [cell, facet_index, facet_type] : component_facets) { + std::cerr << cdt_3_format(" - facet ({}) of with vertices:{}\n", + IO::oformat(Facet{cell, facet_index}), + display_facet(" ", Facet{cell, facet_index})); + } + } std::cerr << "Now remove the Steiner vertex from the CDT_2 of face with id " << face_id << " (orientation: " << (orientation == CGAL::NEGATIVE ? "negative" : "positive") << ")\n"; } @@ -2473,30 +2592,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(true == facet_to_reconnect.first->has_vertex(new_center_vertices[component_index])) { facet_to_reconnect = other_side_of_cdt_2(facet_to_reconnect, face_id); } - if(this->debug().move_Steiner_vertices()) { - auto vertices = this->vertices(facet_to_reconnect); - std::cerr << cdt_3_format("The facet ({}) to reconnect in the 3D triangulation is:\n" - " - {}\n" - " - {}\n" - " - {}\n" - " - (4th vertex: {})\n", - IO::oformat(facet_to_reconnect), - display_vert(vertices[0]), - display_vert(vertices[1]), - display_vert(vertices[2]), - display_vert(facet_to_reconnect.first->vertex(facet_to_reconnect.second))); + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << cdt_3_format("The facet ({}) to reconnect in the 3D triangulation is:{}\n", + IO::oformat(facet_to_reconnect), display_facet(" - ", facet_to_reconnect)); auto other_facet = other_side_of_cdt_2(facet_to_reconnect, face_id); - vertices = this->vertices(other_facet); - std::cerr << cdt_3_format("The facet ({}) on the other side of the CDT_2 is:\n" - " - {}\n" - " - {}\n" - " - {}\n" - " - (4th vertex: {})\n", - IO::oformat(other_facet), - display_vert(vertices[0]), - display_vert(vertices[1]), - display_vert(vertices[2]), - display_vert(other_facet.first->vertex(other_facet.second))); + std::cerr << cdt_3_format("The facet ({}) on the other side of the CDT_2 is:{}\n", + IO::oformat(other_facet), display_facet(" - ", other_facet)); } if(!last_facet_to_reconnect_to_opt.has_value()) { @@ -2513,13 +2614,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D set_adjacency_between_3d_facets(*previous_facet_to_reconnect_opt, *last_facet_to_reconnect_to_opt); } - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << "- number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; } tr().tds().delete_vertex(v); - CGAL_assertion(this->debug().move_Steiner_vertices() == false || tr().tds().is_valid(true)); // DO NOT COMMIT + CGAL_assertion(dbg().move_Steiner_vertices_level() < 2 || tr().tds().is_valid(true)); return true; } // end move_one_Steiner_vertex_to_the_volume(..) @@ -2632,10 +2733,16 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D fc->info().is_outside_the_face = outside_on_left; fc->info().is_in_region = in_region_on_left; } while(++fc != fc_begin); - fc = cdt_2.incident_faces(v_Steiner_2d, fh_2d); - fc_begin = fc; + fc = fc_begin; do { - fc->info().missing_subface = true; + auto is_facet = self->is_2d_face_also_a_3d_facet(fc); + if(!is_facet) { + fc->info().missing_subface = true; + } else { + const auto [facet, orient] = *is_facet; + fc->info().missing_subface = false; + self->set_facet_constrained(facet, polygon_id, fc, orient); + } const auto v_Steiner_index = fc->index(v_Steiner_2d); const auto other_edge = cdt_2.mirror_edge({fc, v_Steiner_index}); fc->info().is_edge_also_in_3d_triangulation.set(other_edge.first->info().is_edge_also_in_3d_triangulation.test(other_edge.second)); @@ -2837,7 +2944,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(false == get(v_filter_map, v)) continue; } auto vh = this->insert(get(vertex_point_map, v), hint_ch, false); - vh->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::CORNER); + vh->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::INPUT_VERTEX); hint_ch = vh->cell(); put(tr_vertex_pmap, v, vh); } @@ -3152,7 +3259,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return {min_distance, vertices_of_min_edge}; } - static void print_minimum_distance_info(const Min_distance_result& min_dist) { + void print_minimum_distance_info(const Min_distance_result& min_dist) const { std::cout << "Min distance between vertices: " << min_dist.min_distance << '\n' << " between vertices: : " << display_vert(min_dist.vertices_of_min_edge[0]) << " " @@ -3313,7 +3420,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } auto move_Steiner_vertices_to_the_volume() { - if(this->debug().move_Steiner_vertices()) { + if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << "move_Steiner_vertices_to_the_volume()\n"; dump_constrained_facets_to_off("dump_before_moving_steiner_vertices.off"); } @@ -4255,10 +4362,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D })); } - using Conforming_Dt::with_offset; - using Conforming_Dt::with_point; - using Conforming_Dt::with_point_and_info; - // ------------------------- // restore_subface_region // ------------------------- @@ -4271,7 +4374,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto guard_color = CGAL::IO::make_color_guards(std::cerr); std::cerr << "restore_subface_region face index: " << face_index << ", region #" << region_index << "\n"; } - auto guard_indenting = CGAL::IO::make_indenting_guards("| ", std::cerr, std::cout); + auto guard_indenting = CGAL::IO::make_indenting_guards("| ", std::cerr); const auto& cdt_2 = non_const_cdt_2; const auto& fh_region = non_const_fh_region; @@ -4296,7 +4399,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << "region_border_vertices.size() = " << region_border_vertices.size() << "\n"; for(auto v : region_border_vertices) { - std::cerr << cdt_3_format(" {}\n", IO::oformat(v, with_point)); + std::cerr << cdt_3_format(" {}\n", with_point(v)); } std::ofstream out(cdt_3_format("dump_border_edges_region_{}_{}.polylines.txt", face_index, region_index)); out.precision(17); @@ -4367,13 +4470,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format ("NOTE: diagonal: {:.6} {:.6} {} in tr\n", - IO::oformat(*diagonal.begin(), with_point), - IO::oformat(*std::next(diagonal.begin()), with_point), + with_point(*diagonal.begin()), + with_point(*std::next(diagonal.begin())), this->is_edge(*diagonal.begin(), *std::next(diagonal.begin())) ? "IS" : "is NOT"); std::cerr << cdt_3_format( "NOTE: the other diagonal: {:.6} {:.6} {} in tr\n", - IO::oformat(*other_diagonal.begin(), with_point), - IO::oformat(*std::next(other_diagonal.begin()), with_point), + with_point(*other_diagonal.begin()), + with_point(*std::next(other_diagonal.begin())), this->is_edge(*other_diagonal.begin(), *std::next(other_diagonal.begin())) ? "IS" : "is NOT"); if(cdt_2.geom_traits().side_of_oriented_circle_2_object()( (*region_border_vertices.begin())->point(), (*std::next(region_border_vertices.begin()))->point(), @@ -4527,10 +4630,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } auto register_internal_constrained_facet = [this](Facet f) { this->register_facet_to_be_constrained(f); }; - std::optional indent_guards; + std::optional indent_guards; if(this->debug().copy_triangulation_into_hole() || this->debug().regions()) { std::cerr << "# upper cavity\n"; - indent_guards.emplace(IO::make_indenting_guards(" ")); + indent_guards.emplace(IO::make_indenting_guards(" ", std::cerr)); } [[maybe_unused]] const auto [upper_cavity_triangulation, vertices_of_upper_cavity, map_upper_cavity_vertices_to_ambient_vertices, facets_of_upper_cavity, @@ -4543,7 +4646,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().copy_triangulation_into_hole() || this->debug().regions()) { indent_guards.reset(); std::cerr << "# lower cavity\n"; - indent_guards.emplace(IO::make_indenting_guards(" ")); + indent_guards.emplace(IO::make_indenting_guards(" ", std::cerr)); } [[maybe_unused]] const auto [lower_cavity_triangulation, vertices_of_lower_cavity, map_lower_cavity_vertices_to_ambient_vertices, facets_of_lower_cavity, @@ -4573,8 +4676,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto display_face = [&]() { std::stringstream s; s.precision(std::cerr.precision()); - s << "(" << IO::oformat(v0, this->with_offset) << ", " << IO::oformat(v1, this->with_offset) - << ", " << IO::oformat(v2, this->with_offset) << ") = ( " + s << "(" << with_offset(v0) << ", " << with_offset(v1) << ", " << with_offset(v2) << ") = ( " << tr().point(v0) << " " << tr().point(v1) << " " << tr().point(v2) << " )"; return s.str(); @@ -4638,12 +4740,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D cells_of_upper_cavity.size() > original_intersecting_cells.size()) { std::cerr << cdt_3_format("!! Cavity has grown and has now " - "{} vertices in upper cavity and {} in lower, " - "{} facets in upper cavity and {} in lower\n", - vertices_of_upper_cavity.size(), - vertices_of_lower_cavity.size(), - facets_of_upper_cavity.size(), - facets_of_lower_cavity.size()); + "{} vertices in upper cavity and {} in lower, " + "{} facets in upper cavity and {} in lower\n", + vertices_of_upper_cavity.size(), + vertices_of_lower_cavity.size(), + facets_of_upper_cavity.size(), + facets_of_lower_cavity.size()); } } @@ -4658,9 +4760,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(vt[0] != vt[2]); CGAL_assertion(vt[1] != vt[2]); std::cerr << cdt_3_format("outer map: Adding {}triple ({:.6}, {:.6}, {:.6})\n", extra, - IO::oformat(vt[0], with_point), - IO::oformat(vt[1], with_point), - IO::oformat(vt[2], with_point)); + with_point(vt[0]), + with_point(vt[1]), + with_point(vt[2])); std::stringstream filename; filename << "dump_upper_outer_map_region_" << face_index << "_" << region_index << ".off"; std::ofstream out(filename.str()); @@ -4723,9 +4825,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "upper_inner_map:\n"; for(auto [vt, _] : upper_inner_map) { std::cerr << cdt_3_format(" {:.6}, {:.6}, {:.6})\n", - IO::oformat(vt[0], with_point), - IO::oformat(vt[1], with_point), - IO::oformat(vt[2], with_point)); + with_point(vt[0]), with_point(vt[1]), with_point(vt[2])); } } this->copy_triangulation_into_hole(map_upper_cavity_vertices_to_ambient_vertices, @@ -4758,9 +4858,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "outer_map:\n"; for(auto [vt, _] : outer_map) { std::cerr << cdt_3_format(" {:.6}, {:.6}, {:.6})\n", - IO::oformat(vt[0], with_point), - IO::oformat(vt[1], with_point), - IO::oformat(vt[2], with_point)); + with_point(vt[0]), with_point(vt[1]), with_point(vt[2])); } std::stringstream filename; filename << "dump_lower_outer_map_region_" << face_index << "_" << region_index << ".off"; @@ -5140,13 +5238,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } cleanup(); - // assert(is_valid(true)); - // this->study_bug = true; const auto v = this->insert_in_cdt_3(steiner_pt, lt, ch, li, lj, insert_in_conflict_visitor);// TODO: use "insert in hole" - // this->study_bug = false; - // assert(is_valid(true)); if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().Steiner_points()) { - std::cerr << " -> " << IO::oformat(v, with_offset) << '\n'; + std::cerr << " -> " << with_offset(v) << '\n'; } v->ccdt_3_data().set_Steiner_vertex_in_face(face_index); [[maybe_unused]] typename CDT_2::Locate_type lt_2; @@ -5194,7 +5288,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto& context = *contexts.begin(); const auto constrained_polyline_id = context.id(); CGAL_assertion(constrained_polyline_id != Constrained_polyline_id{}); - // this->study_bug = true; Locate_type mid_lt; int mid_li, min_lj; Cell_handle mid_c = tr().locate(mid, mid_lt, mid_li, min_lj, va_3d->cell()); @@ -5203,10 +5296,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D this->insert_Steiner_point_on_subconstraint(mid, mid_c, {va_3d, vb_3d}, constrained_polyline_id, insert_in_conflict_visitor); if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().Steiner_points()) { - std::cerr << " -> " << IO::oformat(v, with_offset) << '\n'; + std::cerr << " -> " << with_offset(v) << '\n'; } - // this->study_bug = false; - // assert(is_valid(true)); } bool restore_face(CDT_3_signed_index face_index) { @@ -5327,10 +5418,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto v = tr().vertices(it); std::cerr << "non-empty sphere at non-constrained facet (" << IO::oformat(Cell_handle(it)) << ", " << i << ") the cell is:\n " - << IO::oformat(v[0], with_point) << "\n " - << IO::oformat(v[1], with_point) << "\n " - << IO::oformat(v[2], with_point) << "\n " - << IO::oformat(v[3], with_point) << "\ncontains:\n " + << with_point(v[0]) << "\n " + << with_point(v[1]) << "\n " + << with_point(v[2]) << "\n " + << with_point(v[3]) << "\ncontains:\n " << display_vert(n->vertex(n_index)) << '\n'; using EK = CGAL::Exact_predicates_exact_constructions_kernel; const auto to_exact = CGAL::Cartesian_converter(); @@ -5617,14 +5708,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D { if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format("test_edge {} {} return {} {}\n", - IO::oformat(v0, with_point_and_info), - IO::oformat(v1, with_point_and_info), + with_point_and_info(v0), + with_point_and_info(v1), b, not_visited ? "(new)" : "(cached)"); if(not_visited && b) { - std::cerr << " triangle " << IO::oformat(triangle_vertices[0], with_point_and_info) << "\n" - << " " << IO::oformat(triangle_vertices[1], with_point_and_info) << "\n" - << " " << IO::oformat(triangle_vertices[2], with_point_and_info) << "\n"; + std::cerr << " triangle " << with_point_and_info(triangle_vertices[0]) << "\n" + << " " << with_point_and_info(triangle_vertices[1]) << "\n" + << " " << with_point_and_info(triangle_vertices[2]) << "\n"; } } CGAL_USE(this, v0, v1, b, not_visited); @@ -5644,7 +5735,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::swap(v0, v1); } if constexpr(cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { - auto indent_guard = CGAL::IO::make_indenting_guards("| "); + auto indent_guard = CGAL::IO::make_indenting_guards("| ", std::cerr); debug_dump_edge_region_intersection(face_index, region_index, fh_region, region_vertices, border_edges, intersecting_edges.size(), v0, v1, {cell, index_v0, index_v1}, triangle_vertices); } @@ -5653,8 +5744,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::stringstream ss; ss.precision(std::cerr.precision()); ss << "Error: detected a self-intersection.\n" - " The constrained edge (" << IO::oformat(v0, with_point_and_info) - << ", " << IO::oformat(v1, with_point_and_info) + " The constrained edge (" << with_point_and_info(v0) << ", " << with_point_and_info(v1) << ") intersects the interior of the polygonal constraint #" << face_index << " (sub-region #" << region_index << ").\n" << " The input data is either self-intersecting or the input" @@ -5676,12 +5766,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if constexpr(cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format("test_cell #{}\n {}\n {}\n {}\n {}\n", cell->time_stamp(), - IO::oformat(cell->vertex(0), with_point_and_info), - IO::oformat(cell->vertex(1), with_point_and_info), - IO::oformat(cell->vertex(2), with_point_and_info), - IO::oformat(cell->vertex(3), with_point_and_info)); + with_point_and_info(cell->vertex(0)), + with_point_and_info(cell->vertex(1)), + with_point_and_info(cell->vertex(2)), + with_point_and_info(cell->vertex(3))); } - auto indent_guards = CGAL::IO::make_indenting_guards(this->debug().regions() ? "| " : ""); + auto indent_guards = CGAL::IO::make_indenting_guards(this->debug().regions() ? "| " : "", std::cerr); bool does_intersect = false; for(int i = 0; i < 4; ++i) { const auto v0 = cell->vertex(i); @@ -5731,8 +5821,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // Dump intersecting edges information for(auto edge : intersecting_edges) { auto [v1, v2] = tr().vertices(edge); - std::cerr << cdt_3_format(" edge: {} {}\n", IO::oformat(v1, with_point_and_info), - IO::oformat(v2, with_point_and_info)); + std::cerr << cdt_3_format(" edge: {} {}\n", with_point_and_info(v1), with_point_and_info(v2)); } // Dump upper and lower cavity facets @@ -5755,7 +5844,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << " facet: "; const auto facet_vertices = tr().vertices(facet); for(auto v: facet_vertices) { - std::cerr << IO::oformat(v, with_point_and_info) << " "; + std::cerr << with_point_and_info(v) << " "; } // This assertion is wrong, because there might be only one half-cavity and not a full cavity. // CGAL_assertion(!std::all_of(facet_vertices.begin(), facet_vertices.end(), @@ -5779,18 +5868,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto to_exact = CGAL::Cartesian_converter(); const auto& cdt_2 = this->face_cdt_2(face_index); - auto of = [](auto&&... args) { - return IO::oformat(std::forward(args)..., with_point_and_info); - }; - std::cerr << cdt_3_format("intersecting edge #{}: ( {} {} )\n" "intersected triangle vertices: {}\n" " {}\n" " {}\n", - edge_index, of(v_above), of(v_below), - of(first_intersected_triangle_vertices[0]), - of(first_intersected_triangle_vertices[1]), - of(first_intersected_triangle_vertices[2])); + edge_index, with_point_and_info(v_above), with_point_and_info(v_below), + with_point_and_info(first_intersected_triangle_vertices[0]), + with_point_and_info(first_intersected_triangle_vertices[1]), + with_point_and_info(first_intersected_triangle_vertices[2])); dump_region(face_index, region_index, cdt_2); const auto p_above = this->point(v_above); @@ -5825,7 +5910,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D continue; } std::cerr << cdt_3_format("Test tetrahedron (#{}):\n {}\n {}\n {}\n {}\n", ch->time_stamp(), - of(ch->vertex(0)), of(ch->vertex(1)), of(ch->vertex(2)), of(ch->vertex(3))); + with_point_and_info(ch->vertex(0)), + with_point_and_info(ch->vertex(1)), + with_point_and_info(ch->vertex(2)), + with_point_and_info(ch->vertex(3))); bool does_intersect_region = std::invoke([&]() { for(int i = 0; i < 4; ++i) { for(int j = i + 1; j < 4; ++j) { @@ -5845,7 +5933,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D exception_ostream() << cdt_3_format( "ERROR: The following tetrahedron (#{}) does not intersect the region:\n {}\n {}\n {}\n {}", - ch->time_stamp(), of(ch->vertex(0)), of(ch->vertex(1)), of(ch->vertex(2)), of(ch->vertex(3))) + ch->time_stamp(), + with_point_and_info(ch->vertex(0)), + with_point_and_info(ch->vertex(1)), + with_point_and_info(ch->vertex(2)), + with_point_and_info(ch->vertex(3))) << std::endl; } } @@ -6310,7 +6402,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 { for(auto v : ccdt.triangulation().all_vertex_handles()) { switch(v->ccdt_3_data().vertex_type()) { - case CDT_3_vertex_type::CORNER: + case CDT_3_vertex_type::INPUT_VERTEX: v->set_dimension(0); v->set_index(0); break; diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h index 8e0a99803d83..2f8191b335d1 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h @@ -37,8 +37,7 @@ struct Conforming_constrained_Delaunay_triangulation_vertex_data_3 {}; enum class CDT_3_vertex_type : unsigned char { FREE, - CORNER, - INPUT_VERTEX = CORNER, + INPUT_VERTEX, STEINER_ON_EDGE, STEINER_IN_FACE, STEINER_IN_VOLUME, From 6598ec5f8d0806f89155500ad03ff3e3863fabcf Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 15 Jun 2026 15:02:47 +0200 Subject: [PATCH 064/151] Merge branch Tet_remeshing-for_CCDT_3-jtournois into CDT_3-Steiner_points_only_in_volume-GF --- .../Conforming_Delaunay_triangulation_3.h | 2 + ...ing_constrained_Delaunay_triangulation_3.h | 254 +++++++++++++++++- ...ained_Delaunay_triangulation_cell_base_3.h | 14 + ...ned_Delaunay_triangulation_vertex_base_3.h | 5 + ...ned_Delaunay_triangulation_vertex_data_3.h | 21 ++ Lab/demo/Lab/Plugins/CDT_3/CDT_3_plugin.cpp | 2 +- .../CGAL/Mesh_complex_3_in_triangulation_3.h | 6 +- SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h | 35 +-- .../internal/collapse_short_edges.h | 37 +-- .../internal/flip_edges.h | 34 +-- .../internal/peel_slivers.h | 4 +- .../internal/smooth_vertices.h | 2 +- .../internal/split_long_edges.h | 16 +- .../tetrahedral_adaptive_remeshing_impl.h | 26 +- .../internal/tetrahedral_remeshing_helpers.h | 125 +++++---- .../include/CGAL/tetrahedral_remeshing.h | 109 ++++++++ 16 files changed, 550 insertions(+), 142 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 8f8377dac707..c3c76dc3117c 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -908,6 +908,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { return false; } +public: Constrained_polyline_id constraint_from_extremities(Vertex_handle va, Vertex_handle vb) const { if(va->ccdt_3_data().number_of_incident_constraints() == 0 || vb->ccdt_3_data().number_of_incident_constraints() == 0) { @@ -927,6 +928,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { return c_id; } +protected: auto constraint_extremities(Constrained_polyline_id c_id) const { CGAL_assertion(this->constraint_hierarchy.is_valid_constraint(c_id)); if(debug().constraint_hierarchy()) { diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 54aedb6793d0..0159710a613c 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -657,16 +657,211 @@ class Conforming_constrained_Delaunay_triangulation_3 public: using Vertex_handle = typename Triangulation::Vertex_handle; - using Cell_handle = typename Triangulation::Cell_handle; + using Edge = typename Triangulation::Edge; using Facet = typename Triangulation::Facet; + using Cell_handle = typename Triangulation::Cell_handle; + + using Concurrency_tag = Triangulation::Concurrency_tag; #ifndef DOXYGEN_RUNNING using Constrained_polyline_id = typename CDT_3_impl::Constrained_polyline_id; #endif // not DOXYGEN_RUNNING using size_type = typename Triangulation::size_type; + using Subdomain_index = int; + using Surface_patch_index = CDT_3_signed_index; + using Curve_index = std::size_t;//Constrained_polyline_id::size_type; + using Corner_index = int;//check that + using Index = typename Triangulation::Triangulation_data_structure::Vertex::Index; + + void rescan_after_load_of_triangulation() { /*todo*/ } + + bool is_in_complex(const Cell_handle& c) const + { + for(const auto v : triangulation().vertices(c)) + { + if(impl().vertex_type(v) == CDT_3_vertex_type::BBOX) + return false; + } + return true; + } + bool is_in_complex(const Facet& f) const + { + return is_in_complex(f.first, f.second); + } + bool is_in_complex(const Cell_handle c, const int i) const + { + return is_facet_constrained({c, i}); + } + const bool is_in_complex(const Vertex_handle u, const Vertex_handle v) const + { + Constrained_polyline_id c_id = impl().constraint_from_extremities(u, v); + return (c_id != Constrained_polyline_id{}); + } + bool is_in_complex(const Edge& e) const + { + return is_in_complex(e.first->vertex(e.second), e.first->vertex(e.third)); + } + bool is_in_complex(const Vertex_handle& v) const + { + return impl().vertex_type(v) == CDT_3_vertex_type::INPUT_VERTEX; + } + Subdomain_index subdomain_index(const Cell_handle& c) const + { + return is_in_complex(c) ? Subdomain_index{1} : Subdomain_index(); + } + Surface_patch_index surface_patch_index(const Cell_handle c, const int i) const + { + return surface_patch_index({c, i}); + } + Surface_patch_index surface_patch_index(const Facet& f) const + { + Surface_patch_index index = face_constraint_index(f.first, f.second) + 1; + // valid face_constraint_index() starts at 0 + // Surface_patch_index 0 is for "not a surface" + return index; + } + void set_surface_patch_index(const Facet& f, const Surface_patch_index& index) + { + const Facet& mf = impl().mirror_facet(f); + + // warning : cdt2_index may be outdated + f.first->ccdt_3_data().set_face_constraint_index(f.second, index); + mf.first->ccdt_3_data().set_face_constraint_index(mf.second, index); + } + Curve_index curve_index(const Vertex_handle v1, const Vertex_handle v2) const + { + return impl().constraint_from_extremities(v1, v2).index(); + } + Curve_index curve_index(const Edge& e) const + { + return curve_index(e.first->vertex(e.second), e.first->vertex(e.third)); + } + Index index(const Vertex_handle& v) const + { + return 1;//not used in tetrahedral remeshing outside of sizing field + } + int in_dimension(const Vertex_handle& v) const + { + auto type = impl().vertex_type(v); + switch(type) + { + case CDT_3_vertex_type::INPUT_VERTEX : + return 0; + case CDT_3_vertex_type::STEINER_ON_EDGE: + return 1; + case CDT_3_vertex_type::STEINER_IN_FACE: + return 2; + case CDT_3_vertex_type::STEINER_IN_VOLUME: + case CDT_3_vertex_type::FREE: + return 3; + } + return -1; + } + + void add_to_complex(const Cell_handle c, const Subdomain_index& index) + { + CGAL_precondition(!triangulation().is_infinite(c)); + CGAL_precondition(index == Subdomain_index{1}); + CGAL_USE(index); + } + void set_subdomain_index(const Cell_handle, const Subdomain_index&) + { + ; + } + void add_to_complex(const Facet& f, const Surface_patch_index& index) + { + //todo : use index and cdt2_index too + impl().set_facet_constrained(f, index, {}); + set_surface_patch_index(f, index); + } + void add_to_complex(const Edge& e, const Curve_index& index) + { + add_to_complex(e.first->vertex(e.second), e.first->vertex(e.third), index); + } + void add_to_complex(const Vertex_handle& v, const Corner_index&) + { + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::INPUT_VERTEX); + } + void add_to_complex(const Vertex_handle v1, const Vertex_handle v2, const Curve_index& index) + { +// impl().insert_constrained_edge(v1, v2, false/*restore Delaunay*/); + } + void remove_from_complex(const Cell_handle c) + { + CGAL_precondition(!triangulation().is_infinite(c)); + } + void remove_from_complex(const Facet& f) + { + CGAL_precondition(is_in_complex(f.first, f.second)); + const Facet mf = impl().mirror_facet(f); + impl().set_facet_as_not_constrained(f); + impl().set_facet_as_not_constrained(mf); + } + void remove_from_complex(const Cell_handle c, const int i) + { + remove_from_complex({c, i}); + } + void remove_from_complex(const Edge& e) + { +// remove_from_complex(e.first->vertex(e.second), e.first->vertex(e.third)); + } + void remove_from_complex(const Vertex_handle u, const Vertex_handle v) + { + //should not happen during tetrahedral remeshing since we keep constrained edges + CGAL_error_msg("removing a constrained edge is not supported"); + } + + void set_index(Vertex_handle, const Index&) const + { + // nothing to do since index is not used in tetrahedral remeshing outside of sizing field + } + void set_dimension(Vertex_handle v, const int dim) const + { + switch (dim) + { + case 0: + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::INPUT_VERTEX); + break; + case 1: + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_ON_EDGE); + break; + case 2: + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_FACE); + break; + case 3: + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); + break; + }; + } + + auto edges_in_complex() const + { + return impl().edges_in_complex(); + } + auto facets_in_complex() const + { + auto in_complex = [this](const Facet& f) { return is_in_complex(f); }; + return boost::make_iterator_range( + boost::make_filter_iterator(in_complex, + impl().finite_facets_begin(), + impl().finite_facets_end()), + boost::make_filter_iterator(in_complex, + impl().finite_facets_end(), + impl().finite_facets_end())); + } + auto cells_in_complex() const + { + return impl().finite_cell_handles(); + } + std::size_t number_of_edges() const + { + const auto& [ebegin, eend] = edges_in_complex(); + return std::distance(ebegin, eend); + } + /// \cond SKIP_IN_MANUAL - CDT_3_impl& impl() { return cdt_impl; } + CDT_3_impl& impl() { return cdt_impl; } const CDT_3_impl& impl() const { return cdt_impl; } /// \endcond public: @@ -1030,6 +1225,15 @@ class Conforming_constrained_Delaunay_triangulation_3 /// @} // end constructors section + /** + * swaps `this` and `rhs` + */ + void swap(Conforming_constrained_Delaunay_triangulation_3& rhs) + { + std::swap(cdt_impl, rhs.cdt_impl); + } + + // ----------------------- // Accessors // ----------------------- @@ -1050,6 +1254,8 @@ class Conforming_constrained_Delaunay_triangulation_3 return cdt_impl; } + Triangulation& triangulation() & { return cdt_impl; } + /*! * \brief moves and returns the underlying triangulation, then clears the object. * @@ -1327,6 +1533,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D bool operator()(Facet f) const { return cdt.is_facet_constrained(f); } + bool operator()(Edge e) const { + return Constrained_polyline_id{} + != cdt.constraint_from_extremities(e.first->vertex(e.second), + e.first->vertex(e.third)); + } }; using Constrained_facets_iterator = boost::filter_iterator; @@ -1341,6 +1552,21 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D Constrained_facets_range constrained_facets() const { return {constrained_facets_begin(), constrained_facets_end()}; } + + + using Complex_edges_iterator = boost::filter_iterator; + using Complex_edges_range = CGAL::Iterator_range; + + Complex_edges_iterator edges_in_complex_begin() const { + return {Is_constrained{*this}, this->finite_edges_begin(), this->finite_edges_end()}; + } + Complex_edges_iterator edges_in_complex_end() const { + return {Is_constrained{*this}, this->finite_edges_end(), this->finite_edges_end()}; + } + Complex_edges_range edges_in_complex() const { + return { edges_in_complex_begin(), edges_in_complex_end()}; + } + private: struct CDT_2_types { @@ -1472,6 +1698,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return same_triple(f, f2, NEGATIVE); } +public: void set_facet_constrained(Facet f, CDT_3_signed_index polygon_constraint_id, CDT_2_face_handle fh, [[maybe_unused]] Orientation o = EQUAL) { @@ -1494,6 +1721,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return set_facet_constrained(f, INVALID_FACE_INDEX, CDT_2_face_handle{}); } +protected: void set_adjacency_between_3d_facets(Facet f1, Facet f2) const { tr().tds().set_adjacency(f1.first, f1.second, f2.first, f2.second); if(dbg().move_Steiner_vertices_level() > 1) { @@ -5624,14 +5852,20 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Point = typename T_3::Point_3; - this->insert(Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmin() - d)); - this->insert(Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmin() - d)); - this->insert(Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmax() + d)); - this->insert(Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmax() + d)); - this->insert(Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmin() - d)); - this->insert(Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmin() - d)); - this->insert(Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmax() + d)); - this->insert(Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmax() + d)); + std::vector bbox_points + = { Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmin() - d), + Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmin() - d), + Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmax() + d), + Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmax() + d), + Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmin() - d), + Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmin() - d), + Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmax() + d), + Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmax() + d)}; + for(const auto& p : bbox_points) + { + Vertex_handle v = this->insert(p); + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::BBOX); + } } void add_bbox_points_if_not_dimension_3() { diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h index f109feaed163..039912b48fc3 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h @@ -69,6 +69,20 @@ class Conforming_constrained_Delaunay_triangulation_cell_base_3 return ccdt_3_data_; } + ///////// for C3T3 : dummy implementation for compilation purposes + using Subdomain_index = int; + Subdomain_index subdomain_index() const { return 1; } // dummy value, not used in the implementation of CCDT + // todo : remove this function and the corresponding typedef, as they are not used in the implementation of CCDT, and + // are only here to satisfy the requirements of the concept `SimplicialMeshCellBase_3` + // for tetrahedral remeshing + // PLUS value 1 does not make any sense in the general case + void set_subdomain_index(Subdomain_index) {} + void reset_cache_validity() {} + bool is_cache_valid() const { return false;} + void set_sliver_value(double) {} + double sliver_value() const { return 0.; } + ///////// end of for C3T3 + static std::string io_signature() { static_assert( std::is_same_v< diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h index 30b1c1c54f12..846481eaadef 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h @@ -55,6 +55,8 @@ class Conforming_constrained_Delaunay_triangulation_vertex_base_3 using Base = Triangulation_simplex_base_with_time_stamp; using Base::Base; + using Index = int; + // model of ConformingConstrainedDelaunayTriangulationVertexBase_3 Conforming_constrained_Delaunay_triangulation_vertex_data_3& ccdt_3_data() { return ccdt_3_data_; } const Conforming_constrained_Delaunay_triangulation_vertex_data_3& ccdt_3_data() const { return ccdt_3_data_; } @@ -62,6 +64,9 @@ class Conforming_constrained_Delaunay_triangulation_vertex_base_3 static std::string io_signature() { return Get_io_signature()(); } + + int in_dimension() const { return ccdt_3_data().in_dimension(); } + void set_dimension(int dim) {} }; } // namespace CGAL diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h index 2f8191b335d1..b5cdc4ee7798 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h @@ -41,6 +41,7 @@ enum class CDT_3_vertex_type : unsigned char { STEINER_ON_EDGE, STEINER_IN_FACE, STEINER_IN_VOLUME, + BBOX, nb_of_types }; @@ -151,6 +152,26 @@ class Conforming_constrained_Delaunay_triangulation_vertex_data_3 { bool is_Steiner_vertex_on_edge() const { return m_vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; } bool is_Steiner_vertex_in_face() const { return m_vertex_type == CDT_3_vertex_type::STEINER_IN_FACE; } bool is_Steiner_vertex_in_volume() const { return m_vertex_type == CDT_3_vertex_type::STEINER_IN_VOLUME; } + + int in_dimension() const + { + switch(m_vertex_type) + { + case CDT_3_vertex_type::INPUT_VERTEX: + return 0; + case CDT_3_vertex_type::STEINER_ON_EDGE: + return 1; + case CDT_3_vertex_type::STEINER_IN_FACE: + return 2; + case CDT_3_vertex_type::STEINER_IN_VOLUME: + case CDT_3_vertex_type::FREE: + return 3; + default: + break; + } + return -1; // should not happen + } + }; #endif // DOXYGEN_RUNNING diff --git a/Lab/demo/Lab/Plugins/CDT_3/CDT_3_plugin.cpp b/Lab/demo/Lab/Plugins/CDT_3/CDT_3_plugin.cpp index ab33ceb8e31b..993aaca51ad4 100644 --- a/Lab/demo/Lab/Plugins/CDT_3/CDT_3_plugin.cpp +++ b/Lab/demo/Lab/Plugins/CDT_3/CDT_3_plugin.cpp @@ -37,7 +37,7 @@ struct Vertex_converter Vertex v; v.set_point(Wp{src.point()}); switch(src.ccdt_3_data().vertex_type()) { - case CDT_3_vertex_type::CORNER: + case CDT_3_vertex_type::INPUT_VERTEX: v.set_dimension(0); v.set_index(0); break; diff --git a/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h b/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h index 373de05e0644..4dfa3683bd0b 100644 --- a/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h +++ b/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h @@ -215,15 +215,15 @@ class Mesh_complex_3_in_triangulation_3 /*! Index type. */ - typedef typename Tr::Vertex::Index Index; + typedef typename Tr::Triangulation_data_structure::Vertex::Index Index; /*! Surface index type. */ - typedef typename Tr::Cell::Surface_patch_index Surface_patch_index; + typedef typename Tr::Triangulation_data_structure::Cell::Surface_patch_index Surface_patch_index; /*! Subdomain index type. */ - typedef typename Tr::Cell::Subdomain_index Subdomain_index; + typedef typename Tr::Triangulation_data_structure::Cell::Subdomain_index Subdomain_index; /*! Corner index type. */ diff --git a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h index a627a80c7f8f..9f80f79a4b0b 100644 --- a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h +++ b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h @@ -125,7 +125,6 @@ bool build_finite_cells(Tr& tr, { typedef typename Tr::Vertex_handle Vertex_handle; typedef typename Tr::Cell_handle Cell_handle; - typedef typename Tr::Cell::Surface_patch_index Surface_patch_index; bool success = true; @@ -159,9 +158,11 @@ bool build_finite_cells(Tr& tr, vs[j]->set_dimension(3); } +#ifndef CGAL_T3_ALLOW_NEGATIVE_VOLUME // this assertion also tests for degeneracy CGAL_assertion(orientation(cp(tr.point(vs[0])), cp(tr.point(vs[1])), cp(tr.point(vs[2])), cp(tr.point(vs[3]))) == POSITIVE); +#endif Cell_handle c = tr.tds().create_cell(vs[0], vs[1], vs[2], vs[3]); c->set_subdomain_index(subdomains[i]); // the cell's info keeps the reference of the tetrahedron @@ -197,21 +198,21 @@ bool build_finite_cells(Tr& tr, } while(facet[0] != n0); - typename FacetPatchMap::const_iterator it = border_facets.find(facet); - if(it != border_facets.end()) - { - c->set_surface_patch_index(j, it->second); - } - else - { - std::swap(facet[1], facet[2]); // facet[0] is still the smallest, no need to rotate again - - it = border_facets.find(facet); - if(it != border_facets.end()) - c->set_surface_patch_index(j, it->second); - else - c->set_surface_patch_index(j, Surface_patch_index()); - } +// typename FacetPatchMap::const_iterator it = border_facets.find(facet); +// if(it != border_facets.end()) +// { +// c->set_surface_patch_index(j, it->second); +// } +// else +// { +// std::swap(facet[1], facet[2]); // facet[0] is still the smallest, no need to rotate again +// +// it = border_facets.find(facet); +// if(it != border_facets.end()) +// c->set_surface_patch_index(j, it->second); +// else +// c->set_surface_patch_index(j, Surface_patch_index()); +// } } } } @@ -291,7 +292,7 @@ bool build_infinite_cells(Tr& tr, it->second.emplace_back(opp_c, 0); CGAL_assertion(it->second.size() == 2); - opp_c->set_surface_patch_index(0, c->surface_patch_index(i)); +// opp_c->set_surface_patch_index(0, c->surface_patch_index(i)); } #ifdef CGAL_TET_SOUP_TO_C3T3_DEBUG diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index d78a3092c1e4..58676683f7bb 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -714,7 +714,7 @@ bool are_edge_lengths_valid(const typename C3t3::Edge& edge, } else if (collapse_type == TO_MIDPOINT) { - new_index = max_dimension_index(v0, v1); + new_index = max_dimension_index(v0, v1, c3t3); new_dim = (std::max)(v0->in_dimension(), v1->in_dimension()); #ifdef CGAL_AVERAGE_SIZING_AFTER_COLLAPSE sizing_at_new_pos = sizing_at_midpoint(edge, new_dim, new_index, sizing, c3t3, cell_selector); @@ -783,12 +783,12 @@ void merge_surface_patch_indices(const typename C3t3::Facet& f1, if (in_cx_f1 && !in_cx_f2) { typename C3t3::Surface_patch_index patch = c3t3.surface_patch_index(f1); - f2.first->set_surface_patch_index(f2.second, patch); + c3t3.set_surface_patch_index(f2, patch); } else if (in_cx_f2 && !in_cx_f1) { typename C3t3::Surface_patch_index patch = c3t3.surface_patch_index(f2); - f1.first->set_surface_patch_index(f1.second, patch); + c3t3.set_surface_patch_index(f1, patch); } else if(in_cx_f1 && in_cx_f2) { @@ -796,7 +796,7 @@ void merge_surface_patch_indices(const typename C3t3::Facet& f1, typename C3t3::Surface_patch_index patch = c3t3.surface_patch_index(f2); c3t3.remove_from_complex(f2); - f2.first->set_surface_patch_index(f2.second, patch); + c3t3.set_surface_patch_index(f2, patch); } } @@ -1206,21 +1206,28 @@ auto can_be_collapsed(const typename C3T3::Edge& e, bool on_boundary; }; - const bool boundary = c3t3.is_in_complex(e) - || is_boundary(c3t3, e, cell_selector); + const bool in_cx = c3t3.is_in_complex(e); + if(in_cx && protect_boundaries) + return Collapsible{false, true /*boundary*/}; - if (protect_boundaries) - { - if (boundary) - return Collapsible{false, boundary}; + const bool boundary = is_boundary(c3t3, e, cell_selector); + if(boundary && protect_boundaries) + return Collapsible{false, boundary}; - return Collapsible{ is_internal(e, c3t3, cell_selector), boundary}; - } - else + if(!is_selected(e, c3t3.triangulation(), cell_selector)) + return Collapsible{false, boundary}; + + if(!boundary && !in_cx) { - return Collapsible{ is_selected(e, c3t3.triangulation(), cell_selector), - boundary }; + auto patch_v0 = surface_patch_index(e.first->vertex(e.second), c3t3); + auto patch_v1 = surface_patch_index(e.first->vertex(e.third), c3t3); + + if(patch_v0 != std::nullopt && patch_v1 != std::nullopt && patch_v0 != patch_v1) + return Collapsible{false, boundary}; } + +// if(!is_internal(e, c3t3, cell_selector)) + return Collapsible {true, boundary}; } templateset_surface_patch_index(f.second, patch); + c3t3.set_surface_patch_index(f, patch); else - f.first->set_surface_patch_index(f.second, Surface_patch_index()); + c3t3.set_surface_patch_index(f, Surface_patch_index()); } else { //we are inside the modified zone, c3t3 info is not valid anymore if (c3t3.is_in_complex(f) || c3t3.is_in_complex(mf)) { - f.first->set_surface_patch_index(f.second, Surface_patch_index()); - mf.first->set_surface_patch_index(mf.second, Surface_patch_index()); + c3t3.set_surface_patch_index(f, Surface_patch_index()); + c3t3.set_surface_patch_index(mf, Surface_patch_index()); } } } @@ -1114,15 +1114,15 @@ Sliver_removal_result find_best_flip(typename C3t3::Edge& edge, { vertices_around_edge.insert(vi); - if ( circ->first->subdomain_index() - != circ->first->neighbor(circ->second)->subdomain_index()) + if ( c3t3.subdomain_index(circ->first) + != c3t3.subdomain_index(circ->first->neighbor(circ->second))) { boundary_edge = true; boundary_vertices.insert(vi); } if ( tr.is_infinite(circ->first) - != tr.is_infinite(circ->first->neighbor(circ->second))) + != tr.is_infinite(circ->first->neighbor(circ->second))) { hull_edge = true; //hull_vertices.insert(vi); @@ -1534,8 +1534,8 @@ Sliver_removal_result flip_on_surface(C3T3& c3t3, ch2->set_vertex(ch2->index(vh2), vh3); ch1->set_vertex(ch1->index(vh0), vh1); - c3t3.add_to_complex(ch0, ch0->index(vh4), patch); - c3t3.add_to_complex(ch3, ch3->index(vh4), patch); + c3t3.add_to_complex({ch0, ch0->index(vh4)}, patch); + c3t3.add_to_complex({ch3, ch3->index(vh4)}, patch); for (Cell_handle chi : cells_around_edge) { @@ -1578,8 +1578,8 @@ Sliver_removal_result flip_on_surface(C3T3& c3t3, ci->vertex(j)->set_cell(ci); } - c3t3.add_to_complex(ch0, ch0->index(vh4), patch); - c3t3.add_to_complex(ch3, ch3->index(vh4), patch); + c3t3.add_to_complex({ch0, ch0->index(vh4)}, patch); + c3t3.add_to_complex({ch3, ch3->index(vh4)}, patch); for (Cell_handle chi : cells_around_edge) { @@ -1658,8 +1658,8 @@ Sliver_removal_result flip_on_surface(C3T3& c3t3, ch0->set_vertex(ch0->index(vh5), vh1); ch1->set_vertex(ch1->index(vh4), vh3); - c3t3.add_to_complex(ch0, ch0->index(vh2), patch); - c3t3.add_to_complex(ch1, ch1->index(vh2), patch); + c3t3.add_to_complex({ch0, ch0->index(vh2)}, patch); + c3t3.add_to_complex({ch1, ch1->index(vh2)}, patch); for (Cell_handle chi : cells_around_edge) { @@ -1708,8 +1708,8 @@ Sliver_removal_result flip_on_surface(C3T3& c3t3, ce->vertex(j)->set_cell(ce); } - c3t3.add_to_complex(ch0, ch0->index(vh2), patch); - c3t3.add_to_complex(ch1, ch1->index(vh2), patch); + c3t3.add_to_complex({ch0, ch0->index(vh2)}, patch); + c3t3.add_to_complex({ch1, ch1->index(vh2)}, patch); for (Cell_handle chi : cells_around_edge) { @@ -1866,12 +1866,12 @@ std::size_t flipBoundaryEdges( CGAL_expensive_assertion_code(bool b =) tr.tds().is_facet(vh2, vh3, vh0, c, li, lj, lk); CGAL_expensive_assertion(b); - c3t3.add_to_complex(c, (6 - li - lj - lk), surfi); + c3t3.add_to_complex({c, (6 - li - lj - lk)}, surfi); CGAL_expensive_assertion_code(b = ) tr.tds().is_facet(vh2, vh3, vh1, c, li, lj, lk); CGAL_expensive_assertion(b); - c3t3.add_to_complex(c, (6 - li - lj - lk), surfi); + c3t3.add_to_complex({c, (6 - li - lj - lk)}, surfi); CGAL_expensive_assertion_code(std::size_t nbf_post = std::distance(c3t3.facets_in_complex_begin(), diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h index 00d0e16f0a1e..792ef5495d06 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h @@ -96,9 +96,9 @@ std::size_t peel_slivers(C3T3& c3t3, for (int i = 0; i < 4; ++i) { if (f_on_surface[i]) - c3t3.remove_from_complex(c, i); + c3t3.remove_from_complex({c, i}); else - c3t3.add_to_complex(c, i, patch.value()); + c3t3.add_to_complex({c, i}, patch.value()); } c3t3.remove_from_complex(c); diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h index cf73ab88891c..ecdaaa0944cb 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h @@ -835,7 +835,7 @@ std::size_t smooth_vertices_on_surfaces(C3t3& c3t3, CGAL_assertion(si != Surface_patch_index()); CGAL_expensive_assertion_code(auto siv = surface_patch_index(v, c3t3)); - CGAL_expensive_assertion(si == siv); + CGAL_expensive_assertion(si == siv.value()); if (nb_neighbors > 1) { diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h index 8a53ed2ea9e4..78e51a0f899b 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h @@ -151,7 +151,8 @@ typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e, else if(nb_patches == 0) dimension = 3; else - CGAL_assertion(false);//e should be in complex + dimension = 1;//e is in complex +// CGAL_assertion(false);//e should be in complex } CGAL_assertion(dimension > 0); @@ -254,16 +255,15 @@ typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e, const Facet_info v_and_opp_patch = facets_info.at(mfi); // facet opposite to new_v (status wrt c3t3 is unchanged) - new_cell->set_surface_patch_index(new_cell->index(new_v), - mfi.first->surface_patch_index(mfi.second)); + c3t3.set_surface_patch_index({new_cell, new_cell->index(new_v)}, + c3t3.surface_patch_index(mfi)); // new half-facet (added or not to c3t3 depending on the stored surface patch index) if (Surface_patch_index() == v_and_opp_patch.patch_index_) - new_cell->set_surface_patch_index(new_cell->index(v_and_opp_patch.opp_vertex_), - Surface_patch_index()); + c3t3.set_surface_patch_index({new_cell, new_cell->index(v_and_opp_patch.opp_vertex_)}, + Surface_patch_index()); else - c3t3.add_to_complex(new_cell, - new_cell->index(v_and_opp_patch.opp_vertex_), + c3t3.add_to_complex({new_cell, new_cell->index(v_and_opp_patch.opp_vertex_)}, v_and_opp_patch.patch_index_); // newly created internal facet @@ -272,7 +272,7 @@ typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e, const Vertex_handle vi = new_cell->vertex(i); if (vi == v1 || vi == v2) { - new_cell->set_surface_patch_index(i, Surface_patch_index()); + c3t3.set_surface_patch_index({new_cell, i}, Surface_patch_index()); break; } } diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h index c60d95dc1d01..1a566f645d13 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h @@ -70,14 +70,14 @@ template > class Adaptive_remesher { typedef Triangulation Tr; + typedef typename Tr::Geom_traits Gt; typedef typename Tr::Geom_traits::FT FT; - typedef CGAL::Mesh_complex_3_in_triangulation_3 C3t3; - typedef typename C3t3::Cell_handle Cell_handle; typedef typename C3t3::Vertex_handle Vertex_handle; typedef typename C3t3::Edge Edge; @@ -344,7 +344,7 @@ class Adaptive_remesher { if (get(m_cell_selector, cit)) { - const Subdomain_index index = cit->subdomain_index(); + const Subdomain_index index = m_c3t3.subdomain_index(cit); if (m_c3t3.is_in_complex(cit)) m_c3t3.remove_from_complex(cit); @@ -372,15 +372,15 @@ class Adaptive_remesher for (const Facet& f : tr().finite_facets()) { const Facet mf = tr().mirror_facet(f); - const Subdomain_index s1 = f.first->subdomain_index(); - const Subdomain_index s2 = mf.first->subdomain_index(); + const Subdomain_index s1 = m_c3t3.subdomain_index(f.first); + const Subdomain_index s2 = m_c3t3.subdomain_index(mf.first); if (s1 != s2 || get(fcmap, f) || get(fcmap, mf) || (input_is_c3t3() && m_c3t3.is_in_complex(f)) - || (!input_is_c3t3() && f.first->is_facet_on_surface(f.second))) + || (!input_is_c3t3() && m_c3t3.is_in_complex(f))) { - Surface_patch_index patch = f.first->surface_patch_index(f.second); + Surface_patch_index patch = m_c3t3.surface_patch_index(f); if(patch == Surface_patch_index()) make_surface_patch_index(s1, s2, patch); @@ -449,7 +449,7 @@ class Adaptive_remesher m_c3t3.add_to_complex(vit, ++corner_id); set_dimension(vit, 0); - vit->set_index(corner_id); + //vit->set_index(corner_id); #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG ++nbv; @@ -554,7 +554,7 @@ class Adaptive_remesher = (std::numeric_limits::min)(); for (Cell_handle cit : tr().finite_cell_handles()) { - const Subdomain_index cid = cit->subdomain_index(); + const Subdomain_index cid = m_c3t3.subdomain_index(cit); if (cid > max_index && cid != Subdomain_index()) max_index = cid; } @@ -667,7 +667,9 @@ template + typename CurveIndex = int, + typename C3t3_or_CDT_3 + = CGAL::Mesh_complex_3_in_triangulation_3> struct Adaptive_remesher_type_generator { using Tr = Triangulation; @@ -711,7 +713,9 @@ struct Adaptive_remesher_type_generator >::type; using type = Adaptive_remesher< - Tr, SizingFunction, VCMap, ECMap, FCMap, SelectionFunctor, Visitor>; + Tr, SizingFunction, VCMap, ECMap, FCMap, SelectionFunctor, Visitor, + CornerIndex, CurveIndex, + C3t3_or_CDT_3>; }; }//end namespace internal diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index 73b8da185dba..60cf120d53ab 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -694,8 +694,8 @@ bool is_boundary(const C3T3& c3t3, << "\n\t in_complex = " << c3t3.is_in_complex(f) << "\n\t selector(f.first) = " << get(cell_selector, f.first) << "\n\t selector(mirror ) = " << get(cell_selector, mf.first) - << "\n\t subdomain(f.first)= " << f.first->subdomain_index() - << "\n\t subdomain(mirror) = " << mf.first->subdomain_index() + << "\n\t subdomain(f.first)= " << c3t3.subdomain_index(f.first) + << "\n\t subdomain(mirror) = " << c3t3.subdomain_index(mf.first) << std::endl; } @@ -806,8 +806,9 @@ bool is_boundary_vertex(const typename C3t3::Vertex_handle& v, } template -typename C3t3::Surface_patch_index surface_patch_index(const typename C3t3::Vertex_handle v, - const C3t3& c3t3) +std::optional +surface_patch_index(const typename C3t3::Vertex_handle v, + const C3t3& c3t3) { typedef typename C3t3::Surface_patch_index Surface_patch_index; typedef typename C3t3::Facet Facet; @@ -819,7 +820,7 @@ typename C3t3::Surface_patch_index surface_patch_index(const typename C3t3::Vert if (c3t3.is_in_complex(f)) return c3t3.surface_patch_index(f); } - return Surface_patch_index(); + return std::nullopt; } template @@ -828,18 +829,17 @@ void set_index(typename C3t3::Vertex_handle v, const C3t3& c3t3) switch (v->in_dimension()) { case 3: - v->set_index(v->cell()->subdomain_index()); + c3t3.set_index(v, c3t3.subdomain_index(v->cell())); break; case 2: - CGAL_expensive_assertion(surface_patch_index(v, c3t3) - != typename C3t3::Surface_patch_index()); - v->set_index(surface_patch_index(v, c3t3)); + CGAL_expensive_assertion(surface_patch_index(v, c3t3) != std::nullopt); + c3t3.set_index(v, surface_patch_index(v, c3t3).value()); break; case 1: - v->set_index(typename C3t3::Curve_index(1)); + c3t3.set_index(v, typename C3t3::Curve_index(1)); break; case 0: - v->set_index(Mesh_3::internal::get_index(v->index())); + c3t3.set_index(v, Mesh_3::internal::get_index(c3t3.index(v))); break; case -1://far points from concurrent Mesh_3 break; @@ -895,8 +895,8 @@ OutputIterator incident_subdomains(const typename C3t3::Vertex_handle v, std::vector cells; c3t3.triangulation().incident_cells(v, std::back_inserter(cells)); - for (std::size_t i = 0; i < cells.size(); ++i) - *oit++ = cells[i]->subdomain_index(); + for (Cell_handle ci : cells) + *oit++ = c3t3.subdomain_index(ci); return oit; } @@ -912,7 +912,7 @@ OutputIterator incident_subdomains(const typename C3t3::Edge& e, Cell_circulator end = circ; do { - *oit++ = circ->subdomain_index(); + *oit++ = c3t3.subdomain_index(circ); } while (++circ != end); return oit; @@ -1190,20 +1190,23 @@ bool is_internal(const typename C3t3::Edge& edge, Cell_circulator circ = c3t3.triangulation().incident_cells(edge); Cell_circulator done = circ; - const typename C3t3::Subdomain_index si = circ->subdomain_index(); + const typename C3t3::Subdomain_index si = c3t3.subdomain_index(circ); do { if (c3t3.triangulation().is_infinite(circ)) return false; - if (si != circ->subdomain_index()) + if (si != c3t3.subdomain_index(circ)) return false; if (!get(cell_selector, circ)) return false; - if (c3t3.is_in_complex( - circ, - CGAL::Triangulation_utils_3::next_around_edge(circ->index(vs), circ->index(vt)))) + + Cell_circulator ch = circ; + Cell_circulator next_circ = ++circ; + if(c3t3.is_in_complex(ch, ch->index(next_circ))) return false; - } while (++circ != done); + circ = next_circ; + } + while (circ != done); return true; } @@ -1342,7 +1345,7 @@ auto size_at_centroid(const typename C3t3::Cell_handle c, CGAL_assertion(c3t3.is_in_complex(c)); const auto cc = CGAL::centroid(c3t3.triangulation().tetrahedron(c)); - return sizing(cc, 3, c->subdomain_index()); + return sizing(cc, 3, c3t3.subdomain_index(c)); } template @@ -1421,7 +1424,7 @@ auto sizing_at_vertex(const Vertex_handle v, const C3t3& c3t3, const Cell_selector& cell_selector) { - auto size = sizing(point(v->point()), v->in_dimension(), v->index()); + auto size = sizing(point(v->point()), c3t3.in_dimension(v), c3t3.index(v)); if(v->in_dimension() < 3 && size == 0) { @@ -1455,8 +1458,8 @@ auto sizing_at_midpoint(const typename C3t3::Edge& e, { const auto [u, v] = make_vertex_pair(e); - const FT size_at_u = sizing(cp(u->point()), u->in_dimension(), u->index()); - const FT size_at_v = sizing(cp(v->point()), v->in_dimension(), v->index()); + const FT size_at_u = sizing(cp(u->point()), u->in_dimension(), c3t3.index(u)); + const FT size_at_v = sizing(cp(v->point()), v->in_dimension(), c3t3.index(v)); if (size_at_u == 0. || size_at_v == 0.) return average_sizing_in_incident_cells(e, sizing, c3t3, cell_selector); @@ -1519,23 +1522,26 @@ auto min_sizing_in_incident_cells(const typename C3t3::Edge& e, return size_at_uv; } -template +template auto -max_dimension_index(const Vertex_handle v0, const Vertex_handle v1) +max_dimension_index(const typename C3t3::Vertex_handle v0, + const typename C3t3::Vertex_handle v1, + const C3t3& c3t3) { - const int dim0 = v0->in_dimension(); - const int dim1 = v1->in_dimension(); + const int dim0 = c3t3.in_dimension(v0); + const int dim1 = c3t3.in_dimension(v1); - if (dim0 > dim1) return v0->index(); - else if (dim1 > dim0) return v1->index(); - else return v0->index(); //arbitrary choice, any of the two should be fine + if (dim0 > dim1) return c3t3.index(v0); + else if (dim1 > dim0) return c3t3.index(v1); + else return c3t3.index(v0); //arbitrary choice, any of the two should be fine } -template +template auto -max_dimension_index(const std::array& vs) +max_dimension_index(const std::array& vs, + const C3t3& c3t3) { - return max_dimension_index(vs[0], vs[1]); + return max_dimension_index(vs[0], vs[1], c3t3); } template @@ -1552,15 +1558,15 @@ approximate_edge_length(const typename Tr::Edge& e, const Tr& tr) return CGAL::approximate_sqrt(squared_edge_length(e, tr)); } -template -typename Tr::Cell::Subdomain_index +template +typename C3t3::Subdomain_index subdomain_index_at_point_3(const Pt& p, - const typename Tr::Cell_handle hint, - const Tr& tr) + const typename C3t3::Cell_handle hint, + const C3t3& c3t3) { - const typename Tr::Point tr_p(p); - const typename Tr::Cell_handle c = tr.locate(tr_p, hint); - return c->subdomain_index(); + const typename C3t3::Triangulation::Point tr_p(p); + const typename C3t3::Cell_handle c = c3t3.triangulation().locate(tr_p, hint); + return c3t3.subdomain_index(c); } template @@ -1591,8 +1597,8 @@ auto midpoint_with_info(const typename C3t3::Edge& e, ? (std::max)(u->in_dimension(), v->in_dimension()) : 3; const Index midpoint_index = boundary_edge - ? max_dimension_index(c3t3.triangulation().vertices(e)) - : subdomain_index_at_point_3(midpoint_pt, e.first, c3t3.triangulation()); + ? max_dimension_index(c3t3.triangulation().vertices(e), c3t3) + : subdomain_index_at_point_3(midpoint_pt, e.first, c3t3); return Midpoint_with_info{midpoint_pt, midpoint_dim, midpoint_index}; } @@ -1617,8 +1623,8 @@ squared_upper_size_bound(const typename C3t3::Edge& e, const Vertex_handle u = e.first->vertex(e.second); const Vertex_handle v = e.first->vertex(e.third); - const FT size_at_u = sizing(cp(u->point()), u->in_dimension(), u->index()); - const FT size_at_v = sizing(cp(v->point()), v->in_dimension(), v->index()); + const FT size_at_u = sizing(cp(u->point()), u->in_dimension(), c3t3.index(u)); + const FT size_at_v = sizing(cp(v->point()), v->in_dimension(), c3t3.index(v)); // if e is on the boundary AND sizing at the boundary is set to 0, // we take the maximum size of the incident cells @@ -1674,8 +1680,8 @@ squared_lower_size_bound(const typename C3t3::Edge& e, const Vertex_handle u = e.first->vertex(e.second); const Vertex_handle v = e.first->vertex(e.third); - const FT size_at_u = sizing(point(u->point()), u->in_dimension(), u->index()); - const FT size_at_v = sizing(point(v->point()), v->in_dimension(), v->index()); + const FT size_at_u = sizing(point(u->point()), c3t3.in_dimension(u), c3t3.index(u)); + const FT size_at_v = sizing(point(v->point()), c3t3.in_dimension(v), c3t3.index(v)); // if e is on the boundary AND sizing at the boundary is set to 0, // we take the minimum size of the incident cells @@ -1981,7 +1987,7 @@ namespace internal if (Subdomain_index() != subdomain) c3t3.add_to_complex(c, subdomain); else - c->set_subdomain_index(Subdomain_index()); + c3t3.set_subdomain_index(c, Subdomain_index()); //update cell_selector property map put(cell_selector, c, selected); @@ -2101,7 +2107,7 @@ void check_surface_patch_indices(const C3t3& c3t3) { if (v->in_dimension() != 2) continue; - CGAL_expensive_assertion(surface_patch_index(v, c3t3) != typename C3t3::Surface_patch_index()); + CGAL_expensive_assertion(surface_patch_index(v, c3t3) != std::nullopt); } } @@ -2120,6 +2126,9 @@ void count_far_points(const C3t3& c3t3) template bool are_cell_orientations_valid(const Tr& tr) { +#ifdef CGAL_T3_ALLOW_NEGATIVE_VOLUME + return true; +#else typedef typename Tr::Geom_traits::Point_3 Point_3; typedef typename Tr::Facet Facet; @@ -2147,6 +2156,7 @@ bool are_cell_orientations_valid(const Tr& tr) dump_facets(facets, "cells_with_negative_volume.polylines.txt"); } return facets.empty(); +#endif } template @@ -2595,20 +2605,21 @@ template void dump_medit(const C3t3& c3t3, const char* filename) { std::ofstream os(filename, std::ios::out); - c3t3.output_to_medit(os, true, true); + //CGAL::IO::write_MEDIT(os, c3t3.triangulation()); + //c3t3.output_to_medit(os, true, true); os.close(); } template void dump_c3t3(const C3t3& c3t3, const char* filename_no_extension) { - std::string filename_medit(filename_no_extension); - filename_medit.append(".mesh"); - dump_medit(c3t3, filename_medit.c_str()); - - std::string filename_binary(filename_no_extension); - filename_binary.append(".binary.cgal"); - dump_binary(c3t3, filename_binary.c_str()); +// std::string filename_medit(filename_no_extension); +// filename_medit.append(".mesh"); +// dump_medit(c3t3, filename_medit.c_str()); +// +// std::string filename_binary(filename_no_extension); +// filename_binary.append(".binary.cgal"); +// dump_binary(c3t3, filename_binary.c_str()); } diff --git a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h index 0f2ebf097156..29304df21112 100644 --- a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h +++ b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h @@ -535,6 +535,115 @@ void tetrahedral_isotropic_remeshing( #endif } +//////////////////////////////////////////////////////// +/////// CONFORMING_CONSTRAINED_TRIANGULATION_3 ///////// +//////////////////////////////////////////////////////// + +template > +#endif + > +void tetrahedral_isotropic_remeshing(CDT_3& cdt3, + const SizingFunction& sizing, + const NamedParameters& np = parameters::default_values()) +{ + CGAL_expensive_assertion(cdt3.triangulation().tds().is_valid()); + + using parameters::choose_parameter; + using parameters::get_parameter; + + using Tr = typename CDT_3::Triangulation; + using Remesher_types = + Tetrahedral_remeshing::internal::Adaptive_remesher_type_generator; + + bool remesh_surfaces = choose_parameter(get_parameter(np, internal_np::remesh_boundaries), true); + bool protect = !remesh_surfaces; + std::size_t max_it = choose_parameter(get_parameter(np, internal_np::number_of_iterations), 1); + + bool smooth_constrained_edges = choose_parameter(get_parameter(np, internal_np::smooth_constrained_edges), false); + + auto cell_select = choose_parameter(get_parameter(np, internal_np::cell_selector), + typename Remesher_types::Default_Selection_functor()); + + auto vcmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), + typename Remesher_types::Default_VCMap(false)); + + auto ecmap = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), + typename Remesher_types::Default_ECMap(false)); + + auto fcmap = choose_parameter(get_parameter(np, internal_np::facet_is_constrained), + typename Remesher_types::Default_FCMap(false)); + + // Advanced and non documented parameters + auto visitor = choose_parameter(get_parameter(np, internal_np::visitor), typename Remesher_types::Default_Visitor()); + + auto nb_extra_iterations = + choose_parameter(get_parameter(np, internal_np::nb_flip_smooth_iterations), std::size_t{3}); + +#ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE + std::cout << "Tetrahedral remeshing (" + << "nb_iter = " << max_it << ", " + << "protect = " << std::boolalpha << protect << ")" << std::endl; + + std::cout << "Init tetrahedral remeshing..."; + std::cout.flush(); +#endif + + using Remesher = typename Remesher_types::type; + Remesher remesher(cdt3, sizing, protect, vcmap, ecmap, fcmap, smooth_constrained_edges, cell_select, visitor); + +#ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE + std::cout << "done." << std::endl; + Tetrahedral_remeshing::internal::compute_statistics(remesher.tr(), cell_select, "statistics_begin.txt"); +#endif + + // perform remeshing +#ifdef CGAL_TETRAHEDRAL_REMESHING_NO_EXTRA_ITERATIONS + nb_extra_iterations = 0; +#endif + + remesher.remesh(max_it, nb_extra_iterations); + +#ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG + const double angle_bound = 5.0; + Tetrahedral_remeshing::debug::dump_cells_with_small_dihedral_angle(cdt3.triangulation(), angle_bound, cell_select, + "bad_cells.mesh"); +#endif +#ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG + Tetrahedral_remeshing::internal::compute_statistics(cdt3.triangulation(), cell_select, "statistics_end.txt"); +#endif +} + +template > +#endif + > +void tetrahedral_isotropic_remeshing(CDT_3& cdt3, + const FT target_edge_length, + const NamedParameters& np = parameters::default_values()) +{ + using P = typename CDT_3::Triangulation::Geom_traits::Point_3; + using Index = int;//typename CDT_3::Triangulation::Vertex::Index; + using Sizing_FT = typename CDT_3::Triangulation::Geom_traits::FT; + + const Sizing_FT target_ft = static_cast(target_edge_length); + + tetrahedral_isotropic_remeshing(cdt3, + [target_ft](const P&, const int&, const Index&) + { return target_ft; }, + np); +} + + }//end namespace CGAL #endif //CGAL_TETRAHEDRAL_REMESHING_H From 0e67ea18f09f579eb03a19e036c6665afe8f3524 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 15 Jun 2026 15:06:57 +0200 Subject: [PATCH 065/151] fix warnings --- ...onforming_constrained_Delaunay_triangulation_3.h | 13 +++++++------ ...nstrained_Delaunay_triangulation_vertex_base_3.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 0159710a613c..23fc84dd3608 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -693,7 +693,7 @@ class Conforming_constrained_Delaunay_triangulation_3 { return is_facet_constrained({c, i}); } - const bool is_in_complex(const Vertex_handle u, const Vertex_handle v) const + bool is_in_complex(const Vertex_handle u, const Vertex_handle v) const { Constrained_polyline_id c_id = impl().constraint_from_extremities(u, v); return (c_id != Constrained_polyline_id{}); @@ -737,7 +737,7 @@ class Conforming_constrained_Delaunay_triangulation_3 { return curve_index(e.first->vertex(e.second), e.first->vertex(e.third)); } - Index index(const Vertex_handle& v) const + Index index(const Vertex_handle&) const { return 1;//not used in tetrahedral remeshing outside of sizing field } @@ -754,9 +754,10 @@ class Conforming_constrained_Delaunay_triangulation_3 return 2; case CDT_3_vertex_type::STEINER_IN_VOLUME: case CDT_3_vertex_type::FREE: + case CDT_3_vertex_type::BBOX: return 3; } - return -1; + CGAL_unreachable(); } void add_to_complex(const Cell_handle c, const Subdomain_index& index) @@ -783,7 +784,7 @@ class Conforming_constrained_Delaunay_triangulation_3 { v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::INPUT_VERTEX); } - void add_to_complex(const Vertex_handle v1, const Vertex_handle v2, const Curve_index& index) + void add_to_complex(const Vertex_handle, const Vertex_handle, const Curve_index&) { // impl().insert_constrained_edge(v1, v2, false/*restore Delaunay*/); } @@ -802,11 +803,11 @@ class Conforming_constrained_Delaunay_triangulation_3 { remove_from_complex({c, i}); } - void remove_from_complex(const Edge& e) + void remove_from_complex(const Edge&) { // remove_from_complex(e.first->vertex(e.second), e.first->vertex(e.third)); } - void remove_from_complex(const Vertex_handle u, const Vertex_handle v) + void remove_from_complex(const Vertex_handle, const Vertex_handle) { //should not happen during tetrahedral remeshing since we keep constrained edges CGAL_error_msg("removing a constrained edge is not supported"); diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h index 846481eaadef..3584b7613c42 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h @@ -66,7 +66,7 @@ class Conforming_constrained_Delaunay_triangulation_vertex_base_3 } int in_dimension() const { return ccdt_3_data().in_dimension(); } - void set_dimension(int dim) {} + void set_dimension(int) {} }; } // namespace CGAL From 72d52acec7c29d564ede2a43bf148210dd2a3875 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 15 Jun 2026 17:02:27 +0200 Subject: [PATCH 066/151] fix is_Steiner_vertex_on_surface --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 23fc84dd3608..fbcb2fb5820c 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -3639,6 +3639,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D case CDT_3_vertex_type::INPUT_VERTEX: case CDT_3_vertex_type::FREE: case CDT_3_vertex_type::STEINER_IN_VOLUME: + case CDT_3_vertex_type::BBOX: return false; case CDT_3_vertex_type::STEINER_IN_FACE: case CDT_3_vertex_type::STEINER_ON_EDGE: From 5e3a2e54d0b7c05c666fe22ef0554c72681b414d Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 16 Jun 2026 10:23:17 +0200 Subject: [PATCH 067/151] fix a warning --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index fbcb2fb5820c..ef166a382f10 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -756,6 +756,8 @@ class Conforming_constrained_Delaunay_triangulation_3 case CDT_3_vertex_type::FREE: case CDT_3_vertex_type::BBOX: return 3; + case CDT_3_vertex_type::nb_of_types: + CGAL_unreachable(); } CGAL_unreachable(); } From e43f10ecdc98794ec8d48368eda1c70dafc88dba Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 16 Jun 2026 11:13:28 +0200 Subject: [PATCH 068/151] fix a runtime error --- ...rming_constrained_Delaunay_triangulation_vertex_data_3.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h index b5cdc4ee7798..bb059618636f 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h @@ -158,6 +158,7 @@ class Conforming_constrained_Delaunay_triangulation_vertex_data_3 { switch(m_vertex_type) { case CDT_3_vertex_type::INPUT_VERTEX: + case CDT_3_vertex_type::BBOX: return 0; case CDT_3_vertex_type::STEINER_ON_EDGE: return 1; @@ -166,10 +167,9 @@ class Conforming_constrained_Delaunay_triangulation_vertex_data_3 { case CDT_3_vertex_type::STEINER_IN_VOLUME: case CDT_3_vertex_type::FREE: return 3; - default: - break; + case CDT_3_vertex_type::nb_of_types: + CGAL_unreachable(); } - return -1; // should not happen } }; From 63359b4266c1df43d08bd6e83d2cbef672d0eb79 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 16 Jun 2026 11:14:57 +0200 Subject: [PATCH 069/151] fix support for CLI11 --- .../CMakeLists.txt | 25 ++++++++++++------- .../cdt_3_from_off.cpp | 10 ++++---- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index 608491476ace..52601668847d 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -13,27 +13,34 @@ include(CGAL_Eigen3_support) set(CMAKE_CXX_SCAN_FOR_MODULES FALSE) # CDT_3: C++20 for structured bindings -add_library(CDT_3_dependencies INTERFACE) -target_compile_features(CDT_3_dependencies INTERFACE cxx_std_20) -target_link_libraries(CDT_3_dependencies INTERFACE CGAL::CGAL CGAL::Data CGAL::Eigen3_support) +add_library(CGAL_CDT_3_dependencies INTERFACE) +target_compile_features(CGAL_CDT_3_dependencies INTERFACE cxx_std_20) +target_link_libraries(CGAL_CDT_3_dependencies INTERFACE CGAL::CGAL CGAL::Data CGAL::Eigen3_support) + +find_package(CLI11 QUIET) +add_library(CGAL_CDT_3_CLI11 INTERFACE) +if(TARGET CLI11::CLI11) + target_link_libraries(CGAL_CDT_3_CLI11 INTERFACE CLI11::CLI11) + target_compile_definitions(CGAL_CDT_3_CLI11 INTERFACE CGAL_USE_CLI11) +endif() create_single_source_cgal_program( "cdt_test_insert_constrained_edge_from_EDG_file.cpp") -target_link_libraries(cdt_test_insert_constrained_edge_from_EDG_file PRIVATE CDT_3_dependencies) +target_link_libraries(cdt_test_insert_constrained_edge_from_EDG_file PRIVATE CGAL_CDT_3_dependencies) create_single_source_cgal_program( "cdt_test_insert_constrained_edge_from_OFF_file.cpp") -target_link_libraries(cdt_test_insert_constrained_edge_from_OFF_file PRIVATE CDT_3_dependencies) +target_link_libraries(cdt_test_insert_constrained_edge_from_OFF_file PRIVATE CGAL_CDT_3_dependencies) create_single_source_cgal_program( "cdt_3_from_off.cpp") -target_link_libraries(cdt_3_from_off PRIVATE CDT_3_dependencies) +target_link_libraries(cdt_3_from_off PRIVATE CGAL_CDT_3_dependencies CGAL_CDT_3_CLI11) create_single_source_cgal_program( "cdt_3_from_off_with_Epeck.cpp") -target_link_libraries(cdt_3_from_off_with_Epeck PRIVATE CDT_3_dependencies) +target_link_libraries(cdt_3_from_off_with_Epeck PRIVATE CGAL_CDT_3_dependencies CGAL_CDT_3_CLI11) create_single_source_cgal_program( "snap_and_cdt3.cpp") add_executable(test_CDT_3_insert_constrained_edge_from_EDG_file cdt_test_insert_constrained_edge_from_EDG_file.cpp) -target_link_libraries(test_CDT_3_insert_constrained_edge_from_EDG_file PRIVATE CDT_3_dependencies) +target_link_libraries(test_CDT_3_insert_constrained_edge_from_EDG_file PRIVATE CGAL_CDT_3_dependencies) target_compile_definitions(test_CDT_3_insert_constrained_edge_from_EDG_file PUBLIC CGAL_TEST_CDT_3_USE_CDT) cgal_add_test(test_CDT_3_insert_constrained_edge_from_EDG_file) add_executable(test_CDT_3_insert_constrained_edge_from_OFF_file cdt_test_insert_constrained_edge_from_OFF_file.cpp) -target_link_libraries(test_CDT_3_insert_constrained_edge_from_OFF_file PRIVATE CDT_3_dependencies) +target_link_libraries(test_CDT_3_insert_constrained_edge_from_OFF_file PRIVATE CGAL_CDT_3_dependencies) target_compile_definitions(test_CDT_3_insert_constrained_edge_from_OFF_file PUBLIC CGAL_TEST_CDT_3_USE_CDT) cgal_add_test(test_CDT_3_insert_constrained_edge_from_OFF_file) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index b00615d611b2..09e2394e53f8 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -33,11 +33,10 @@ #include #include +#include #include #include -#include -#include #include #include #include @@ -61,9 +60,8 @@ # include #endif -#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L -# define CGAL_CDT_3_CAN_USE_CXX20_RANGES 1 -# include +#if CGAL_USE_CLI11 +# include #endif #ifndef CGAL_CDT_3_USE_EPECK @@ -145,6 +143,7 @@ struct CDT_options }; CDT_options::CDT_options(int argc, char* argv[]) { +#if CGAL_USE_CLI11 CLI::App app{"Build and process a constrained Delaunay triangulation from an OFF mesh"}; app.set_help_flag("-h,--help", "print this help"); app.allow_extras(false); @@ -258,6 +257,7 @@ CDT_options::CDT_options(int argc, char* argv[]) { if(merge_facets_old_method) { merge_facets = true; } +#endif // CGAL_USE_CLI11 } CGAL::CDT_3::Debug_options cdt_debug_options(const CDT_options& options) { From 50267fc2dce3207f5706b7b1ff51233d896d9720 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 16 Jun 2026 11:32:35 +0200 Subject: [PATCH 070/151] fix warnings --- SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h | 2 ++ .../internal/tetrahedral_remeshing_helpers.h | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h index 9f80f79a4b0b..904217404a60 100644 --- a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h +++ b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h @@ -128,10 +128,12 @@ bool build_finite_cells(Tr& tr, bool success = true; +#ifndef CGAL_T3_ALLOW_NEGATIVE_VOLUME CGAL_assertion_code( typename Tr::Geom_traits::Construct_point_3 cp = tr.geom_traits().construct_point_3_object(); typename Tr::Geom_traits::Orientation_3 orientation = tr.geom_traits().orientation_3_object(); ) +#endif typename SubdomainsRange::value_type max_domain = 0; if(replace_domain_0) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index 60cf120d53ab..0e0f43338ae9 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -810,7 +810,6 @@ std::optional surface_patch_index(const typename C3t3::Vertex_handle v, const C3t3& c3t3) { - typedef typename C3t3::Surface_patch_index Surface_patch_index; typedef typename C3t3::Facet Facet; std::vector facets; c3t3.triangulation().incident_facets(v, std::back_inserter(facets)); @@ -1183,9 +1182,6 @@ bool is_internal(const typename C3t3::Edge& edge, const C3t3& c3t3, CellSelector cell_selector) { - const typename C3t3::Vertex_handle vs = edge.first->vertex(edge.second); - const typename C3t3::Vertex_handle vt = edge.first->vertex(edge.third); - typedef typename C3t3::Triangulation::Cell_circulator Cell_circulator; Cell_circulator circ = c3t3.triangulation().incident_cells(edge); Cell_circulator done = circ; @@ -2124,7 +2120,7 @@ void count_far_points(const C3t3& c3t3) } template -bool are_cell_orientations_valid(const Tr& tr) +bool are_cell_orientations_valid([[maybe_unused]] const Tr& tr) { #ifdef CGAL_T3_ALLOW_NEGATIVE_VOLUME return true; From 62e28b2212837c76b60bf23b93c5922cfabd3848 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 16 Jun 2026 11:33:15 +0200 Subject: [PATCH 071/151] take the whole bbox as the domain --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index ef166a382f10..518598d503d1 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -678,6 +678,7 @@ class Conforming_constrained_Delaunay_triangulation_3 bool is_in_complex(const Cell_handle& c) const { + return !triangulation().is_infinite(c); for(const auto v : triangulation().vertices(c)) { if(impl().vertex_type(v) == CDT_3_vertex_type::BBOX) From 1d22cefd7a3c58ac7b38ad751209b14cc0e1487a Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 16 Jun 2026 11:43:17 +0200 Subject: [PATCH 072/151] WIP CDT_3+tet remeshing --- .../Constrained_triangulation_3/cdt_3_from_off.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 09e2394e53f8..59eb8ad03b27 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -1,5 +1,7 @@ #include +#define CGAL_T3_ALLOW_NEGATIVE_VOLUME 1 +#define CGAL_TETRAHEDRAL_REMESHING_VERBOSE 1 // #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1 #include #include @@ -21,6 +23,7 @@ #include #include #include +#include #include #include @@ -705,7 +708,7 @@ int go(Mesh mesh, CDT_options options) { if(!options.quiet) { std::cout << "[timings] built CDT from mesh in " << milliseconds_since(build_start) << " ms\n"; - std::cout << cdt.statistics() << "\n\n"; + std::cout << cdt.statistics() << "\n"; } // Validation: check conforming status and validity @@ -720,10 +723,17 @@ int go(Mesh mesh, CDT_options options) { auto move_vertices_guard = CGAL::CDT_3_MOVE_STEINER_VERTICES_TASK_guard(); cdt.move_Steiner_vertices_to_the_volume(); if(!options.quiet) { - std::cout << "\n[timings] moved Steiner vertices to the volume in " + std::cout << "[timings] moved Steiner vertices to the volume in " << move_vertices_guard.time_ms() << " ms\n"; std::cout << cdt.statistics() << "\n"; } + CGAL::tetrahedral_isotropic_remeshing( + cdt, + bbox_max_span, + CGAL::parameters::number_of_iterations(30) + .remesh_boundaries(false)); + // CGAL_assertion(cdt.tr().tds().is_valid(true)); + std::cout << cdt.statistics() << "\n"; } return EXIT_SUCCESS; From dbd604189050c63975aecef3bd55f9aadab9d2eb Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 16 Jun 2026 15:14:40 +0200 Subject: [PATCH 073/151] fix compilation errors on Windows --- ...ing_constrained_Delaunay_triangulation_3.h | 2 +- .../include/CGAL/IO/Indenting_ostream.h | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 518598d503d1..c7b34166b6e0 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -661,7 +661,7 @@ class Conforming_constrained_Delaunay_triangulation_3 using Facet = typename Triangulation::Facet; using Cell_handle = typename Triangulation::Cell_handle; - using Concurrency_tag = Triangulation::Concurrency_tag; + using Concurrency_tag = typename Triangulation::Concurrency_tag; #ifndef DOXYGEN_RUNNING using Constrained_polyline_id = typename CDT_3_impl::Constrained_polyline_id; diff --git a/Stream_support/include/CGAL/IO/Indenting_ostream.h b/Stream_support/include/CGAL/IO/Indenting_ostream.h index 59a01ff325f9..d7aee11780a8 100644 --- a/Stream_support/include/CGAL/IO/Indenting_ostream.h +++ b/Stream_support/include/CGAL/IO/Indenting_ostream.h @@ -325,14 +325,15 @@ using Indenting_wstream_guard = Basic_indenting_stream_guard; * std::cerr << "Indented error\n"; * \endcode */ + /// @{ template auto make_indenting_guards(int spaces_per_level, Streams&... streams) { - if constexpr (sizeof...(Streams) == 0) { - return make_indenting_guards(spaces_per_level, std::cout, std::cerr, std::clog); - } else { - return std::make_tuple(Basic_indenting_stream_guard(streams, spaces_per_level)...); - } + return std::make_tuple(Basic_indenting_stream_guard(streams, spaces_per_level)...); +} +inline auto make_indenting_guards(int spaces_per_level) { + return make_indenting_guards(spaces_per_level, std::cout, std::cerr, std::clog); } +/// @} /** * \ingroup PkgStreamSupportRef @@ -351,14 +352,15 @@ auto make_indenting_guards(int spaces_per_level, Streams&... streams) { * * \sa Basic_indenting_stream_guard */ + /// @{ template auto make_indenting_guards(const std::string& indent_string, Streams&... streams) { - if constexpr (sizeof...(Streams) == 0) { - return make_indenting_guards(indent_string, std::cout, std::cerr, std::clog); - } else { - return std::make_tuple(Basic_indenting_stream_guard(streams, indent_string)...); - } + return std::make_tuple(Basic_indenting_stream_guard(streams, indent_string)...); +} +inline auto make_indenting_guards(const std::string& indent_string) { + return make_indenting_guards(indent_string, std::cout, std::cerr, std::clog); } +/// @} } // namespace IO } // namespace CGAL From 0946ff76ad3c24bb04ddf2de161fad80ce9e2b6a Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 16 Jun 2026 16:09:50 +0200 Subject: [PATCH 074/151] fix trailing spaces --- .../include/CGAL/Conforming_Delaunay_triangulation_3.h | 4 ++-- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index c3c76dc3117c..fdbecdd3f23a 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -161,9 +161,9 @@ struct Debug_options { } unsigned int move_Steiner_vertices_level() const { - return get(Flag::debug_move_Steiner_vertices_bit1) + 2 * get(Flag::debug_move_Steiner_vertices_bit2); + return get(Flag::debug_move_Steiner_vertices_bit1) + 2 * get(Flag::debug_move_Steiner_vertices_bit2); } - void move_Steiner_vertices(unsigned int level) { + void move_Steiner_vertices(unsigned int level) { CGAL_assertion(level <= 3); get(Flag::debug_move_Steiner_vertices_bit1) = level & 1; get(Flag::debug_move_Steiner_vertices_bit2) = (level >> 1) & 1; diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index c7b34166b6e0..a3e317ba9de9 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -758,7 +758,7 @@ class Conforming_constrained_Delaunay_triangulation_3 case CDT_3_vertex_type::BBOX: return 3; case CDT_3_vertex_type::nb_of_types: - CGAL_unreachable(); + CGAL_unreachable(); } CGAL_unreachable(); } @@ -1738,7 +1738,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } CGAL_assertion(std::all_of(boost::counting_iterator(1), boost::counting_iterator(4), [f1, f2](int i) { - return f1.first->neighbor((f1.second + i) % 4) != f2.first && + return f1.first->neighbor((f1.second + i) % 4) != f2.first && f2.first->neighbor((f2.second + i) % 4) != f1.first; } )); From f7285555615a1ded25eb2499d542d31a81aac5f3 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 18 Jun 2026 12:48:28 +0200 Subject: [PATCH 075/151] enums: switch from `nb_of...` to `last` --- ...onforming_constrained_Delaunay_triangulation_3.h | 13 ++++--------- ...constrained_Delaunay_triangulation_cell_data_3.h | 5 +++-- ...nstrained_Delaunay_triangulation_vertex_data_3.h | 9 ++++----- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 05279f6ee436..9ea569921a9a 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -757,8 +757,6 @@ class Conforming_constrained_Delaunay_triangulation_3 case CDT_3_vertex_type::FREE: case CDT_3_vertex_type::BBOX: return 3; - case CDT_3_vertex_type::nb_of_types: - CGAL_unreachable(); } CGAL_unreachable(); } @@ -1447,7 +1445,7 @@ class Conforming_constrained_Delaunay_triangulation_3 } auto statistics(std::string prefix = "") const { - using Stats_array = std::array(CDT_3_vertex_type::nb_of_types)>; + using Stats_array = std::array(CDT_3_vertex_type::last) + 1>; return IO::oformat([this, prefix=std::move(prefix)](std::ostream& out) -> auto& { out << prefix << "Number of vertices: " << triangulation().number_of_vertices() << "\n"; out << prefix << "Number of cells: " << triangulation().number_of_cells() << "\n"; @@ -3646,9 +3644,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D case CDT_3_vertex_type::STEINER_IN_FACE: case CDT_3_vertex_type::STEINER_ON_EDGE: return true; - default: - CGAL_unreachable(); } + CGAL_unreachable(); } auto move_Steiner_vertices_to_the_volume() { @@ -3678,7 +3675,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D case Vertex_marker::CAVITY: std::cerr << "CAVITY"; break; case Vertex_marker::CAVITY_ABOVE: std::cerr << "CAVITY_ABOVE"; break; case Vertex_marker::CAVITY_BELOW: std::cerr << "CAVITY_BELOW"; break; - case Vertex_marker::nb_of_markers: CGAL_unreachable(); break; } std::cerr << ")\n"; } @@ -6653,12 +6649,11 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 v->set_index(v->ccdt_3_data().face_index()); break; case CDT_3_vertex_type::FREE: + case CDT_3_vertex_type::BBOX: + case CDT_3_vertex_type::STEINER_IN_VOLUME: v->set_dimension(3); v->set_index(1); break; - default: - CGAL_error(); - break; } } diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h index e787a54be970..dc1eff2b045c 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -25,7 +26,7 @@ enum class CDT_3_cell_marker { IN_REGION = 1, VISITED = 1, ON_REGION_BOUNDARY = 2, - nb_of_markers + last = ON_REGION_BOUNDARY, }; /*! @@ -44,7 +45,7 @@ class Conforming_constrained_Delaunay_triangulation_cell_data_3 { std::array face_id = { -1, -1, -1, -1 }; std::array facet_2d = {nullptr, nullptr, nullptr, nullptr}; - std::bitset(CDT_3_cell_marker::nb_of_markers)> markers{}; + std::bitset(CDT_3_cell_marker::last) + 1> markers{}; bool is_marked() const { return markers.any(); } bool is_marked(CDT_3_cell_marker m) const { return markers.test(static_cast(m)); } diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h index bb059618636f..5cb2efbb46bc 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h @@ -42,7 +42,7 @@ enum class CDT_3_vertex_type : unsigned char { STEINER_IN_FACE, STEINER_IN_VOLUME, BBOX, - nb_of_types + last = BBOX, }; enum class CDT_3_vertex_marker { @@ -52,14 +52,14 @@ enum class CDT_3_vertex_marker { CAVITY, CAVITY_ABOVE, CAVITY_BELOW, - nb_of_markers + last = CAVITY_BELOW }; class Conforming_constrained_Delaunay_triangulation_vertex_data_3 { protected: // TODO: check and improve the compactness of this class CDT_3_vertex_type m_vertex_type = CDT_3_vertex_type::FREE; - std::bitset(CDT_3_vertex_marker::nb_of_markers)> mark{}; + std::bitset(CDT_3_vertex_marker::last) + 1> mark{}; struct C_id { void* ptr = nullptr; std::size_t id = 0; @@ -167,9 +167,8 @@ class Conforming_constrained_Delaunay_triangulation_vertex_data_3 { case CDT_3_vertex_type::STEINER_IN_VOLUME: case CDT_3_vertex_type::FREE: return 3; - case CDT_3_vertex_type::nb_of_types: - CGAL_unreachable(); } + CGAL_unreachable(); } }; From 90d09fc15c6b5934dde4183826bc70fa6e6c3db3 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 18 Jun 2026 18:32:15 +0200 Subject: [PATCH 076/151] fix compilation errors with CGAL_TETRAHEDRAL_REMESHING_VERBOSE --- ...ing_constrained_Delaunay_triangulation_3.h | 15 ++++--- ...ned_Delaunay_triangulation_vertex_base_3.h | 6 +-- ...ned_Delaunay_triangulation_vertex_data_3.h | 19 --------- .../CGAL/Simplicial_mesh_vertex_base_3.h | 7 +++- .../internal/collapse_short_edges.h | 2 +- .../internal/flip_edges.h | 2 +- .../internal/smooth_vertices.h | 23 ++++++----- .../internal/split_long_edges.h | 2 +- .../tetrahedral_adaptive_remeshing_impl.h | 26 ++++++------ .../internal/tetrahedral_remeshing_helpers.h | 40 ++++++++++--------- 10 files changed, 66 insertions(+), 76 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 9ea569921a9a..7d8e31742ee1 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -837,21 +837,20 @@ class Conforming_constrained_Delaunay_triangulation_3 }; } + using Edges_in_complex_iterator = typename CDT_3_impl::Complex_edges_iterator; auto edges_in_complex() const { return impl().edges_in_complex(); } + auto edges_in_complex_begin() const { using std::begin; return begin(impl().edges_in_complex()); } + auto edges_in_complex_end() const { using std::end; return end(impl().edges_in_complex()); } + using Facets_in_complex_iterator = typename CDT_3_impl::Constrained_facets_iterator; auto facets_in_complex() const { - auto in_complex = [this](const Facet& f) { return is_in_complex(f); }; - return boost::make_iterator_range( - boost::make_filter_iterator(in_complex, - impl().finite_facets_begin(), - impl().finite_facets_end()), - boost::make_filter_iterator(in_complex, - impl().finite_facets_end(), - impl().finite_facets_end())); + return impl().constrained_facets(); } + auto facets_in_complex_begin() const { using std::begin; return begin(impl().constrained_facets()); } + auto facets_in_complex_end() const { using std::end; return end(impl().constrained_facets()); } auto cells_in_complex() const { return impl().finite_cell_handles(); diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h index 3584b7613c42..305c3e9456dc 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h @@ -55,7 +55,10 @@ class Conforming_constrained_Delaunay_triangulation_vertex_base_3 using Base = Triangulation_simplex_base_with_time_stamp; using Base::Base; + ///////// for C3T3 : dummy implementation for compilation purposes using Index = int; + void set_dimension(int) {} + ///////// end of for C3T3 // model of ConformingConstrainedDelaunayTriangulationVertexBase_3 Conforming_constrained_Delaunay_triangulation_vertex_data_3& ccdt_3_data() { return ccdt_3_data_; } @@ -64,9 +67,6 @@ class Conforming_constrained_Delaunay_triangulation_vertex_base_3 static std::string io_signature() { return Get_io_signature()(); } - - int in_dimension() const { return ccdt_3_data().in_dimension(); } - void set_dimension(int) {} }; } // namespace CGAL diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h index 5cb2efbb46bc..a1f728015354 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h @@ -152,25 +152,6 @@ class Conforming_constrained_Delaunay_triangulation_vertex_data_3 { bool is_Steiner_vertex_on_edge() const { return m_vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; } bool is_Steiner_vertex_in_face() const { return m_vertex_type == CDT_3_vertex_type::STEINER_IN_FACE; } bool is_Steiner_vertex_in_volume() const { return m_vertex_type == CDT_3_vertex_type::STEINER_IN_VOLUME; } - - int in_dimension() const - { - switch(m_vertex_type) - { - case CDT_3_vertex_type::INPUT_VERTEX: - case CDT_3_vertex_type::BBOX: - return 0; - case CDT_3_vertex_type::STEINER_ON_EDGE: - return 1; - case CDT_3_vertex_type::STEINER_IN_FACE: - return 2; - case CDT_3_vertex_type::STEINER_IN_VOLUME: - case CDT_3_vertex_type::FREE: - return 3; - } - CGAL_unreachable(); - } - }; #endif // DOXYGEN_RUNNING diff --git a/SMDS_3/include/CGAL/Simplicial_mesh_vertex_base_3.h b/SMDS_3/include/CGAL/Simplicial_mesh_vertex_base_3.h index 9238c4d9b710..0e7a1a481b7f 100644 --- a/SMDS_3/include/CGAL/Simplicial_mesh_vertex_base_3.h +++ b/SMDS_3/include/CGAL/Simplicial_mesh_vertex_base_3.h @@ -39,6 +39,8 @@ #include #include #include +#include +#include namespace CGAL { @@ -113,7 +115,10 @@ private : using Curve_index = CurveIndex; using Corner_index = CornerIndex; - using Index = Variant_with_no_duplicate_t; + using Index_variant = Variant_with_no_duplicate_t; + using Index = std::conditional_t == 1, + std::variant_alternative_t<0, Index_variant>, + Index_variant>; using FT = typename Gt::FT; diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index 58676683f7bb..5d198942b597 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -715,7 +715,7 @@ bool are_edge_lengths_valid(const typename C3t3::Edge& edge, else if (collapse_type == TO_MIDPOINT) { new_index = max_dimension_index(v0, v1, c3t3); - new_dim = (std::max)(v0->in_dimension(), v1->in_dimension()); + new_dim = (std::max)(c3t3.in_dimension(v0), c3t3.in_dimension(v1)); #ifdef CGAL_AVERAGE_SIZING_AFTER_COLLAPSE sizing_at_new_pos = sizing_at_midpoint(edge, new_dim, new_index, sizing, c3t3, cell_selector); #else diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h index 0a155e444655..90dd50302b10 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h @@ -1225,7 +1225,7 @@ void collect_subdomains_on_boundary(const C3t3& c3t3, { for (auto v : c3t3.triangulation().vertices(c)) { - const int dim = v->in_dimension(); + const int dim = c3t3.in_dimension(v); if(dim >= 0 && dim < 3) vertices_subdomain_indices[v].insert(c->subdomain_index()); } diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h index ecdaaa0944cb..ea83703503cb 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h @@ -137,7 +137,7 @@ class Tetrahedral_remeshing_smoother { CGAL_assertion(!m_flip_smooth_steps); reset_vertex_id_map(c3t3.triangulation()); - reset_free_vertices(c3t3.triangulation()); + reset_free_vertices(c3t3); // once this variable is set to true, // m_vertex_id becomes constant and @@ -169,8 +169,9 @@ class Tetrahedral_remeshing_smoother // this function can be used iff m_vertex_id // has already been initialized - void reset_free_vertices(const Tr& tr) + void reset_free_vertices(const C3t3& c3t3) { + const auto& tr = c3t3.triangulation(); // when flip-smooth steps start, // m_free_vertices should already be initialized // by the last smoothing step. @@ -192,7 +193,7 @@ class Tetrahedral_remeshing_smoother for (auto vi : tr.vertices(c)) { const std::size_t idi = vertex_id(vi); - const int dim = vi->in_dimension(); + const int dim = c3t3.in_dimension(vi); switch (dim) { @@ -671,8 +672,8 @@ class Tetrahedral_remeshing_smoother const Vertex_handle vh0 = e.first->vertex(e.second); const Vertex_handle vh1 = e.first->vertex(e.third); - CGAL_expensive_assertion(is_on_feature(vh0)); - CGAL_expensive_assertion(is_on_feature(vh1)); + CGAL_expensive_assertion(is_on_feature(c3t3, vh0)); + CGAL_expensive_assertion(is_on_feature(c3t3, vh1)); const std::size_t& i0 = vertex_id(vh0); const std::size_t& i1 = vertex_id(vh1); @@ -706,7 +707,7 @@ class Tetrahedral_remeshing_smoother { const std::size_t vid = vertex_id(v); - if (!is_free(vid) || !is_on_feature(v)) + if (!is_free(vid) || !is_on_feature(c3t3, v)) continue; const Point_3 current_pos = point(v->point()); @@ -792,8 +793,8 @@ std::size_t smooth_vertices_on_surfaces(C3t3& c3t3, const std::size_t& i0 = vertex_id(vh0); const std::size_t& i1 = vertex_id(vh1); - const bool vh0_moving = !is_on_feature(vh0) && is_free(i0); - const bool vh1_moving = !is_on_feature(vh1) && is_free(i1); + const bool vh0_moving = !is_on_feature(c3t3, vh0) && is_free(i0); + const bool vh1_moving = !is_on_feature(c3t3, vh1) && is_free(i1); if (!vh0_moving && !vh1_moving) continue; @@ -822,7 +823,7 @@ std::size_t smooth_vertices_on_surfaces(C3t3& c3t3, { const std::size_t vid = vertex_id(v); - if (!is_free(vid) || v->in_dimension() != 2) + if (!is_free(vid) || c3t3.in_dimension(v) != 2) continue; const std::size_t nb_neighbors = moves[vid].neighbors; @@ -1085,7 +1086,7 @@ std::size_t smooth_internal_vertices(C3t3& c3t3, reset_vertex_id_map(tr); //are vertices free to move? indices are in `vertex_id` - reset_free_vertices(tr); + reset_free_vertices(c3t3); //collect incident cells using Incident_cells_vector = boost::container::small_vector; @@ -1146,7 +1147,7 @@ std::size_t smooth_internal_vertices(C3t3& c3t3, #endif #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - c3t3.triangulation(), "c3t3_vertices_after_smoothing"); + c3t3, "c3t3_vertices_after_smoothing"); os_surf.close(); os_vol.close(); #endif diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h index 78e51a0f899b..225b87971b4c 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h @@ -234,7 +234,7 @@ typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e, // insert midpoint Vertex_handle new_v = tr.tds().insert_in_edge(e); new_v->set_point(typename Tr::Point(m)); - new_v->set_dimension(dimension); + c3t3.set_dimension(new_v, dimension); // update c3t3 with subdomain and surface patch indices std::vector new_cells; diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h index b1aa8efafb9f..b13adfc128ac 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h @@ -16,7 +16,9 @@ #include #ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE_PROGRESS -#define CGAL_TETRAHEDRAL_REMESHING_VERBOSE +# ifndef CGAL_TETRAHEDRAL_REMESHING_VERBOSE +# define CGAL_TETRAHEDRAL_REMESHING_VERBOSE +# endif #endif #include @@ -180,7 +182,7 @@ class Adaptive_remesher CGAL::Tetrahedral_remeshing::debug::dump_facets_in_complex(m_c3t3, "1-facets_in_complex_after_split.off"); CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "1-c3t3_vertices_after_split"); + m_c3t3, "1-c3t3_vertices_after_split"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, "1-edges_in_complex_after_split.polylines.txt"); @@ -202,7 +204,7 @@ class Adaptive_remesher CGAL_assertion(tr().tds().is_valid(true)); CGAL_assertion(debug::are_cell_orientations_valid(tr())); CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "2-c3t3_vertices_after_collapse"); + m_c3t3, "2-c3t3_vertices_after_collapse"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, "2-edges_in_complex_after_collapse.polylines.txt"); @@ -223,7 +225,7 @@ class Adaptive_remesher CGAL_assertion(tr().tds().is_valid(true)); CGAL_assertion(debug::are_cell_orientations_valid(tr())); CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "3-c3t3_vertices_after_flip"); + m_c3t3, "3-c3t3_vertices_after_flip"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, "3-edges_in_complex_after_flip.polylines.txt"); @@ -243,7 +245,7 @@ class Adaptive_remesher CGAL_assertion(tr().tds().is_valid(true)); CGAL_assertion(debug::are_cell_orientations_valid(tr())); CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "4-c3t3_vertices_after_smooth"); + m_c3t3, "4-c3t3_vertices_after_smooth"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, "4-edges_in_complex_after_smoothing.polylines.txt"); @@ -307,7 +309,7 @@ class Adaptive_remesher { //reset far points dimension for (Vertex_handle v : m_far_points) - v->set_dimension(-1); + m_c3t3.set_dimension(v, -1); m_c3t3_pbackup->swap(m_c3t3); } else @@ -331,7 +333,7 @@ class Adaptive_remesher #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "00-c3t3_vertices_before_init_"); + m_c3t3, "00-c3t3_vertices_before_init_"); #endif if (input_is_c3t3()) @@ -471,7 +473,7 @@ class Adaptive_remesher std::cout << "\t Min dihedral angle = " << mdh << std::endl; CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "0-c3t3_vertices_after_init_"); + m_c3t3, "0-c3t3_vertices_after_init_"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::count_far_points(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, @@ -483,7 +485,7 @@ class Adaptive_remesher { for (Vertex_handle v : tr().finite_vertex_handles()) { - if (v->in_dimension() == -1) + if (m_c3t3.in_dimension(v) == -1) m_far_points.push_back(v); } } @@ -491,7 +493,7 @@ class Adaptive_remesher private: bool dimension_is_modifiable(const Vertex_handle& v, const int new_dim) const { - const int vdim = v->in_dimension(); + const int vdim = m_c3t3.in_dimension(v); // feature edges and tip/endpoints vertices are kept switch (vdim) { @@ -510,7 +512,7 @@ class Adaptive_remesher void set_dimension(Vertex_handle v, const int new_dim) { if (dimension_is_modifiable(v, new_dim)) - v->set_dimension(new_dim); + m_c3t3.set_dimension(v, new_dim); } bool check_vertex_dimensions() @@ -519,7 +521,7 @@ class Adaptive_remesher { // dimension is -1 for Mesh_3 "far points" // for other vertices, it is in [0; 3] - if (vit->in_dimension() < -1 || vit->in_dimension() > 3) + if (m_c3t3.in_dimension(vit) < -1 || m_c3t3.in_dimension(vit) > 3) return false; } return true; diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index 6a0ef4a7926d..1a4ee5d8ace7 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -644,10 +644,10 @@ Facet canonical_facet(const Facet& f) return (c2 < c) ? std::make_pair(c2, c2->index(c)) : std::make_pair(c, i); } -template -bool is_on_feature(const VertexHandle v) +template +bool is_on_feature(const C3T3& c3t3, const VertexHandle v) { - return (v->in_dimension() == 1 || v->in_dimension() == 0); + return (c3t3.in_dimension(v) == 1 || c3t3.in_dimension(v) == 0); } template @@ -825,7 +825,7 @@ surface_patch_index(const typename C3t3::Vertex_handle v, template void set_index(typename C3t3::Vertex_handle v, const C3t3& c3t3) { - switch (v->in_dimension()) + switch (c3t3.in_dimension(v)) { case 3: c3t3.set_index(v, c3t3.subdomain_index(v->cell())); @@ -1419,7 +1419,7 @@ auto sizing_at_vertex(const Vertex_handle v, { auto size = sizing(point(v->point()), c3t3.in_dimension(v), c3t3.index(v)); - if(v->in_dimension() < 3 && size == 0) + if(c3t3.in_dimension(v) < 3 && size == 0) { std::vector cells; c3t3.triangulation().incident_cells(v, std::back_inserter(cells)); @@ -1451,8 +1451,8 @@ auto sizing_at_midpoint(const typename C3t3::Edge& e, { const auto [u, v] = make_vertex_pair(e); - const FT size_at_u = sizing(cp(u->point()), u->in_dimension(), c3t3.index(u)); - const FT size_at_v = sizing(cp(v->point()), v->in_dimension(), c3t3.index(v)); + const FT size_at_u = sizing(cp(u->point()), c3t3.in_dimension(u), c3t3.index(u)); + const FT size_at_v = sizing(cp(v->point()), c3t3.in_dimension(v), c3t3.index(v)); if (size_at_u == 0. || size_at_v == 0.) return average_sizing_in_incident_cells(e, sizing, c3t3, cell_selector); @@ -1587,7 +1587,7 @@ auto midpoint_with_info(const typename C3t3::Edge& e, const Point_3 midpoint_pt = midpt(cp(u->point()), cp(v->point())); const int midpoint_dim = boundary_edge - ? (std::max)(u->in_dimension(), v->in_dimension()) + ? (std::max)(c3t3.in_dimension(u), c3t3.in_dimension(v)) : 3; const Index midpoint_index = boundary_edge ? max_dimension_index(c3t3.triangulation().vertices(e), c3t3) @@ -1616,8 +1616,8 @@ squared_upper_size_bound(const typename C3t3::Edge& e, const Vertex_handle u = e.first->vertex(e.second); const Vertex_handle v = e.first->vertex(e.third); - const FT size_at_u = sizing(cp(u->point()), u->in_dimension(), c3t3.index(u)); - const FT size_at_v = sizing(cp(v->point()), v->in_dimension(), c3t3.index(v)); + const FT size_at_u = sizing(cp(u->point()), c3t3.in_dimension(u), c3t3.index(u)); + const FT size_at_v = sizing(cp(v->point()), c3t3.in_dimension(v), c3t3.index(v)); // if e is on the boundary AND sizing at the boundary is set to 0, // we take the maximum size of the incident cells @@ -1873,8 +1873,8 @@ void get_edge_info(const typename C3t3::Edge& edge, } else { - const bool v0_on_feature = is_on_feature(v0); - const bool v1_on_feature = is_on_feature(v1); + const bool v0_on_feature = is_on_feature(c3t3, v0); + const bool v1_on_feature = is_on_feature(c3t3, v1); if (v0_on_feature && v1_on_feature) { if (c3t3.is_in_complex(edge)) { @@ -2098,7 +2098,7 @@ void check_surface_patch_indices(const C3t3& c3t3) typedef typename C3t3::Vertex_handle Vertex_handle; for (Vertex_handle v : c3t3.triangulation().finite_vertex_handles()) { - if (v->in_dimension() != 2) + if (c3t3.in_dimension(v) != 2) continue; CGAL_expensive_assertion(surface_patch_index(v, c3t3) != std::nullopt); } @@ -2524,10 +2524,12 @@ void dump_cells_with_small_dihedral_angle(const Tr& tr, dump_cells_off(cells, tr, "bad_cells.off"); } -template -void dump_vertices_by_dimension(const Tr& tr, const char* prefix) +template +void dump_vertices_by_dimension(const C3t3& c3t3, const char* prefix) { - typedef typename Tr::Vertex_handle Vertex_handle; + using Tr = typename C3t3::Triangulation; + const Tr& tr = c3t3.triangulation(); + using Vertex_handle = typename Tr::Vertex_handle; std::vector< std::vector > vertices_per_dimension(4); std::size_t nb_far_points = 0; @@ -2536,14 +2538,14 @@ void dump_vertices_by_dimension(const Tr& tr, const char* prefix) vit != tr.finite_vertices_end(); ++vit) { - if (vit->in_dimension() == -1) + if (c3t3.in_dimension(vit) == -1) { ++nb_far_points; continue;//far point } - CGAL_assertion(vit->in_dimension() >= 0 && vit->in_dimension() < 4); + CGAL_assertion(c3t3.in_dimension(vit) >= 0 && c3t3.in_dimension(vit) < 4); - vertices_per_dimension[vit->in_dimension()].push_back(vit); + vertices_per_dimension[c3t3.in_dimension(vit)].push_back(vit); } for (int i = 0; i < 4; ++i) From 580d296f0016105f6278aa36ca2cc03ded501c44 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 24 Jul 2026 15:47:06 +0200 Subject: [PATCH 077/151] fix compilation for msvc --- Stream_support/include/CGAL/IO/Indenting_ostream.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Stream_support/include/CGAL/IO/Indenting_ostream.h b/Stream_support/include/CGAL/IO/Indenting_ostream.h index d7aee11780a8..1de0ba8c8c95 100644 --- a/Stream_support/include/CGAL/IO/Indenting_ostream.h +++ b/Stream_support/include/CGAL/IO/Indenting_ostream.h @@ -335,6 +335,11 @@ inline auto make_indenting_guards(int spaces_per_level) { } /// @} +template +auto make_indenting_guard(Stream& stream, const std::string& indent_string) { + return Basic_indenting_stream_guard(stream, indent_string); +} + /** * \ingroup PkgStreamSupportRef * @@ -355,7 +360,11 @@ inline auto make_indenting_guards(int spaces_per_level) { /// @{ template auto make_indenting_guards(const std::string& indent_string, Streams&... streams) { - return std::make_tuple(Basic_indenting_stream_guard(streams, indent_string)...); + if constexpr(sizeof...(Streams) == 0) { + return make_indenting_guards(indent_string, std::cout, std::cerr, std::clog); + } else { + return std::make_tuple(make_indenting_guard(streams, indent_string)...); + } } inline auto make_indenting_guards(const std::string& indent_string) { return make_indenting_guards(indent_string, std::cout, std::cerr, std::clog); From 6e34fca48cc9b381be9c3d8cd6bf632c3407cbf4 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 31 Jul 2026 15:04:27 +0200 Subject: [PATCH 078/151] add dump of CDT_3 surface --- .../internal/tetrahedral_remeshing_helpers.h | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index 1a4ee5d8ace7..fa83d591c480 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -2605,12 +2605,26 @@ void dump_medit(const C3t3& c3t3, const char* filename) os.close(); } -template -void dump_c3t3(const C3t3& c3t3, const char* filename_no_extension) +template +void dump_c3t3( + const CGAL::Mesh_complex_3_in_triangulation_3& c3t3, + const char* filename_no_extension) +{ + std::string filename_medit(filename_no_extension); + filename_medit.append(".mesh"); + dump_medit(c3t3, filename_medit.c_str()); + + std::string filename_binary(filename_no_extension); + filename_binary.append(".binary.cgal"); + dump_binary(c3t3, filename_binary.c_str()); +} + +template +void dump_c3t3(const CDT_3& cdt3, const char* filename_no_extension) { -// std::string filename_medit(filename_no_extension); -// filename_medit.append(".mesh"); -// dump_medit(c3t3, filename_medit.c_str()); + std::string filename_off(filename_no_extension); + filename_off.append("_facets.off"); + cdt3.dump_constrained_facets_to_off(filename_off.c_str()); // // std::string filename_binary(filename_no_extension); // filename_binary.append(".binary.cgal"); From f5e6a17c4b34f47866b49bb3cebc8f80ce436005 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 11 Aug 2026 16:09:45 +0200 Subject: [PATCH 079/151] add completion --- .../cdt_3_from_off.cpp | 85 +++++++++++++++++-- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 59eb8ad03b27..37325685199d 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -181,13 +182,13 @@ CDT_options::CDT_options(int argc, char* argv[]) { "max distance for coplanar polygons"); app.add_option("--dump-patches-after-merge", dump_patches_after_merge_filename, - "dump patches after merging facets in PLY"); + "dump patches after merging facets in PLY")->option_text("FILENAME"); app.add_option("--dump-surface-mesh-after-merge", dump_surface_mesh_after_merge_filename, - "dump surface mesh after merging facets in OFF"); + "dump surface mesh after merging facets in OFF")->option_text("FILENAME"); app.add_option("--dump-patches-borders-prefix", dump_patches_borders_prefix, - "dump patches borders"); + "dump patches borders")->option_text("PREFIX"); app.add_option("--dump-after-conforming", dump_after_conforming_filename, - "dump mesh after conforming in OFF"); + "dump mesh after conforming in OFF")->option_text("FILENAME"); app.add_flag("--debug-Steiner-points", debug_Steiner_points, "debug Steiner point insertion"); @@ -238,8 +239,80 @@ CDT_options::CDT_options(int argc, char* argv[]) { use_epeck_for_Steiner_points, "use exact kernel for Steiner point computations"); - app.add_option("input.off", input_filename, "input mesh"); - app.add_option("output.off", output_filename, "output mesh"); + app.add_option("--input,input_mesh", input_filename, "input mesh")->option_text("INPUT_FILENAME"); + app.add_option("--output,output_mesh", output_filename, "output mesh")->option_text("FILENAME"); + + auto quote_colon = [](std::string_view s) { + auto nb_colons = std::count(s.begin(), s.end(), ':'); + std::string result; + result.reserve(s.size() + nb_colons); // reserve space for the extra backslashes + for(char c : s) { + if(c == ':') { + result.push_back('\\'); + result.push_back(':'); + } else { + result.push_back(c); + } + } + return result; + }; + + auto optspec = [&,positional=0](const CLI::Option* opt, std::string_view name = "") mutable { + std::ostringstream oss; + oss << " '"; + if(name.empty()) { + oss << opt->get_name(opt->get_positional()); + } else { + if(name[0] == '-') { + oss << name; + } + else { + oss << ++positional; + } + if(name[0] == '-' && opt->get_expected() > 0) { + oss << "=-"; + } + if(!opt->get_option_text().empty()) { + oss << "::" << quote_colon(opt->get_description()) << ":_files"; + } else { + if(opt->has_description()) { + oss << "[" << quote_colon(opt->get_description()) << "]"; + } + } + } + oss << "'"; + return oss.str(); + }; + + app.add_flag_callback("--completion",[&]() { + std::cout << +R"(#compdef _cdt_3_from_off cdt_3_from_off + +_cdt_3_from_off() { + _arguments \ +)"; + for(const auto* opt : app.get_options()) { + if(opt->get_group().empty()) continue; + if(opt->get_positional()) { + std::cout << " " << optspec(opt, opt->get_name(true)); + } + std::string all_names = opt->get_name(false, true, true); + for(std::string::size_type prev = 0, pos = all_names.find(',');;) + { + const auto name = std::string_view(all_names.data() + prev, + (std::min)(pos, all_names.size()) - prev); + if(!name.empty() && name.front() == '-') { + std::cout << " " << optspec(opt, name); + } + if(pos == std::string::npos) break; + prev = pos + 1; + pos = all_names.find(',', prev); + } + std::cout << "\\\n"; + } + std::cout << "\n}\n"; + std::exit(0); + })->group(""); for(auto* option : app.get_options()) { option->always_capture_default(true); From f717d765572debeb5a8b2819f6a80fcb0ac8ba27 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 17 Aug 2026 12:21:52 +0200 Subject: [PATCH 080/151] wip --- .../Conforming_Delaunay_triangulation_3.h | 79 +++++++++++++++---- ...ing_constrained_Delaunay_triangulation_3.h | 34 ++++++-- .../cdt_3_from_off.cpp | 1 + 3 files changed, 94 insertions(+), 20 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index fdbecdd3f23a..f820d4179cfe 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -382,24 +382,29 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { Insert_in_conflict_visitor(Conforming_Delaunay_triangulation_3* self) : self(self) {} template - void process_cells_in_conflict(InputIterator cell_it, InputIterator end) { + void process_cells_in_conflict(InputIterator cell_it, InputIterator end) + { const CGAL::unordered_flat_set cells_set(cell_it, end); - std::set> edges_in_conflict; - auto d = self->tr().dimension(); - for( ; cell_it != end; ++cell_it ) { - for( int i = 0; i < d; ++i ) { - const auto n = (*cell_it)->neighbor(i); - if(cells_set.find(n) == cells_set.end()) continue; - // here: (c, i) is a facet internal to the conflict region - for(int j = 0; j < 3; ++j ) { - auto v1 = (*cell_it)->vertex(self->tr().vertex_triple_index(i, j)); - auto v2 = (*cell_it)->vertex(self->tr().vertex_triple_index(i, self->tr().cw(j))); - if(self->tr().is_infinite(v1) || self->tr().is_infinite(v2)) continue; - edges_in_conflict.insert(CGAL::make_sorted_pair(v1, v2)); + std::set border_facets; + std::set> cavity_edges; + for(auto c : cells_set) { + for(int i = 0; i < 4; ++i) { + if(!cells_set.count(c->neighbor(i))) { + border_facets.emplace(c, i); + } + for(int j = i + 1; j < 4; ++j) { + cavity_edges.emplace(make_sorted_pair(c->vertex(i), c->vertex(j))); } } } - for(auto [v1, v2]: edges_in_conflict) { + for(auto f : border_facets) { + const auto f_vertices = T_3::vertices(f); + for(int i = 0; i < 3; ++i) { + cavity_edges.erase(make_sorted_pair(f_vertices[i], f_vertices[T_3::ccw(i)])); + } + } + + for(auto [v1, v2]: cavity_edges) { if(self->use_finite_edges_map()) { auto v1_index = v1->time_stamp(); [[maybe_unused]] auto nb_erased = self->all_finite_edges[v1_index].erase(v2); @@ -677,6 +682,10 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { }); } + auto all_constrained_edges () const { + return constraint_hierarchy.subconstraints(); + } + enum class Check_distance { SQUARED_DISTANCE, NON_SQUARED_DISTANCE }; void check_segment_vertex_distance_or_throw(Vertex_handle va, @@ -819,6 +828,15 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { } } + bool has_constrained_edges_to_restore() const { + return !subconstraints_to_conform.empty(); + } + + template + void restore_constrained_edges(Visitor& visitor) { + return restore_Delaunay(visitor); + } + template void restore_Delaunay(Visitor& visitor) { update_all_finite_edges(); @@ -890,6 +908,34 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { const Vertex_handle vb = subconstraint.second; CGAL_assertion(va != vb); if(!this->is_edge(va, vb)) { +#ifdef CGAL_CT3_FLIP_BEFORE_INSERTING_STEINER_POINTS + // try to flip facet crossed by [va, vb] to make it an + // edge of the triangulation + std::vector inc_cells; + this->incident_cells(va, std::back_inserter(inc_cells)); + for(auto c : inc_cells) + { + int ia = c->index(va); + if(c->neighbor(ia)->has_vertex(vb)) + { + if(this->flip(Facet(c, ia))) + { + std::cout << "flipped facet to make edge (" + << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation\n"; + this->is_Delaunay = false; + CGAL_assertion(this->is_edge(va, vb)); + return true; + } + else + { + std::cout << "-- flip failed to make edge (" << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation\n"; + } + break; + } + } +#endif const auto& [steiner_pt, hint, ref_vertex] = construct_Steiner_point(constraint, subconstraint); [[maybe_unused]] const auto v = insert_Steiner_point_on_subconstraint(steiner_pt, hint, subconstraint, constraint, visitor); @@ -909,6 +955,11 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { } public: + bool is_a_constrained_edge(Vertex_handle va, Vertex_handle vb) const { + auto [contexts_begin, contexts_end] = this->constraint_hierarchy.contexts(va, vb); + return (contexts_begin != contexts_end); + } + Constrained_polyline_id constraint_from_extremities(Vertex_handle va, Vertex_handle vb) const { if(va->ccdt_3_data().number_of_incident_constraints() == 0 || vb->ccdt_3_data().number_of_incident_constraints() == 0) { diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 7d8e31742ee1..803075a61e02 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1494,9 +1494,10 @@ class Conforming_constrained_Delaunay_triangulation_3 // ---------------------------------------------------------- template class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_Delaunay_triangulation_3 { + Conforming_constrained_Delaunay_triangulation_3_impl& tr() { return *this; }; + const Conforming_constrained_Delaunay_triangulation_3_impl& tr() const { return *this; }; public: using Conforming_Dt = Conforming_Delaunay_triangulation_3; - using Conforming_Dt::tr; static_assert(Conforming_Dt::t_3_is_not_movable || CGAL::is_nothrow_movable_v); using Vertex_handle = typename T_3::Vertex_handle; @@ -5055,10 +5056,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D with_point(vt[0]), with_point(vt[1]), with_point(vt[2])); } } - this->copy_triangulation_into_hole(map_upper_cavity_vertices_to_ambient_vertices, - std::move(outer_map), - upper_inner_map, - this->new_cells_output_iterator()); + tr().copy_triangulation_into_hole(map_upper_cavity_vertices_to_ambient_vertices, + std::move(outer_map), + upper_inner_map, + this->new_cells_output_iterator()); } if(this->debug().copy_triangulation_into_hole()) { std::cerr << "# glu the lower triangulation of the cavity\n"; @@ -5125,8 +5126,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D set_facet_constrained(T_3::mirror_facet(f), face_index, f2d); f2d->info().missing_subface = false; } + + if(this->has_constrained_edges_to_restore()) { + this->restore_constrained_edges(insert_in_conflict_visitor); + } + + // CGAL_assertion(has_all_constrained_edges()); CGAL_assume(!this->debug().validity() || this->is_valid(true)); - }; + } // ------------------------- // end of restore_subface_region @@ -5527,7 +5534,22 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } + bool has_all_constrained_edges() const { + bool result = true; + for(const auto& [va, vb] : this->all_constrained_edges()) { + const bool is_3d = this->is_edge(va, vb); + if(!is_3d) { + std::cerr << cdt_3_format("Edge is not 3D: ({} , {})\n", display_vert(va), display_vert(vb)); + result = false; + } + } + return result; + } + bool restore_face(CDT_3_signed_index face_index) { + // CGAL_assertion(has_all_constrained_edges()); + // auto _ = CGAL::make_scope_exit([&]() { CGAL_assertion(has_all_constrained_edges()); }); + CDT_2& non_const_cdt_2 = this->non_const_face_cdt_2(face_index); const CDT_2& cdt_2 = non_const_cdt_2; if constexpr (cdt_3_can_use_cxx20_format()) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 37325685199d..ce50a408a465 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -1,5 +1,6 @@ #include +// #define CGAL_CT3_FLIP_BEFORE_INSERTING_STEINER_POINTS 1 #define CGAL_T3_ALLOW_NEGATIVE_VOLUME 1 #define CGAL_TETRAHEDRAL_REMESHING_VERBOSE 1 // #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1 From 76435d9fdac942804ea23c566b73233c8851816d Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Mon, 17 Aug 2026 17:44:44 +0200 Subject: [PATCH 081/151] fix compilation --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 803075a61e02..bd4d98bc7bf7 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2702,7 +2702,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto kernel_center_point = *opt_kernel_center_point; central_points->push_back(kernel_center_point); if(dbg().move_Steiner_vertices_level() > 1) { - std::cerr << kernel_center_point + std::cerr << *opt_kernel_center_point << " (distance to vertex: " << CGAL::sqrt(CGAL::squared_distance(kernel_center_point, v->point())) << ")" << "\n"; From 023bdf1f6188d4b31e3c6554b68abdd461f4239e Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 19 Aug 2026 11:56:02 +0200 Subject: [PATCH 082/151] add a command line option --use-flips-to-recover-segments to replace CGAL_CT3_FLIP_BEFORE_INSERTING_STEINER_POINTS --- .../Conforming_Delaunay_triangulation_3.h | 50 ++++++++++--------- .../cdt_3_from_off.cpp | 10 +++- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index f820d4179cfe..65340c8fd019 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -100,6 +100,7 @@ struct Debug_options { use_epeck_for_normals, use_epeck_for_Steiner_points, move_Steiner_vertices_allow_negative_tets, + use_flips_to_recover_segments, nb_of_flags }; @@ -181,6 +182,9 @@ struct Debug_options { bool use_epeck_for_Steiner_points() const { return get(Flag::use_epeck_for_Steiner_points); } void use_epeck_for_Steiner_points(bool b) { get(Flag::use_epeck_for_Steiner_points) = b; } + bool use_flips_to_recover_segments() const { return get(Flag::use_flips_to_recover_segments); } + void use_flips_to_recover_segments(bool b) { get(Flag::use_flips_to_recover_segments) = b; } + double segment_vertex_epsilon() const { return segment_vertex_epsilon_; } void set_segment_vertex_epsilon(double eps) { segment_vertex_epsilon_ = eps; } @@ -908,34 +912,34 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { const Vertex_handle vb = subconstraint.second; CGAL_assertion(va != vb); if(!this->is_edge(va, vb)) { -#ifdef CGAL_CT3_FLIP_BEFORE_INSERTING_STEINER_POINTS - // try to flip facet crossed by [va, vb] to make it an - // edge of the triangulation - std::vector inc_cells; - this->incident_cells(va, std::back_inserter(inc_cells)); - for(auto c : inc_cells) - { - int ia = c->index(va); - if(c->neighbor(ia)->has_vertex(vb)) + if(options().use_flips_to_recover_segments()) { + // try to flip facet crossed by [va, vb] to make it an + // edge of the triangulation + std::vector inc_cells; + this->incident_cells(va, std::back_inserter(inc_cells)); + for(auto c : inc_cells) { - if(this->flip(Facet(c, ia))) - { - std::cout << "flipped facet to make edge (" - << display_vert(va) << ", " << display_vert(vb) - << ") an edge in the triangulation\n"; - this->is_Delaunay = false; - CGAL_assertion(this->is_edge(va, vb)); - return true; - } - else + int ia = c->index(va); + if(c->neighbor(ia)->has_vertex(vb)) { - std::cout << "-- flip failed to make edge (" << display_vert(va) << ", " << display_vert(vb) - << ") an edge in the triangulation\n"; + if(this->flip(Facet(c, ia))) + { + std::cout << "flipped facet to make edge (" + << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation\n"; + this->is_Delaunay = false; + CGAL_assertion(this->is_edge(va, vb)); + return true; + } + else + { + std::cout << "-- flip failed to make edge (" << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation\n"; + } + break; } - break; } } -#endif const auto& [steiner_pt, hint, ref_vertex] = construct_Steiner_point(constraint, subconstraint); [[maybe_unused]] const auto v = insert_Steiner_point_on_subconstraint(steiner_pt, hint, subconstraint, constraint, visitor); diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index ce50a408a465..86a3d591026c 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -1,6 +1,5 @@ #include -// #define CGAL_CT3_FLIP_BEFORE_INSERTING_STEINER_POINTS 1 #define CGAL_T3_ALLOW_NEGATIVE_VOLUME 1 #define CGAL_TETRAHEDRAL_REMESHING_VERBOSE 1 // #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1 @@ -127,6 +126,12 @@ struct CDT_options bool debug_polygon_insertion = false; bool debug_restore_faces = false; bool use_finite_edges_map = false; + bool use_flips_to_recover_segments = +#ifdef CGAL_CT3_FLIP_BEFORE_INSERTING_STEINER_POINTS + true; +#else + false; +#endif bool move_Steiner_vertices_to_the_volume = true; bool allow_moving_Steiner_vertices_to_create_negative_tets = false; bool use_epeck_for_normals = false; @@ -228,6 +233,8 @@ CDT_options::CDT_options(int argc, char* argv[]) { app.add_flag("--use-finite-edges-map", use_finite_edges_map, "use a hash map for finite edges"); + app.add_flag("--use-flips-to-recover-segments", use_flips_to_recover_segments, + "use flips to recover constrained segments"); app.add_flag("--move-Steiner-vertices-to-the-volume,!--no-move-Steiner-vertices-to-the-volume", move_Steiner_vertices_to_the_volume, "move Steiner vertices to the volume"); @@ -358,6 +365,7 @@ CGAL::CDT_3::Debug_options cdt_debug_options(const CDT_options& options) { cdt_debug.restore_faces(options.debug_restore_faces); cdt_debug.copy_triangulation_into_hole(options.debug_copy_triangulation_into_hole); cdt_debug.use_finite_edges_map(options.use_finite_edges_map); + cdt_debug.use_flips_to_recover_segments(options.use_flips_to_recover_segments); cdt_debug.display_statistics(!options.quiet); cdt_debug.use_epeck_for_normals(options.use_epeck_for_normals); cdt_debug.use_epeck_for_Steiner_points(options.use_epeck_for_Steiner_points); From 915d69ac02174f23cfeed2c539f372326019458c Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 19 Aug 2026 13:14:08 +0200 Subject: [PATCH 083/151] fix compilation error @janetournois --- .../Constrained_triangulation_3/cdt_3_from_off.cpp | 4 ++-- .../include/CGAL/tetrahedral_remeshing.h | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 86a3d591026c..bba11acc69d2 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -811,8 +811,8 @@ int go(Mesh mesh, CDT_options options) { } CGAL::tetrahedral_isotropic_remeshing( cdt, - bbox_max_span, - CGAL::parameters::number_of_iterations(30) + 3 * bbox_max_span, + CGAL::parameters::number_of_iterations(30).nb_flip_smooth_iterations(20u) .remesh_boundaries(false)); // CGAL_assertion(cdt.tr().tds().is_valid(true)); std::cout << cdt.statistics() << "\n"; diff --git a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h index a6e0f87d17b1..8c185f133d1b 100644 --- a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h +++ b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h @@ -577,8 +577,12 @@ void tetrahedral_isotropic_remeshing(CDT_3& cdt3, // Advanced and non documented parameters auto visitor = choose_parameter(get_parameter(np, internal_np::visitor), typename Remesher_types::Default_Visitor()); - auto nb_extra_iterations = - choose_parameter(get_parameter(np, internal_np::nb_flip_smooth_iterations), std::size_t{3}); + Tetrahedral_remeshing::internal::Remeshing_steps steps{ + choose_parameter(get_parameter(np, internal_np::do_split), true), + choose_parameter(get_parameter(np, internal_np::do_collapse), true), + choose_parameter(get_parameter(np, internal_np::do_flip), true), + choose_parameter(get_parameter(np, internal_np::nb_smoothing_iterations), std::size_t{1}), + choose_parameter(get_parameter(np, internal_np::nb_flip_smooth_iterations), std::size_t{3})}; #ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE std::cout << "Tetrahedral remeshing (" @@ -602,7 +606,7 @@ void tetrahedral_isotropic_remeshing(CDT_3& cdt3, nb_extra_iterations = 0; #endif - remesher.remesh(max_it, nb_extra_iterations); + remesher.remesh(max_it, steps); #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG const double angle_bound = 5.0; From 82fdc7c0136c1028a9de6661b5cfb4b4307ffde8 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 20 Aug 2026 11:05:46 +0200 Subject: [PATCH 084/151] minor change in I/O --- .../CGAL/Conforming_Delaunay_triangulation_3.h | 15 --------------- ...forming_constrained_Delaunay_triangulation_3.h | 4 ---- STL_Extension/include/CGAL/Compact_container.h | 15 +++++++++++++++ 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 65340c8fd019..9031f4311077 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -330,21 +330,6 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { inline static With_point_tag with_point_tag{ {-1} }; inline static With_point_and_info_tag with_point_and_info_tag{ { {-1} } }; - template - static auto with_offset(T&& arg) { - return IO::oformat(std::forward(arg), with_offset_tag); - }; - - template - static auto with_point(T&& arg) { - return IO::oformat(std::forward(arg), with_point_tag); - }; - - template - static auto with_point_and_info(T&& arg) { - return IO::oformat(std::forward(arg), with_point_and_info_tag); - }; - Conforming_Delaunay_triangulation_3(const Geom_traits& gt = Geom_traits()) : T_3(gt) { diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index bd4d98bc7bf7..c8bb692071af 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1640,10 +1640,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Constraint_hierarchy = typename Conforming_Dt::Constraint_hierarchy; using Subconstraint = typename Constraint_hierarchy::Subconstraint; - using Conforming_Dt::with_offset; - using Conforming_Dt::with_point; - using Conforming_Dt::with_point_and_info; - public: using Constrained_polyline_id = typename Constraint_hierarchy::Constraint_id; diff --git a/STL_Extension/include/CGAL/Compact_container.h b/STL_Extension/include/CGAL/Compact_container.h index ec04664d7ffb..9349cd0ca22c 100644 --- a/STL_Extension/include/CGAL/Compact_container.h +++ b/STL_Extension/include/CGAL/Compact_container.h @@ -1241,11 +1241,26 @@ struct With_offset_tag { // from 0 to number_of_vertices-1. }; +template +inline auto with_offset(T&& v) { + return IO::oformat(std::forward(v), With_offset_tag{}); +} + struct With_point_tag : public With_offset_tag { }; +template +inline auto with_point(T&& v) { + return IO::oformat(std::forward(v), With_point_tag{}); +} + struct With_point_and_info_tag : public With_point_tag {}; +template +inline auto with_point_and_info(T&& v) { + return IO::oformat(std::forward(v), With_point_and_info_tag{}); +} + template struct Output_rep, With_offset_tag> : public Output_rep> From 1dc8d63159c12dfd414bad852fd833597ef33507 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 20 Aug 2026 16:23:15 +0200 Subject: [PATCH 085/151] fix includes (IWYU errors) + add option `--debug-missing-triangles` --- .../Conforming_Delaunay_triangulation_3.h | 6 ++++- ...ing_constrained_Delaunay_triangulation_3.h | 22 +++++++++---------- .../cdt_3_from_off.cpp | 3 +++ ...t_constrained_Delaunay_triangulation_3.cpp | 18 ++++++++++++--- .../Polyline_constraint_hierarchy_2.h | 12 ++++++++-- 5 files changed, 44 insertions(+), 17 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 9031f4311077..767b76e23f3c 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -14,7 +14,7 @@ #include -#include +#include // IWYU pragma: export #include #include @@ -81,6 +81,7 @@ struct Debug_options { conforming, input_faces, missing_region, + debug_missing_triangles, regions, copy_triangulation_into_hole, validity, @@ -118,6 +119,9 @@ struct Debug_options { bool missing_region() const { return get(Flag::missing_region); } void missing_region(bool b) { get(Flag::missing_region) = b; } + bool missing_triangles() const { return get(Flag::debug_missing_triangles); } + void missing_triangles(bool b) { get(Flag::debug_missing_triangles) = b; } + bool regions() const { return get(Flag::regions); } void regions(bool b) { get(Flag::regions) = b; } diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index c8bb692071af..445ac21b115f 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -16,7 +16,7 @@ #include -#include +#include // IWYU pragma: export #include #include @@ -2881,12 +2881,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto c = *cell_it; for(int li = first_li; li < 4; ++li) { if(c->ccdt_3_data().is_facet_constrained(li)) { - #if CGAL_CDT_3_DEBUG_MISSING_TRIANGLES - auto face_id = static_cast(c->ccdt_3_data().face_constraint_index(li)); - auto fh_2 = c->ccdt_3_data().face_2(self->face_cdt_2(face_id), li); - std::cerr << "Add missing triangle (from visitor), face F#" << face_id << ": \n"; - self->write_2d_triangle(std::cerr, fh_2); - #endif // CGAL_CDT_3_DEBUG_MISSING_TRIANGLES + if(self->debug().missing_triangles()) { + auto face_id = static_cast(c->ccdt_3_data().face_constraint_index(li)); + auto fh_2 = c->ccdt_3_data().face_2(self->face_cdt_2(face_id), li); + std::cerr << "Add missing triangle (from visitor), face F#" << face_id << ": \n"; + self->write_2d_triangle(std::cerr, fh_2); + } self->register_facet_to_be_constrained(c, li); } } @@ -3918,10 +3918,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D fh->info().missing_subface = true; this->face_constraint_misses_subfaces_set(static_cast(polygon_constraint_id)); something_has_changed = true; -#if CGAL_CDT_3_DEBUG_MISSING_TRIANGLES - std::cerr << cdt_3_format("Missing triangle in polygon #{}:\n", polygon_constraint_id); - write_triangle(std::cerr, v0, v1, v2); -#endif // CGAL_CDT_3_DEBUG_MISSING_TRIANGLES + if(this->debug().missing_triangles()) { + std::cerr << cdt_3_format("Missing triangle in polygon #{}:\n", polygon_constraint_id); + write_triangle(std::cerr, v0, v1, v2); + } } else { const auto [facet, orient] = *is_facet; fh->info().missing_subface = false; diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index bba11acc69d2..72d6828443c8 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -113,6 +113,7 @@ struct CDT_options unsigned debug_move_Steiner_vertices = 0; bool debug_input_faces = false; bool debug_missing_regions = false; + bool debug_missing_triangles = false; bool debug_regions = false; bool debug_copy_triangulation_into_hole = false; bool debug_validity = false; @@ -207,6 +208,7 @@ CDT_options::CDT_options(int argc, char* argv[]) { ->expected(0, 1); app.add_flag("--debug-input-faces", debug_input_faces, "debug input faces"); app.add_flag("--debug-missing-regions", debug_missing_regions, "debug missing regions"); + app.add_flag("--debug-missing-triangles", debug_missing_triangles, "debug missing triangles"); app.add_flag("--debug-regions", debug_regions, "debug regions"); app.add_flag("--debug_copy_triangulation_into_hole", debug_copy_triangulation_into_hole, "debug copy_triangulation_into_hole"); @@ -350,6 +352,7 @@ CGAL::CDT_3::Debug_options cdt_debug_options(const CDT_options& options) { cdt_debug.Steiner_points_construction(options.debug_Steiner_points_construction); cdt_debug.input_faces(options.debug_input_faces); cdt_debug.missing_region(options.verbose_level > 1 || options.debug_missing_regions); + cdt_debug.missing_triangles(options.debug_missing_triangles); cdt_debug.regions(options.debug_regions); cdt_debug.validity(options.debug_validity); cdt_debug.finite_edges_map(options.debug_finite_edges_map); diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/test_constrained_Delaunay_triangulation_3.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/test_constrained_Delaunay_triangulation_3.cpp index e14eb01dac49..4f4786477182 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/test_constrained_Delaunay_triangulation_3.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/test_constrained_Delaunay_triangulation_3.cpp @@ -1,9 +1,21 @@ -#include +#include #include #include +#include #include - -#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include using K = CGAL::Exact_predicates_inexact_constructions_kernel; using CDT = CGAL::Conforming_constrained_Delaunay_triangulation_3; diff --git a/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h b/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h index 446c5b61f458..e401605f1a8b 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h @@ -13,17 +13,23 @@ #ifndef CGAL_POLYLINE_CONSTRAINT_HIERARCHY_2_H #define CGAL_POLYLINE_CONSTRAINT_HIERARCHY_2_H +#include #include #include +#include +#include +#include +#include +#include +#include #include #include -#include #include #include -#include #include +#include #include #include @@ -34,6 +40,8 @@ #include #include #include +#include +#include #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS # define CGAL_DEBUG_POLYLINE_CONSTRAINT_HIERARCHY_2 CGAL_CDT_2_DEBUG_INTERSECTIONS From 447f9edea53dcbd97dd8b1d0b3cff6b24570aa97 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 21 Aug 2026 10:00:30 +0200 Subject: [PATCH 086/151] cosmetic change --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 445ac21b115f..18112fe42c66 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -186,16 +186,14 @@ auto segment_soup_to_polylines(const Range_of_segments& segment_soup) { boost::add_edge(va, vb, graph); } - struct Polylines_visitor - { + struct { Graph& graph; std::vector>& polylines; void start_new_polyline() { polylines.emplace_back(); } void add_node(typename Graph::vertex_descriptor vd) { polylines.back().push_back(graph[vd]); } void end_polyline() {} - }; - Polylines_visitor visitor{graph, polylines}; + } visitor{graph, polylines}; CGAL::split_graph_into_polylines(graph, visitor); return polylines; From 10f5de4968e6a6ba48af39146c24ea6eb6442764 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 21 Aug 2026 10:01:43 +0200 Subject: [PATCH 087/151] first fix for recovery of segments using flip32 --- .../Conforming_Delaunay_triangulation_3.h | 41 +++++++++++++++++-- ...ing_constrained_Delaunay_triangulation_3.h | 41 +++++++++++++++++-- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 767b76e23f3c..404133523e3d 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -360,6 +361,27 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { return IO::oformat(v, with_point_tag); } + auto display_facet(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2) const { + return IO::oformat( + [v0, v1, v2, this](std::ostream& os) -> auto& { + os << "(" << this->display_vert(v0) << ", " << this->display_vert(v1) << ", " << this->display_vert(v2) + << ")"; + return os; + }, + IO_manip_tag{}); + } + + auto display_facet(Facet f) const { + return IO::oformat( + [f, this](std::ostream& os) -> auto& { + auto [v0, v1, v2] = T_3::vertices(f); + os << "(" << this->display_vert(v0) << ", " << this->display_vert(v1) << ", " << this->display_vert(v2) + << ")"; + return os; + }, + IO_manip_tag{}); + } + auto display_subcstr(Subconstraint subconstraint) const { auto [va, vb] = subconstraint; std::stringstream os; @@ -448,6 +470,10 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { { } + bool is_facet_protected(Facet ) const { return false; } + + void after_flip23(Vertex_handle, Vertex_handle, Facet) const {} + Vertex_handle insert_in_triangulation(const Point& p, Locate_type lt, Cell_handle c, int li, int lj) { return self->insert_impl_do_not_split(p, lt, c, li, lj, *this); } @@ -911,9 +937,18 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { int ia = c->index(va); if(c->neighbor(ia)->has_vertex(vb)) { - if(this->flip(Facet(c, ia))) + Facet facet{c, ia}; + if(visitor.is_facet_protected(facet)) { + std::cerr << "-- facet " << display_facet(facet) + << " is protected, cannot flip to make edge (" << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation\n"; + break; + } + auto [v0, v1, v2] = this->vertices(facet); + if(this->flip(facet)) { - std::cout << "flipped facet to make edge (" + visitor.after_flip23(va, vb, facet); + std::cerr << "flipped facet " << display_facet(v0, v1, v2) << " to make edge (" << display_vert(va) << ", " << display_vert(vb) << ") an edge in the triangulation\n"; this->is_Delaunay = false; @@ -922,7 +957,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { } else { - std::cout << "-- flip failed to make edge (" << display_vert(va) << ", " << display_vert(vb) + std::cerr << "-- flip failed to make edge (" << display_vert(va) << ", " << display_vert(vb) << ") an edge in the triangulation\n"; } break; diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 18112fe42c66..49c1a893b454 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2907,6 +2907,35 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void hide_point(Cell_handle, const Point_3 &) const {} + bool is_facet_protected(Facet f) const { + return self->is_facet_constrained(f); + } + + void after_flip23(Vertex_handle va, Vertex_handle vb, Facet facet) const { + const auto [cell, _] = facet; + const auto ia = cell->index(va); + const auto ib = cell->index(vb); + const auto ic = Tds::next_around_edge(ia, ib); + const auto id = Tds::next_around_edge(ib, ia); + + for(const auto cell : make_array(cell, cell->neighbor(ic), cell->neighbor(id))) { + const auto ia = cell->index(va); + const auto ib = cell->index(vb); + for(auto index : make_array(ia, ib)) { + const Facet f{cell, index}; + const auto [mirror_cell, mirror_index] = self->mirror_facet(f); + if(mirror_cell->ccdt_3_data().is_facet_constrained(mirror_index)) { + auto face_id = self->face_constraint_index(mirror_cell, mirror_index); + const CDT_2& cdt_2 = self->face_cdt_2(face_id); + const auto f2d = mirror_cell->ccdt_3_data().face_2(cdt_2, mirror_index); + self->set_facet_constrained(f, face_id, f2d); + } else { + self->set_facet_as_not_constrained(f); + } + } + } + } + void insert_Steiner_point_on_constraint(Constrained_polyline_id constraint, Vertex_handle va, Vertex_handle vb, @@ -4589,7 +4618,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // ------------------------- template - void restore_subface_region(CDT_3_signed_index face_index, int region_index, + bool restore_subface_region(CDT_3_signed_index face_index, int region_index, CDT_2& non_const_cdt_2, Fh_region& non_const_fh_region) { if(this->debug().regions()) { @@ -4734,7 +4763,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; if(!found_edge_opt) { if(try_flip_region_size_4()) { - return; + return true; } // { // Conforming_constrained_Delaunay_triangulation_3_impl new_tr; @@ -5120,13 +5149,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D set_facet_constrained(T_3::mirror_facet(f), face_index, f2d); f2d->info().missing_subface = false; } - + bool result = true; if(this->has_constrained_edges_to_restore()) { this->restore_constrained_edges(insert_in_conflict_visitor); + result = false; } // CGAL_assertion(has_all_constrained_edges()); CGAL_assume(!this->debug().validity() || this->is_valid(true)); + return result; } // ------------------------- @@ -5614,13 +5645,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D insert_mid_point_in_constrained_edge(va_3d, vb_3d); } }; + bool result = false; try { - restore_subface_region(face_index, region_index++, non_const_cdt_2, fh_region); + result = restore_subface_region(face_index, region_index++, non_const_cdt_2, fh_region); } catch(Next_region& e) { handle_error_with_region(e.what(), e.fh_2d); return false; } + if(!result) return false; } return true; } From b9cd1091ca2754189865d302ae1351fa90b49d35 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 21 Aug 2026 17:17:21 +0200 Subject: [PATCH 088/151] fix use-after-free for I/O messages --- ...ing_constrained_Delaunay_triangulation_3.h | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 49c1a893b454..7b4ed102dc72 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1646,18 +1646,18 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return with_point_and_info(v); } - auto display_facet(std::string_view prefix, Facet f) const { - return IO::oformat([&,prefix,f](auto& out) -> auto& { - const auto vertices = tr().vertices(f); - return out << cdt_3_format("\n{}{}" - "\n{}{}" - "\n{}{}" - "\n{}(fourth vertex of its cell: {})", - prefix, with_offset(vertices[0]), - prefix, with_offset(vertices[1]), - prefix, with_offset(vertices[2]), - prefix, with_offset(f.first->vertex((f.second)))); - }, IO_manip_tag{}); + template + auto display_facet(std::string_view prefix, Facet f, Tag tag = {}) const { + return IO::oformat( + [prefix, tag, vertices = tr().vertices(f), v4 = f.first->vertex((f.second))](auto& out) -> auto& { + return out << cdt_3_format("\n{}{}" + "\n{}{}" + "\n{}{}" + "\n{}(fourth vertex of its cell: {})", + prefix, IO::oformat(vertices[0], tag), prefix, IO::oformat(vertices[1], tag), + prefix, IO::oformat(vertices[2], tag), prefix, IO::oformat(v4, tag)); + }, + IO_manip_tag{}); } const auto& dbg() const { return this->debug(); } From 71bb9829d9b971354a581a461390195bd41098b4 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 21 Aug 2026 17:44:45 +0200 Subject: [PATCH 089/151] add an assertion --- ...ing_constrained_Delaunay_triangulation_3.h | 59 +++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 7b4ed102dc72..8dfa05b4f6ee 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -5190,19 +5190,21 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto [v0, v1, v2] = tr.vertices(f); auto v = vertex_of_cdt_2_functor(cdt_2); - const auto cdt_2_v0 = v(tr.point(v0)); - const auto cdt_2_v1 = v(tr.point(v1)); - const auto cdt_2_v2 = v(tr.point(v2)); + try { + const auto cdt_2_v0 = v(tr.point(v0)); + const auto cdt_2_v1 = v(tr.point(v1)); + const auto cdt_2_v2 = v(tr.point(v2)); - CDT_2_face_handle fh; - const bool is_face = cdt_2.is_face(cdt_2_v0, cdt_2_v1, cdt_2_v2, fh); - if(is_face && fh->info().is_in_region != 0) { - const int index_v0 = fh->index(cdt_2_v0); - const bool reverse_orientation = (cdt_2_v2 == fh->vertex(T_3::ccw(index_v0))); - return Oriented_face_of_cdt_2{fh, reverse_orientation}; + CDT_2_face_handle fh; + const bool is_face = cdt_2.is_face(cdt_2_v0, cdt_2_v1, cdt_2_v2, fh); + if(is_face && fh->info().is_outside_the_face != OUTSIDE) { + const int index_v0 = fh->index(cdt_2_v0); + const bool reverse_orientation = (cdt_2_v2 == fh->vertex(T_3::ccw(index_v0))); + return Oriented_face_of_cdt_2{fh, reverse_orientation}; + } + } catch(CGAL::Failure_exception&) { } - else - return std::nullopt; + return std::nullopt; } std::optional> @@ -5237,6 +5239,38 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } + bool constrained_facet_has_2d_triangle(Facet facet) const + { + const auto face_index = face_constraint_index(facet); + if(face_index < 0 || static_cast(face_index) >= face_data.size() || + face_data[face_index].skip_face) + { + return false; + } + + const auto& cdt_2 = face_cdt_2(face_index); + auto opt = facet_is_facet_of_cdt_2(*this, facet, cdt_2); + return opt.has_value(); + } + + bool has_extra_constrained_facets() const + { + bool result = true; + for(const auto facet : constrained_facets()) { + if(constrained_facet_has_2d_triangle(facet)) { + continue; + } + result = false; + if(this->debug().restore_faces() || this->debug().conforming_validation()) { + std::cerr << "ERROR: constrained 3D facet" + << display_facet(" ", facet, With_point_and_info_tag{}) + << "\n has no corresponding 2D triangle in face #" + << face_constraint_index(facet) << '\n'; + } + } + return result; + } + auto edge_of_cdt_2(const CDT_2& cdt_2, const Vertex_handle va, const Vertex_handle vb) const -> std::optional { @@ -5888,6 +5922,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D recheck_for_missing_subfaces() && are_there_any_face_constraint_misses_subfaces() == false, "All faces have been restored, but the triangulation is still not a CDT. This should not happen."); + CGAL_assertion_msg( + has_extra_constrained_facets(), + "All faces have been restored, but the triangulation has extra constrained facets. This should not happen."); } void add_bbox_points() { From 35162b3c55784d957016b0423c434b0aa926c9ed Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 21 Aug 2026 17:45:00 +0200 Subject: [PATCH 090/151] fix the bug --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 8dfa05b4f6ee..faf1d48dccfa 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2933,6 +2933,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D self->set_facet_as_not_constrained(f); } } + const auto ic = Tds::next_around_edge(ia, ib); + self->set_facet_as_not_constrained({cell, ic}); } } From d93a95bea6b50ae526e6ee1009387cfe94a49a97 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 25 Aug 2026 16:38:42 +0200 Subject: [PATCH 091/151] wip on recover_subconstraint_by_flips --- .../Conforming_Delaunay_triangulation_3.h | 164 ++++++++++++------ .../cdt_3_from_off.cpp | 3 + .../include/CGAL/Triangulation_3.h | 123 +++++-------- 3 files changed, 159 insertions(+), 131 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 404133523e3d..de36d15b4161 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,7 @@ struct Debug_options { input_faces, missing_region, debug_missing_triangles, + flips, regions, copy_triangulation_into_hole, validity, @@ -123,6 +125,9 @@ struct Debug_options { bool missing_triangles() const { return get(Flag::debug_missing_triangles); } void missing_triangles(bool b) { get(Flag::debug_missing_triangles) = b; } + bool flips() const { return get(Flag::flips); } + void flips(bool b) { get(Flag::flips) = b; } + bool regions() const { return get(Flag::regions); } void regions(bool b) { get(Flag::regions) = b; } @@ -917,6 +922,94 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { return v; } + template + std::optional + find_in_incident_cells(Vertex_handle v, Pred pred) const + { + boost::container::flat_set, + boost::container::small_vector> found_cells; + boost::container::small_vector cells; + cells.push_back(v->cell()); + found_cells.insert(cells[0]); + std::size_t head=0; + do { + Cell_handle c = cells[head]; + if(pred(c)) return c; + for (int i=0; i<4; ++i) { + if (c->vertex(i) == v) continue; + Cell_handle next = c->neighbor(i); + if (! found_cells.insert(next).second ) continue; + cells.push_back(next); + } + ++head; + } while(head != cells.size()); + return std::nullopt; + } + + bool remove_edge_by_flips(Edge edge_preventing_flip, auto& visitor) + { + return false; + } + + // try to flip facet crossed by [va, vb] to make it an edge of the triangulation + template + bool recover_subconstraint_by_flips(Subconstraint subconstraint, + [[maybe_unused]] Constrained_polyline_id constraint, + Visitor& visitor) + { + const Vertex_handle va = subconstraint.first; + const Vertex_handle vb = subconstraint.second; + CGAL_assertion(va != vb); + const auto cell_opt = find_in_incident_cells(va, [&](Cell_handle c) { + int ia = c->index(va); + return c->neighbor(ia)->has_vertex(vb); + }); + if(cell_opt.has_value()) + { + auto c = cell_opt.value(); + int ia = c->index(va); + Facet facet{c, ia}; + if(visitor.is_facet_protected(facet)) { + if(debug().flips()) { + std::cerr << "-- facet " << display_facet(facet) + << " is protected, cannot flip to make edge (" << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation\n"; + } + } else { + auto [v0, v1, v2] = this->vertices(facet); + auto opt_edge_preventing_flip = this->edge_preventing_the_flip(c, ia); + if(!opt_edge_preventing_flip.has_value()) + { + this->flip_flippable(facet); + visitor.after_flip23(va, vb, facet); + if(debug().flips()) { + std::cerr << "flipped facet " << display_facet(v0, v1, v2) << " to make edge (" + << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation\n"; + } + this->is_Delaunay = false; + CGAL_assertion(this->is_edge(va, vb)); + return true; + } + else + { + int edge_index = opt_edge_preventing_flip.value(); + Edge edge_preventing_flip{c, + this->vertex_triple_index(ia, this->ccw(edge_index)), + this->vertex_triple_index(ia, this-> cw(edge_index))}; + if(debug().flips()) { + std::cerr << "-- flip failed to make edge (" << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation: edge ( " + << display_vert(c->vertex(edge_preventing_flip.second)) << " " + << display_vert(c->vertex(edge_preventing_flip.third)) << " ) is in the way\n"; + } + // remove_edge_by_flips(edge_preventing_flip, visitor); + } + } + } + return false; + } + /// Return `true` if a Steiner point was inserted template bool conform_subconstraint(Subconstraint subconstraint, @@ -926,60 +1019,31 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { const Vertex_handle va = subconstraint.first; const Vertex_handle vb = subconstraint.second; CGAL_assertion(va != vb); - if(!this->is_edge(va, vb)) { - if(options().use_flips_to_recover_segments()) { - // try to flip facet crossed by [va, vb] to make it an - // edge of the triangulation - std::vector inc_cells; - this->incident_cells(va, std::back_inserter(inc_cells)); - for(auto c : inc_cells) - { - int ia = c->index(va); - if(c->neighbor(ia)->has_vertex(vb)) - { - Facet facet{c, ia}; - if(visitor.is_facet_protected(facet)) { - std::cerr << "-- facet " << display_facet(facet) - << " is protected, cannot flip to make edge (" << display_vert(va) << ", " << display_vert(vb) - << ") an edge in the triangulation\n"; - break; - } - auto [v0, v1, v2] = this->vertices(facet); - if(this->flip(facet)) - { - visitor.after_flip23(va, vb, facet); - std::cerr << "flipped facet " << display_facet(v0, v1, v2) << " to make edge (" - << display_vert(va) << ", " << display_vert(vb) - << ") an edge in the triangulation\n"; - this->is_Delaunay = false; - CGAL_assertion(this->is_edge(va, vb)); - return true; - } - else - { - std::cerr << "-- flip failed to make edge (" << display_vert(va) << ", " << display_vert(vb) - << ") an edge in the triangulation\n"; - } - break; - } - } + if(this->is_edge(va, vb)) { + if(debug().subconstraints_to_conform()) { + std::cerr << "conform_subconstraint>> subconstraint " << display_subcstr(subconstraint) + << " is already an edge in the triangulation.\n"; } - const auto& [steiner_pt, hint, ref_vertex] = construct_Steiner_point(constraint, subconstraint); - [[maybe_unused]] const auto v = - insert_Steiner_point_on_subconstraint(steiner_pt, hint, subconstraint, constraint, visitor); - if(debug().Steiner_points()) { - const auto [c_start, c_end] = constraint_extremities(constraint); - std::cerr << "(" << with_offset(va) << ", " << with_offset(vb) << ")"; - std::cerr << ": [ " << display_vert(c_start) << " - " << display_vert(c_end) << " ] "; - std::cerr << " new vertex " << display_vert(v) << '\n'; + return false; + } + if(options().use_flips_to_recover_segments() && recover_subconstraint_by_flips(subconstraint, constraint, visitor)) + { + if(debug().subconstraints_to_conform()) { + std::cerr << "conform_subconstraint>> subconstraint " << display_subcstr(subconstraint) + << " was recovered by flips.\n"; } return true; - } else if(debug().subconstraints_to_conform()) { - std::cerr << "conform_subconstraint>> subconstraint " << display_subcstr(subconstraint) - << " is already an edge in the triangulation.\n"; } - - return false; + const auto& [steiner_pt, hint, ref_vertex] = construct_Steiner_point(constraint, subconstraint); + [[maybe_unused]] const auto v = + insert_Steiner_point_on_subconstraint(steiner_pt, hint, subconstraint, constraint, visitor); + if(debug().Steiner_points()) { + const auto [c_start, c_end] = constraint_extremities(constraint); + std::cerr << "(" << with_offset(va) << ", " << with_offset(vb) << ")"; + std::cerr << ": [ " << display_vert(c_start) << " - " << display_vert(c_end) << " ] "; + std::cerr << " new vertex " << display_vert(v) << '\n'; + } + return true; } public: diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 72d6828443c8..10fcd4448af6 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -114,6 +114,7 @@ struct CDT_options bool debug_input_faces = false; bool debug_missing_regions = false; bool debug_missing_triangles = false; + bool debug_flips = false; bool debug_regions = false; bool debug_copy_triangulation_into_hole = false; bool debug_validity = false; @@ -209,6 +210,7 @@ CDT_options::CDT_options(int argc, char* argv[]) { app.add_flag("--debug-input-faces", debug_input_faces, "debug input faces"); app.add_flag("--debug-missing-regions", debug_missing_regions, "debug missing regions"); app.add_flag("--debug-missing-triangles", debug_missing_triangles, "debug missing triangles"); + app.add_flag("--debug-flips", debug_flips, "debug flips"); app.add_flag("--debug-regions", debug_regions, "debug regions"); app.add_flag("--debug_copy_triangulation_into_hole", debug_copy_triangulation_into_hole, "debug copy_triangulation_into_hole"); @@ -353,6 +355,7 @@ CGAL::CDT_3::Debug_options cdt_debug_options(const CDT_options& options) { cdt_debug.input_faces(options.debug_input_faces); cdt_debug.missing_region(options.verbose_level > 1 || options.debug_missing_regions); cdt_debug.missing_triangles(options.debug_missing_triangles); + cdt_debug.flips(options.debug_flips); cdt_debug.regions(options.debug_regions); cdt_debug.validity(options.debug_validity); cdt_debug.finite_edges_map(options.debug_finite_edges_map); diff --git a/Triangulation_3/include/CGAL/Triangulation_3.h b/Triangulation_3/include/CGAL/Triangulation_3.h index 5321eed713fd..3329e2e3b400 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3.h +++ b/Triangulation_3/include/CGAL/Triangulation_3.h @@ -1103,6 +1103,9 @@ class Triangulation_3 // c will be replaced by one of the new cells return flip(f.first, f.second); } + std::optional edge_preventing_the_flip(Cell_handle c, int i) const; + bool is_flippable(const Facet& f) const { return is_flippable(f.first, f.second); } + bool is_flippable(Cell_handle c, int i) const; bool flip(Cell_handle c, int i); void flip_flippable(const Facet& f) { flip_flippable(f.first, f.second); } void flip_flippable(Cell_handle c, int i); @@ -3749,6 +3752,18 @@ template < class GT, class Tds, class Lds > bool Triangulation_3:: flip(Cell_handle c, int i) +{ + if(!is_flippable(c, i)) + return false; + + _tds.flip_flippable(c, i); + return true; +} + +template < class GT, class Tds, class Lds > +std::optional +Triangulation_3:: +edge_preventing_the_flip(Cell_handle c, int i) const { CGAL_precondition((dimension() == 3) && (0<=i) && (i<4) && (number_of_vertices() >= 5)); @@ -3756,51 +3771,33 @@ flip(Cell_handle c, int i) Cell_handle n = c->neighbor(i); int in = n->index(c); if(is_infinite(c) || is_infinite(n)) - return false; - - if(i%2 == 1) - { - if(orientation(c->vertex((i+1)&3)->point(), - c->vertex((i+2)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) != POSITIVE) - return false; - - if(orientation(c->vertex((i+2)&3)->point(), - c->vertex((i+3)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) != POSITIVE) - return false; - - if(orientation(c->vertex((i+3)&3)->point(), - c->vertex((i+1)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) != POSITIVE) - return false; - } - else - { - if(orientation(c->vertex((i+2)&3)->point(), - c->vertex((i+1)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) != POSITIVE) - return false; - - if(orientation(c->vertex((i+3)&3)->point(), - c->vertex((i+2)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) != POSITIVE) - return false; - - if(orientation(c->vertex((i+1)&3)->point(), - c->vertex((i+3)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) != POSITIVE) - return false; - } + return { -1 }; + + Vertex_handle va = c->vertex(i); + Vertex_handle vb = n->vertex(in); + auto face_vert = vertices(Facet{c, i}); + + if(orientation(face_vert[0]->point(), + face_vert[1]->point(), + vb->point(), + va->point()) != POSITIVE) return {2}; + if(orientation(face_vert[1]->point(), + face_vert[2]->point(), + vb->point(), + va->point()) != POSITIVE) return {0}; + if(orientation(face_vert[2]->point(), + face_vert[0]->point(), + vb->point(), + va->point()) != POSITIVE) return {1}; + return std::nullopt; +} - _tds.flip_flippable(c, i); - return true; +template < class GT, class Tds, class Lds > +bool +Triangulation_3:: +is_flippable(Cell_handle c, int i) const +{ + return edge_preventing_the_flip(c, i) == std::nullopt; } template < class GT, class Tds, class Lds > @@ -3808,43 +3805,7 @@ void Triangulation_3:: flip_flippable(Cell_handle c, int i) { - CGAL_precondition((dimension() == 3) && (0<=i) && (i<4) && - (number_of_vertices() >= 5)); - CGAL_precondition_code(Cell_handle n = c->neighbor(i);); - CGAL_precondition_code(int in = n->index(c);); - CGAL_precondition((! is_infinite(c)) &&(! is_infinite(n))); - - if(i%2 == 1) - { - CGAL_precondition(orientation(c->vertex((i+1)&3)->point(), - c->vertex((i+2)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) == POSITIVE); - CGAL_precondition(orientation(c->vertex((i+2)&3)->point(), - c->vertex((i+3)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) == POSITIVE); - CGAL_precondition(orientation(c->vertex((i+3)&3)->point(), - c->vertex((i+1)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) == POSITIVE); - } - else - { - CGAL_precondition(orientation(c->vertex((i+2)&3)->point(), - c->vertex((i+1)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) == POSITIVE); - CGAL_precondition(orientation(c->vertex((i+3)&3)->point(), - c->vertex((i+2)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) == POSITIVE); - CGAL_precondition(orientation(c->vertex((i+1)&3)->point(), - c->vertex((i+3)&3)->point(), - n->vertex(in)->point(), - c->vertex(i)->point()) == POSITIVE); - } - + CGAL_precondition(is_flippable(c, i)); _tds.flip_flippable(c, i); } From 57293827e19785fdc3a11017266bfde3b5280c20 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 26 Aug 2026 10:16:01 +0200 Subject: [PATCH 092/151] wip --- .../Conforming_Delaunay_triangulation_3.h | 103 ++++++++++-------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index de36d15b4161..5bb43fc05b62 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -946,8 +946,11 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { return std::nullopt; } - bool remove_edge_by_flips(Edge edge_preventing_flip, auto& visitor) + template + bool remove_edge_by_flips(Edge edge_preventing_flip, Visitor& visitor) { + CGAL_USE(edge_preventing_flip); + CGAL_USE(visitor); return false; } @@ -960,53 +963,67 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { const Vertex_handle va = subconstraint.first; const Vertex_handle vb = subconstraint.second; CGAL_assertion(va != vb); - const auto cell_opt = find_in_incident_cells(va, [&](Cell_handle c) { - int ia = c->index(va); - return c->neighbor(ia)->has_vertex(vb); - }); - if(cell_opt.has_value()) - { - auto c = cell_opt.value(); - int ia = c->index(va); - Facet facet{c, ia}; - if(visitor.is_facet_protected(facet)) { - if(debug().flips()) { - std::cerr << "-- facet " << display_facet(facet) - << " is protected, cannot flip to make edge (" << display_vert(va) << ", " << display_vert(vb) - << ") an edge in the triangulation\n"; - } - } else { - auto [v0, v1, v2] = this->vertices(facet); - auto opt_edge_preventing_flip = this->edge_preventing_the_flip(c, ia); - if(!opt_edge_preventing_flip.has_value()) - { - this->flip_flippable(facet); - visitor.after_flip23(va, vb, facet); - if(debug().flips()) { - std::cerr << "flipped facet " << display_facet(v0, v1, v2) << " to make edge (" - << display_vert(va) << ", " << display_vert(vb) - << ") an edge in the triangulation\n"; + auto simplex_it = tr().segment_traverser_simplices_begin(va, vb); + while(true) { + switch(simplex_it->dimension()) { + case 0: { + const auto v = static_cast(*simplex_it); + if(v == vb) { + return true; } - this->is_Delaunay = false; - CGAL_assertion(this->is_edge(va, vb)); - return true; + CGAL_assertion(v == va); + ++simplex_it; + break; } - else - { - int edge_index = opt_edge_preventing_flip.value(); - Edge edge_preventing_flip{c, - this->vertex_triple_index(ia, this->ccw(edge_index)), - this->vertex_triple_index(ia, this-> cw(edge_index))}; - if(debug().flips()) { - std::cerr << "-- flip failed to make edge (" << display_vert(va) << ", " << display_vert(vb) - << ") an edge in the triangulation: edge ( " - << display_vert(c->vertex(edge_preventing_flip.second)) << " " - << display_vert(c->vertex(edge_preventing_flip.third)) << " ) is in the way\n"; + case 1: return false; + case 2: { + const auto facet = tr().mirror_facet(static_cast(*simplex_it)); + const auto [c, facet_index] = facet; + CGAL_assertion(c->vertex(facet_index) == va); + if(visitor.is_facet_protected(facet)) { + if(debug().flips()) { + std::cerr << "-- facet " << display_facet(facet) + << " is protected, cannot flip to make edge (" << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation\n"; + } + return false; + } + const auto [v0, v1, v2] = tr().vertices(facet); + const auto opt_edge_preventing_flip = tr().edge_preventing_the_flip(c, facet_index); + if(!opt_edge_preventing_flip.has_value()) + { + const auto [other_cell, other_index] = tr().mirror_facet(facet); + const auto next_v = other_cell->vertex(other_index); + tr().flip_flippable(facet); + visitor.after_flip23(va, next_v, facet); + if(debug().flips()) { + std::cerr << "flipped facet " << display_facet(v0, v1, v2) << " to make edge (" + << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation\n"; + } + this->is_Delaunay = false; + simplex_it = tr().segment_traverser_simplices_begin(va, vb); + break; + } + else + { + int edge_index = opt_edge_preventing_flip.value(); + Edge edge_preventing_flip{c, + tr().vertex_triple_index(facet_index, tr().ccw(edge_index)), + tr().vertex_triple_index(facet_index, tr(). cw(edge_index))}; + if(debug().flips()) { + std::cerr << "-- flip failed to make edge (" << display_vert(va) << ", " << display_vert(vb) + << ") an edge in the triangulation: edge ( " + << display_vert(c->vertex(edge_preventing_flip.second)) << ", " + << display_vert(c->vertex(edge_preventing_flip.third)) << " ) is in the way\n"; + } + return false; + // remove_edge_by_flips(edge_preventing_flip, visitor); } - // remove_edge_by_flips(edge_preventing_flip, visitor); } + case 3: ++simplex_it; break; } - } + } // end while(true) return false; } From 1f9d5bd6ce900ba8fac8602e173a250dcee9c13d Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 26 Aug 2026 11:32:07 +0200 Subject: [PATCH 093/151] fix iwyu warnings --- .../include/CGAL/Triangulation_3.h | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Triangulation_3/include/CGAL/Triangulation_3.h b/Triangulation_3/include/CGAL/Triangulation_3.h index 3329e2e3b400..4006a8fa3245 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3.h +++ b/Triangulation_3/include/CGAL/Triangulation_3.h @@ -28,6 +28,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include @@ -46,13 +52,15 @@ #include #include +#include +#include #include #include #include -#include +#include #include +#include #include -#include #ifndef CGAL_TRIANGULATION_3_DONT_INSERT_RANGE_OF_POINTS_WITH_INFO #include @@ -69,14 +77,19 @@ # include #endif +#include +#include #include +#include #include -#include #include +#include +#include +#include +#include #include #include -#include -#include +#include #define CGAL_TRIANGULATION_3_USE_THE_4_POINTS_CONSTRUCTOR @@ -4497,7 +4510,7 @@ test_dim_down(Vertex_handle v) const } else // dimension() == 1 or 0 { - return number_of_vertices() == (size_type) dimension() + 1; + return number_of_vertices() == static_cast(dimension()) + 1; } return true; From 7979bb80cf8131bd96edd10bf43d784de004423f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 26 Aug 2026 17:21:56 +0200 Subject: [PATCH 094/151] improve debuigging of extra cstrained facets at the end --- .../Conforming_constrained_Delaunay_triangulation_3.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index faf1d48dccfa..98fb779b295e 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -5258,18 +5258,26 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D bool has_extra_constrained_facets() const { bool result = true; + const bool debug = this->debug().flips() || this->debug().restore_faces() || this->debug().conforming_validation(); + std::vector extra_facets; for(const auto facet : constrained_facets()) { if(constrained_facet_has_2d_triangle(facet)) { continue; } result = false; - if(this->debug().restore_faces() || this->debug().conforming_validation()) { + if(debug) { + extra_facets.push_back(facet); std::cerr << "ERROR: constrained 3D facet" << display_facet(" ", facet, With_point_and_info_tag{}) << "\n has no corresponding 2D triangle in face #" << face_constraint_index(facet) << '\n'; } } + if(debug && !extra_facets.empty()) { + std::ofstream dump("dump_extra_constrained_facets.off"); + dump.precision(17); + write_facets(dump, *this, extra_facets); + } return result; } From 16f5d41f347e9c60b2220fc74cc5fcebd92b547c Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 26 Aug 2026 17:23:49 +0200 Subject: [PATCH 095/151] fix bug about extra constrained facets --- ...ing_constrained_Delaunay_triangulation_3.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 98fb779b295e..4621f2dc9a90 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1646,7 +1646,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return with_point_and_info(v); } - template + template auto display_facet(std::string_view prefix, Facet f, Tag tag = {}) const { return IO::oformat( [prefix, tag, vertices = tr().vertices(f), v4 = f.first->vertex((f.second))](auto& out) -> auto& { @@ -1699,6 +1699,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D { CGAL_assertion(fh == CDT_2_face_handle{} || same_triple(f, fh, o)); CGAL_assertion(o == EQUAL); + CGAL_assertion(fh == CDT_2_face_handle{} || fh->info().is_outside_the_face != OUTSIDE); const auto [c, facet_index] = f; c->ccdt_3_data().set_facet_constraint(facet_index, polygon_constraint_id, fh); @@ -2923,12 +2924,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto ib = cell->index(vb); for(auto index : make_array(ia, ib)) { const Facet f{cell, index}; - const auto [mirror_cell, mirror_index] = self->mirror_facet(f); + const Facet mirror_facet = self->mirror_facet(f); + const auto [mirror_cell, mirror_index] = mirror_facet; if(mirror_cell->ccdt_3_data().is_facet_constrained(mirror_index)) { auto face_id = self->face_constraint_index(mirror_cell, mirror_index); const CDT_2& cdt_2 = self->face_cdt_2(face_id); const auto f2d = mirror_cell->ccdt_3_data().face_2(cdt_2, mirror_index); - self->set_facet_constrained(f, face_id, f2d); + Facet oriented_facet = self->same_triple(f, f2d) ? f : mirror_facet; + self->set_facet_constrained(oriented_facet, face_id, f2d); } else { self->set_facet_as_not_constrained(f); } @@ -2991,11 +2994,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D do { auto is_facet = self->is_2d_face_also_a_3d_facet(fc); if(!is_facet) { - fc->info().missing_subface = true; + fc->info().missing_subface = (fc->info().is_outside_the_face != OUTSIDE); } else { - const auto [facet, orient] = *is_facet; - fc->info().missing_subface = false; - self->set_facet_constrained(facet, polygon_id, fc, orient); + if((fc->info().is_outside_the_face != OUTSIDE)) { + const auto [facet, orient] = *is_facet; + fc->info().missing_subface = false; + self->set_facet_constrained(facet, polygon_id, fc, orient); + } } const auto v_Steiner_index = fc->index(v_Steiner_2d); const auto other_edge = cdt_2.mirror_edge({fc, v_Steiner_index}); From 55ba6229232b82660cbc35fa56f1e70744e91997 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 26 Aug 2026 17:24:54 +0200 Subject: [PATCH 096/151] wip flips --- .../Conforming_Delaunay_triangulation_3.h | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 5bb43fc05b62..1aae61359ee4 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -949,6 +949,18 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { template bool remove_edge_by_flips(Edge edge_preventing_flip, Visitor& visitor) { + const auto [va, vb] = tr().vertices(edge_preventing_flip); + if(is_a_constrained_edge(va, vb)) { + if(debug().flips()) { + std::cerr << "-- edge (" << display_vert(va) << ", " << display_vert(vb) + << ") is a constrained edge, cannot remove it\n"; + } + return false; + } + if(debug().flips()) { + std::cerr << "-- edge (" << display_vert(va) << ", " << display_vert(vb) + << ") is NOT a constrained edge\n"; + } CGAL_USE(edge_preventing_flip); CGAL_USE(visitor); return false; @@ -975,7 +987,17 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { ++simplex_it; break; } - case 1: return false; + case 1: { + const auto [v0, v1] = tr().vertices(static_cast(*simplex_it)); + if( ( v0 == va && v1 == vb) || (v0 == vb && v1 == va) ) { + if(debug().flips()) { + std::cerr << "-- flip restored edge (" << display_vert(va) << ", " << display_vert(vb) << ")\n"; + } + return true; + } else { + return false; + } + } case 2: { const auto facet = tr().mirror_facet(static_cast(*simplex_it)); const auto [c, facet_index] = facet; @@ -1017,8 +1039,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { << display_vert(c->vertex(edge_preventing_flip.second)) << ", " << display_vert(c->vertex(edge_preventing_flip.third)) << " ) is in the way\n"; } - return false; - // remove_edge_by_flips(edge_preventing_flip, visitor); + return remove_edge_by_flips(edge_preventing_flip, visitor); } } case 3: ++simplex_it; break; From da0f37e7832235647d24c62c42357507abe7b51b Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 26 Aug 2026 18:10:41 +0200 Subject: [PATCH 097/151] restore compilation with Epeck --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 4621f2dc9a90..1ebdffa59d8a 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2697,10 +2697,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto kernel_center_point = *opt_kernel_center_point; central_points->push_back(kernel_center_point); if(dbg().move_Steiner_vertices_level() > 1) { - std::cerr << *opt_kernel_center_point - << " (distance to vertex: " << CGAL::sqrt(CGAL::squared_distance(kernel_center_point, v->point())) - << ")" - << "\n"; + std::cerr << *opt_kernel_center_point << " (distance to vertex: " + << CGAL::approximate_sqrt(CGAL::squared_distance(kernel_center_point, v->point())) << ")" + << "\n"; } } else { if(dbg().move_Steiner_vertices_level() > 1) { From b1290c62a1dff1f58306a995fee6ece23934165f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 28 Aug 2026 14:56:39 +0200 Subject: [PATCH 098/151] bug fixes --- .../CGAL/Conforming_Delaunay_triangulation_3.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 1aae61359ee4..256b60cbfe1c 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -999,8 +999,15 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { } } case 2: { - const auto facet = tr().mirror_facet(static_cast(*simplex_it)); + auto facet = static_cast(*simplex_it); + if(facet.first->vertex(facet.second) != va) { + facet = tr().mirror_facet(facet); + } const auto [c, facet_index] = facet; + if(c->vertex(facet_index) != va && c->has_vertex(va)) { + // the segment is coplanar with the facet + return false; + } CGAL_assertion(c->vertex(facet_index) == va); if(visitor.is_facet_protected(facet)) { if(debug().flips()) { @@ -1397,6 +1404,12 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { } const auto vector_of_encroaching_vertices = encroaching_vertices(va, vb); + if(vector_of_encroaching_vertices.empty()) { + if(debug().Steiner_points_construction()) { + std::cerr << " -> no encroaching vertices, using midpoint\n"; + } + return {exact(midpoint_functor(pa, pb)), va->cell(), va}; + } CGAL_assertion(vector_of_encroaching_vertices.size() > 0); const auto reference_vertex_it = std::max_element( From b76b715b7cd9e2d71db8bfbd0acd106be77f1787 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 1 Sep 2026 10:25:55 +0200 Subject: [PATCH 099/151] improve display functions to deal with null facets --- ...onforming_constrained_Delaunay_triangulation_3.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 1ebdffa59d8a..cbf6bd1ad2f6 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1648,8 +1648,17 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D template auto display_facet(std::string_view prefix, Facet f, Tag tag = {}) const { + std::array vertices = { Vertex_handle{} , Vertex_handle{} , Vertex_handle{} }; + Vertex_handle v4 = {}; + if(f.first != Cell_handle{}) { + vertices = tr().vertices(f); + v4 = f.first->vertex((f.second)); + } return IO::oformat( - [prefix, tag, vertices = tr().vertices(f), v4 = f.first->vertex((f.second))](auto& out) -> auto& { + [prefix, tag, vertices, v4](auto& out) -> auto& { + if(v4 == Vertex_handle{}) { + return out << '\n' << prefix << "null facet"; + } return out << cdt_3_format("\n{}{}" "\n{}{}" "\n{}{}" @@ -2136,7 +2145,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D display_vert(vertex_3d(first_2d_face->vertex(2)))); std::cerr << cdt_3_format("- front_cell is ({}) with vertices:{}\n", IO::oformat(front_cell), - display_facet(" - {}\n", Facet{front_cell, front_index})) + display_facet(" - ", Facet{front_cell, front_index})) << "\n- back vertex is "<< display_vert(v_back) << '\n'; } From 9257a04ac63755f111dc1ad19fa8ee840ac90e44 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 4 Sep 2026 18:17:22 +0200 Subject: [PATCH 100/151] bug fix --- ...ing_constrained_Delaunay_triangulation_3.h | 190 +++++++++++------- 1 file changed, 113 insertions(+), 77 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index cbf6bd1ad2f6..ad3ddf3648ae 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1503,6 +1503,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Facet = typename T_3::Facet; using Cell_handle = typename T_3::Cell_handle; + using Facet_pair = std::array; + static Facet_pair null_facet_pair() { return{Facet{}, Facet{}}; }; + using Geom_traits = typename T_3::Geom_traits; using Point_3 = typename T_3::Point; using Segment_3 = typename Geom_traits::Segment_3; @@ -1657,7 +1660,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return IO::oformat( [prefix, tag, vertices, v4](auto& out) -> auto& { if(v4 == Vertex_handle{}) { - return out << '\n' << prefix << "null facet"; + return out << "null facet"; } return out << cdt_3_format("\n{}{}" "\n{}{}" @@ -1728,12 +1731,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D protected: void set_adjacency_between_3d_facets(Facet f1, Facet f2) const { + // } tr().tds().set_adjacency(f1.first, f1.second, f2.first, f2.second); if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << cdt_3_format("-> set adjacency between facets ({})=[{}]+{} and ({})=[{}]+{}\n", - with_offset(f1), with_offset(tr().vertices(f1)), + IO::oformat(f1), with_offset(tr().vertices(f1)), with_offset(f1.first->vertex(f1.second)), - with_offset(f2), with_offset(tr().vertices(f2)), + IO::oformat(f2), with_offset(tr().vertices(f2)), with_offset(f2.first->vertex(f2.second))); CGAL_assertion(opposite_triple(f1, f2)); } @@ -2069,7 +2073,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D enum Run_mode { NORMAL_RUN, DRY_RUN }; template - std::pair + std::pair fill_cdt_2_hole_with_delaunay(CDT_2& cdt_2, Face_index face_index, CDT_3_vertex_type vertex_type, @@ -2101,41 +2105,69 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }); }); + if(is_outside_the_face == OUTSIDE) { + if(hole_boundary.size() <= 2) { // degenerated hole + CGAL_assertion(hole_boundary.size() == 2); // size == 1 is not possible + auto [f0, i0] = hole_boundary.front(); + auto [f1, i1] = hole_boundary.back(); + + cdt_2.tds().set_adjacency(f1, i1, f0, i0); + } else { + CGAL::fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { + auto& info = fh->info(); + info.is_outside_the_face = OUTSIDE; + info.missing_subface = false; + info.is_in_region = 0; + info.facet_3d = {}; + })); + } + return {new_face_handles_out, null_facet_pair()}; + } + + const CDT_2_face_handle half_hole_neighbor_face = is_a_half_hole ? hole_boundary.back().first : CDT_2_face_handle{}; + const auto [outside_2d_face, outside_2d_index] = hole_boundary.front(); + const auto first_2d_face = outside_2d_face->neighbor(outside_2d_index); + const auto v0 = vertex_3d(outside_2d_face->vertex(cdt_2.cw(outside_2d_index))); + const auto v1 = vertex_3d(outside_2d_face->vertex(cdt_2.ccw(outside_2d_index))); + const auto front_facet = first_2d_face->info().facet_3d; + const auto [front_cell, front_index] = front_facet; + CGAL_assertion(front_cell != Cell_handle{}); + const auto back_facet = tr().mirror_facet(front_facet); + const auto [back_cell, back_index] = back_facet; + + const auto index_v_front = 6 - front_index - front_cell->index(v0) - front_cell->index(v1); + const Vertex_handle v_front = front_cell->vertex(index_v_front); + const Vertex_handle v_back = back_cell->vertex(6 - back_index - back_cell->index(v0) - back_cell->index(v1)); + if(hole_boundary.size() <= 2) { // degenerated hole CGAL_assertion(hole_boundary.size() == 2); // size == 1 is not possible auto [f0, i0] = hole_boundary.front(); auto [f1, i1] = hole_boundary.back(); + cdt_2.tds().set_adjacency(f1, i1, f0, i0); - return {new_face_handles_out, {}}; - } - if(is_outside_the_face == OUTSIDE) { - CGAL::fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { - auto& info = fh->info(); - info.is_outside_the_face = OUTSIDE; - info.missing_subface = false; - info.is_in_region = 0; - info.facet_3d = {}; - })); - return {new_face_handles_out, {}}; + if(debug) { + std::cerr << "Degenerated hole with edges: (f0, " << i0 << ") and (f1, " << i1 << ")\n"; + std::cerr << "f0 is: " + << display_vert(vertex_3d(f0->vertex(0))) << "\n " + << display_vert(vertex_3d(f0->vertex(1))) << "\n " + << display_vert(vertex_3d(f0->vertex(2))) << "\n"; + std::cerr << "f1 is: " + << display_vert(vertex_3d(f1->vertex(0))) << "\n " + << display_vert(vertex_3d(f1->vertex(1))) << "\n " + << display_vert(vertex_3d(f1->vertex(2))) << "\n"; + std::cerr << "front_facet is " << IO::oformat(front_facet) << display_facet(" - ", front_facet) << "\n"; + std::cerr << "back_facet is " << IO::oformat(back_facet) << display_facet(" - ", back_facet) << "\n"; + } + + return {new_face_handles_out, Facet_pair{front_facet, back_facet}}; } - const CDT_2_face_handle half_hole_neighbor_face = is_a_half_hole ? hole_boundary.back().first : CDT_2_face_handle{}; - Facet half_hole_front_facet{Cell_handle{}, 0}; + Facet_pair half_hole_facet_pair = null_facet_pair(); std::for_each(hole_boundary.begin(), hole_boundary.end(), [](const CDT_2_edge& e) { e.first->info().is_in_region = 0; }); - const auto [outside_2d_face, outside_2d_index] = hole_boundary.front(); - const auto first_2d_face = outside_2d_face->neighbor(outside_2d_index); - const auto v0 = vertex_3d(outside_2d_face->vertex(cdt_2.cw(outside_2d_index))); - const auto v1 = vertex_3d(outside_2d_face->vertex(cdt_2.ccw(outside_2d_index))); - const auto [front_cell, front_index] = first_2d_face->info().facet_3d; - CGAL_assertion(front_cell != Cell_handle{}); - const auto [back_cell, back_index] = tr().mirror_facet({front_cell, front_index}); - - const Vertex_handle v_front = front_cell->vertex(6 - front_index - front_cell->index(v0) - front_cell->index(v1)); - const Vertex_handle v_back = back_cell->vertex(6 - back_index - back_cell->index(v0) - back_cell->index(v1)); if(debug) { std::cerr << cdt_3_format("- first_2d_face has vertices ( {} ,\n" " {} ,\n" @@ -2182,6 +2214,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D set_facet_constrained({new_front_cell, 3}, face_index, fh_2d); for(int i = 0; i < 3; ++i) { + const int back_cell_i = (3 - i) % 3; // the index of the neighbor in `new_back_cell` is deduced from the + // fact that `new_back_cell` was created with vertices in reversed + // order compared to `new_front_cell` const CDT_2_face_handle neighbor_2d = fh_2d->neighbor(i); if(debug) { std::cerr << "-- explore neighbor(" << i << ") --\n"; @@ -2195,12 +2230,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D continue; } else if(neighbor_2d == half_hole_neighbor_face) { // this edge of `fh_2d` is dandling to be connected to the other part of the half hole - CGAL_assertion(half_hole_front_facet.first == Cell_handle{}); // only one edge of the hole can be a half hole - half_hole_front_facet = {new_front_cell, i}; + CGAL_assertion(half_hole_facet_pair[0].first == Cell_handle{}); // only one edge of the hole can be a half hole + half_hole_facet_pair = Facet_pair{Facet{new_front_cell, i}, Facet{new_back_cell, back_cell_i}}; if(debug) { std::cerr << cdt_3_format("- the 3D facet (new_front_cell={}, i={}) is ON THE BORDER of the half hole:{}\n" , IO::oformat(new_front_cell), i , display_facet(" ", Facet{new_front_cell, i})); + std::cerr << cdt_3_format("- its back facet (new_back_cell={}, i={}) is ON THE BORDER of the half hole:{}\n" + , IO::oformat(new_back_cell), back_cell_i + , display_facet(" ", Facet{new_back_cell, back_cell_i})); } continue; } @@ -2237,9 +2275,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D set_adjacency_between_3d_facets({new_front_cell, i}, {neighbor_front_cell_3d, fh_index_in_neighbor_2d}); - const int back_cell_i = (3 - i) % 3; // the index of the neighbor in `new_back_cell` is deduced from the - // fact that `new_back_cell` was created with vertices in reversed - // order compared to `new_front_cell` const int back_cell_fh_index_in_neighbor_2d = neighbor_2d_is_in_the_star ? (3 - fh_index_in_neighbor_2d) % 3 : neighbor_back_cell_3d->index(neighbor_front_cell_3d); @@ -2270,7 +2305,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D default: CGAL_unreachable(); } - return {new_face_handles_out, half_hole_front_facet}; + return {new_face_handles_out, half_hole_facet_pair}; } bool maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(Vertex_handle v, Face_index face_id) { @@ -2349,7 +2384,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } template - std::tuple + std::tuple remove_Steiner_vertex_from_cdt_2(Vertex_handle v, CDT_3_vertex_type vertex_type, Face_index face_id, @@ -2357,8 +2392,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D Run_mode run_mode = NORMAL_RUN) { const bool debug = dbg().move_Steiner_vertices_level() > 1 && run_mode == NORMAL_RUN; - std::tuple result{new_face_handles_out, {}, {}}; - auto& [new_face_handles_out_it, half_hole_front_facet, second_half_hole_front_facet] = result; + std::tuple result{ + new_face_handles_out, null_facet_pair(), null_facet_pair()}; + auto& [new_face_handles_out_it, half_hole_facet_pair, second_half_hole_facet_pair] = result; using CDT_2_base = typename CDT_2::Triangulation; auto& original_mutable_cdt_2 = static_cast(non_const_face_cdt_2(face_id)); @@ -2432,9 +2468,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return map; }; - auto bore_hole_and_fill_with_delaunay = [&](CDT_3_vertex_type vertex_type, auto& hole_boundary, - const auto& faces_to_delete, - Face_is_outside_the_face is_outside_the_face) { + auto carve_hole_and_fill_with_delaunay = [&](CDT_3_vertex_type vertex_type, auto& hole_boundary, + const auto& faces_to_delete, + Face_is_outside_the_face is_outside_the_face) + { if(debug) { std::cerr << "The star of " << display_vert(v) << " in the CDT_2 of face F#" << face_id << " has a component " << (is_outside_the_face == OUTSIDE ? "OUTSIDE the face " : "") << "with " @@ -2459,10 +2496,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto hole_boundary_map = map_from_hole_edges_to_incident_3d_facets(hole_boundary.begin(), hole_boundary_end, is_outside_the_face); + + auto result = + fill_cdt_2_hole_with_delaunay(mutable_cdt_2, face_id, vertex_type, hole_boundary, hole_boundary_map, + is_outside_the_face, new_face_handles_out, run_mode); + std::for_each(faces_to_delete.begin(), faces_to_delete.end(), [&mutable_cdt_2](CDT_2_face_handle f) { mutable_cdt_2.delete_face(f); }); - return fill_cdt_2_hole_with_delaunay(mutable_cdt_2, face_id, vertex_type, hole_boundary, hole_boundary_map, - is_outside_the_face, new_face_handles_out, run_mode); + + return result; }; auto set_constrained = [&](CDT_2_edge e, bool is_constrained) { @@ -2540,24 +2582,24 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D hole_side_1.push_back(side_2_last_incident_edge); // - bore the hole on side 1, and fill it with CDT triangles - auto [out_it, facet_side_1] = bore_hole_and_fill_with_delaunay( + auto [out_it, facet_pair_side_1] = carve_hole_and_fill_with_delaunay( vertex_type, hole_side_1, hole_side_1_faces_to_delete, side_1_hole_is_outside_the_face); new_face_handles_out = out_it; CGAL_assertion(run_mode == DRY_RUN || - side_1_hole_is_outside_the_face == OUTSIDE || facet_side_1.first != Cell_handle{}); + side_1_hole_is_outside_the_face == OUTSIDE || facet_pair_side_1[0].first != Cell_handle{}); // - now the edge opposite to `(side_2_last_face, side_2_last_edge_index)` is on the border of the hole // `hole_side_2`, so we can start sewing from there const auto side_1_border_edge = cdt_2.mirror_edge(CDT_2_edge{side_2_last_face, side_2_last_edge_index}); hole_side_2.push_back(side_1_border_edge); - auto [out_it_2, facet_side_2] = bore_hole_and_fill_with_delaunay( + auto [out_it_2, facet_pair_side_2] = carve_hole_and_fill_with_delaunay( vertex_type, hole_side_2, hole_side_2_faces_to_delete, side_2_hole_is_outside_the_face); new_face_handles_out = out_it_2; CGAL_assertion(run_mode == DRY_RUN || - side_2_hole_is_outside_the_face == OUTSIDE || facet_side_2.first != Cell_handle{}); + side_2_hole_is_outside_the_face == OUTSIDE || facet_pair_side_2[0].first != Cell_handle{}); - half_hole_front_facet = facet_side_1; - second_half_hole_front_facet = facet_side_2; + half_hole_facet_pair = facet_pair_side_1; + second_half_hole_facet_pair = facet_pair_side_2; // - then re-mark the edge between the two holes as a constrained edge set_constrained(side_1_border_edge, true); @@ -2584,10 +2626,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return result; }); - auto [out_it, facet] = - bore_hole_and_fill_with_delaunay(vertex_type, hole_boundary, faces_to_delete, INSIDE); + auto [out_it, facet_pair] = + carve_hole_and_fill_with_delaunay(vertex_type, hole_boundary, faces_to_delete, INSIDE); new_face_handles_out = out_it; - CGAL_assertion(facet.first == Cell_handle{}); + CGAL_assertion(facet_pair[0].first == Cell_handle{}); } mutable_cdt_2.delete_vertex(vh_2d); if(debug) { @@ -2606,6 +2648,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D bool move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { if(dbg().move_Steiner_vertices()) { + auto _ = IO::make_color_guards(std::cerr); std::cerr << "Moving Steiner vertex " << display_vert(v) << " to the volume\n"; } auto indent_guards = CGAL::IO::make_indenting_guards(dbg().move_Steiner_vertices_level() > 1 ? "| " : "", std::cerr); @@ -2768,18 +2811,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D star_component_new_center_vertex->set_cell(begin(component_facets)->cell); // cell of the first facet } // loop on each component of the star of `v` - auto other_side_of_cdt_2 = [&](const Facet& facet, Face_index face_id) -> Facet { - const auto [cell, facet_index] = facet; - for(int i = 0; i < 3; ++i) { - const auto other_facet_index = Tds::vertex_triple_index(facet_index, i); - if(face_constraint_index(Facet{cell, other_facet_index}) == face_id) { - const auto opposite_vertex = cell->vertex(facet_index); - const auto neighbor_cell = cell->neighbor(other_facet_index); - return Facet{neighbor_cell, neighbor_cell->index(opposite_vertex)}; - } - } - CGAL_error(); - }; std::size_t nb_of_new_2d_faces = 0; CGAL::Counting_output_iterator output_iterator{&nb_of_new_2d_faces}; @@ -2812,38 +2843,43 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "Now remove the Steiner vertex from the CDT_2 of face with id " << face_id << " (orientation: " << (orientation == CGAL::NEGATIVE ? "negative" : "positive") << ")\n"; } - auto [new_output_iterator, half_hole_front_facet, second_half_hole_front_facet] = + auto [new_output_iterator, half_hole_facet_pair, second_half_hole_facet_pair] = remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, output_iterator); output_iterator = new_output_iterator; - Facet facet_to_reconnect = half_hole_front_facet; - if(facet_to_reconnect.first == Cell_handle{}) { - facet_to_reconnect = second_half_hole_front_facet; + auto facet_pair_to_reconnect = half_hole_facet_pair; + if(facet_pair_to_reconnect[0].first == Cell_handle{}) { + facet_pair_to_reconnect = second_half_hole_facet_pair; } - CGAL_assertion(facet_to_reconnect.first != Cell_handle{}); - if(true == facet_to_reconnect.first->has_vertex(new_center_vertices[component_index])) { - facet_to_reconnect = other_side_of_cdt_2(facet_to_reconnect, face_id); + CGAL_assertion(facet_pair_to_reconnect[0].first != Cell_handle{}); + if(true == facet_pair_to_reconnect[0].first->has_vertex(new_center_vertices[component_index]) && + false == facet_pair_to_reconnect[1].first->has_vertex(new_center_vertices[component_index])) + { + if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << "SWAPPING facet pair to reconnect\n"; } + std::swap(facet_pair_to_reconnect[0], facet_pair_to_reconnect[1]); } if(dbg().move_Steiner_vertices_level() > 1) { std::cerr << cdt_3_format("The facet ({}) to reconnect in the 3D triangulation is:{}\n", - IO::oformat(facet_to_reconnect), display_facet(" - ", facet_to_reconnect)); - auto other_facet = other_side_of_cdt_2(facet_to_reconnect, face_id); + IO::oformat(facet_pair_to_reconnect[0]), + display_facet(" - ", facet_pair_to_reconnect[0])); std::cerr << cdt_3_format("The facet ({}) on the other side of the CDT_2 is:{}\n", - IO::oformat(other_facet), display_facet(" - ", other_facet)); + IO::oformat(facet_pair_to_reconnect[1]), + display_facet(" - ", facet_pair_to_reconnect[1])); } if(!last_facet_to_reconnect_to_opt.has_value()) { - last_facet_to_reconnect_to_opt = facet_to_reconnect; + last_facet_to_reconnect_to_opt = facet_pair_to_reconnect[0]; CGAL_assertion(previous_facet_to_reconnect_opt.has_value() == false); } - if(previous_facet_to_reconnect_opt.has_value()) { - set_adjacency_between_3d_facets(*previous_facet_to_reconnect_opt, facet_to_reconnect); + else if(previous_facet_to_reconnect_opt.has_value()) { + set_adjacency_between_3d_facets(previous_facet_to_reconnect_opt.value(), facet_pair_to_reconnect[0]); } - previous_facet_to_reconnect_opt = other_side_of_cdt_2(facet_to_reconnect, face_id); + previous_facet_to_reconnect_opt = facet_pair_to_reconnect[1]; } CGAL_assertion(previous_facet_to_reconnect_opt.has_value()); CGAL_assertion(last_facet_to_reconnect_to_opt.has_value()); - set_adjacency_between_3d_facets(*previous_facet_to_reconnect_opt, *last_facet_to_reconnect_to_opt); + set_adjacency_between_3d_facets(previous_facet_to_reconnect_opt.value(), + last_facet_to_reconnect_to_opt.value()); } if(dbg().move_Steiner_vertices_level() > 1) { From 31860abdd96a1fefb8ff462e246357483e96b1b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valque=20L=C3=A9o?= Date: Wed, 9 Sep 2026 12:37:36 +0200 Subject: [PATCH 101/151] Fix a bug when a vertex of a degenerate kernel may store an invalid halfedge after a plane cut --- .../include/CGAL/Polygon_mesh_processing/internal/clip_convex.h | 1 + 1 file changed, 1 insertion(+) diff --git a/PMP_Boolean_operations/include/CGAL/Polygon_mesh_processing/internal/clip_convex.h b/PMP_Boolean_operations/include/CGAL/Polygon_mesh_processing/internal/clip_convex.h index 68d6fe583e16..8f173c6b2409 100644 --- a/PMP_Boolean_operations/include/CGAL/Polygon_mesh_processing/internal/clip_convex.h +++ b/PMP_Boolean_operations/include/CGAL/Polygon_mesh_processing/internal/clip_convex.h @@ -795,6 +795,7 @@ clip_convex(PolygonMesh& pm, set_next(src_split, split_edge, pm); set_next(opposite(split_edge, pm), opposite(src_split, pm), pm); set_halfedge(*faces(pm).begin(), split_edge, pm); + set_halfedge(target(src_split, pm), src_split, pm); if(triangulate){ if constexpr(has_visitor){ Wrap_visitor_for_triangulation wrap_visitor(pm, visitor); From cfa0264c19222bee61814b6a1a2112ba737c6ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valque=20L=C3=A9o?= Date: Wed, 9 Sep 2026 13:03:50 +0200 Subject: [PATCH 102/151] Add a test for issue 9640 --- .../test/PMP_Boolean_operations/test_mesh_kernel.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/PMP_Boolean_operations/test/PMP_Boolean_operations/test_mesh_kernel.cpp b/PMP_Boolean_operations/test/PMP_Boolean_operations/test_mesh_kernel.cpp index e95b94743d08..ae6a3a04a6d8 100644 --- a/PMP_Boolean_operations/test/PMP_Boolean_operations/test_mesh_kernel.cpp +++ b/PMP_Boolean_operations/test/PMP_Boolean_operations/test_mesh_kernel.cpp @@ -323,6 +323,15 @@ void test_kernel_on_mesh(const Mesh &input, std::size_t expected_nb_vertices, st } } +template +void test_issue_9640(){ + Mesh m, out; + bool res = CGAL::IO::read_polygon_mesh("data-clip/data_issue_9640.off", m); + assert(res); + PMP::kernel(m, out); + assert(CGAL::is_valid_polygon_mesh(out)); +} + template void tests(){ using P = typename K::Point_3; @@ -498,6 +507,8 @@ void tests(){ std::cout << "Test clip full inside except one vertex" << std::endl; #endif test_clip_convex_on_mesh(m, opposite(pl), 8, 12, 6); + + test_issue_9640(); } int main(/*int argc, char** argv*/) From 853f6518a2b46ccaa92fabe14407fba45644b450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valque=20L=C3=A9o?= Date: Wed, 9 Sep 2026 13:30:20 +0200 Subject: [PATCH 103/151] The file for the test --- .../data-clip/data_issue_9640.off | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 PMP_Boolean_operations/test/PMP_Boolean_operations/data-clip/data_issue_9640.off diff --git a/PMP_Boolean_operations/test/PMP_Boolean_operations/data-clip/data_issue_9640.off b/PMP_Boolean_operations/test/PMP_Boolean_operations/data-clip/data_issue_9640.off new file mode 100644 index 000000000000..c5ff5efd7a84 --- /dev/null +++ b/PMP_Boolean_operations/test/PMP_Boolean_operations/data-clip/data_issue_9640.off @@ -0,0 +1,71 @@ +OFF +24 44 0 + +0.033080998799999997 -0.016919000100000001 0.027995502066309668 +0.033080998799999997 -0.016919000100000001 0.033080998799999997 +0.033393061132402528 -0.017175371963361934 0.028011564157286194 +0.034028001099999997 -0.017697000899999999 0.0323030017 +0.034028001099999997 -0.017697000899999999 0.017697000899999999 +0.033080998799999997 -0.016919000100000001 0.025048743936311672 +0.033473540210279422 -0.017238648897250582 0.024977435767456271 +0.03444574809566861 -0.017919628514840521 0.026445469133660738 +0.03252260903626264 -0.016239311981041508 0.027960518145194777 +0.034125653461625875 -0.01772879087198264 0.025260261995471683 +0.034056275840831873 -0.017701454352649811 0.026333452281319641 +0.035572566822660814 -0.01841445710503975 0.02657884810394421 +0.031725998999999998 -0.0148919998 0.031725998999999998 +0.034605982122789876 -0.017990729199942608 0.028070810470145952 +0.043696130750898912 -0.01173972950105415 0.027061807185675739 +0.031369999099999997 -0.013718999900000001 0.031369999099999997 +0.0323030017 -0.015971999600000002 0.0323030017 +0.035108 -0.018273999900000001 0.031725998999999998 +0.04053595190198403 -0.0070779126264824861 0.028747059218290072 +0.041669567976439531 -0.017123919775243406 0.02902896716505414 +0.036281000799999998 -0.018629999800000002 0.031369999099999997 +0.039392494420349283 -0.018422832207214156 0.02828496997522344 +0.035494340420194968 -0.018391145116812453 0.028110870381732281 +0.04068991574883022 -0.01784770740021413 0.028638763524932543 +3 0 2 1 +3 2 3 1 +3 2 4 3 +3 0 4 2 +3 5 7 6 +3 3 4 6 +3 0 8 7 +3 9 6 7 +3 6 10 3 +3 7 5 0 +3 7 8 11 +3 12 8 0 +3 6 9 10 +3 9 7 10 +3 3 10 13 +3 11 8 14 +3 7 11 13 +3 12 15 8 +3 16 12 0 +3 7 13 10 +3 13 17 3 +3 14 8 18 +3 13 11 14 +3 19 8 15 +3 12 17 15 +3 3 16 0 +3 17 12 16 +3 3 17 16 +3 13 20 17 +3 18 8 19 +3 21 14 18 +3 22 13 14 +3 15 13 19 +3 17 20 15 +3 0 1 3 +3 13 15 20 +3 18 19 23 +3 22 14 21 +3 21 18 23 +3 13 22 23 +3 23 19 13 +3 4 5 6 +3 0 5 4 +3 22 21 23 From beb727cb02670dbfeaa1f4555da7a5ebdf664e9c Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 15 Sep 2026 10:08:57 +0200 Subject: [PATCH 104/151] fix operator>> --- .../Conforming_constrained_Delaunay_triangulation_cell_base_3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h index 039912b48fc3..f44460b7b4b6 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h @@ -119,7 +119,7 @@ class Conforming_constrained_Delaunay_triangulation_cell_base_3 CGAL::read(is, i); } if(!is) return is; - c->ccdt_3_data().set_face_constraint_index(li, i); + c.ccdt_3_data().set_face_constraint_index(li, i); } return is; } From c57fd102a026afa0a672603e71aa93891f287078 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 15 Sep 2026 10:11:21 +0200 Subject: [PATCH 105/151] cosmetic: use `tr*().` instead of `this->` --- ...ing_constrained_Delaunay_triangulation_3.h | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index ad3ddf3648ae..3c34a4d9c083 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -3157,14 +3157,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto inner_map = tr().create_triangulation_inner_map( cavity_triangulation, map_cavity_vertices_to_ambient_vertices, the_infinite_vertex_is_in_the_cavity); - this->copy_triangulation_into_hole(map_cavity_vertices_to_ambient_vertices, - std::move(outer_map), - inner_map, - this->new_cells_output_iterator()); + tr().copy_triangulation_into_hole(map_cavity_vertices_to_ambient_vertices, + std::move(outer_map), + inner_map, + this->new_cells_output_iterator()); for(auto outside_facet : facets_of_cavity) { const auto [outside_cell, outside_face_index] = outside_facet; - const auto mirror_facet = this->mirror_facet(outside_facet); + const auto mirror_facet = tr().mirror_facet(outside_facet); if(outside_cell->ccdt_3_data().is_facet_constrained(outside_face_index)) { const auto polygon_id = outside_cell->ccdt_3_data().face_constraint_index(outside_face_index); const CDT_2& cdt_2 = face_cdt_2(polygon_id); @@ -3179,7 +3179,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D this->new_vertex(p_vh); - CGAL_assume(!this->debug().validity() || this->is_valid(true)); + CGAL_assertion(!this->debug().validity() || tr().tds().is_valid(true)); return p_vh; } @@ -5170,7 +5170,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D out.close(); } #endif // CGAL_CDT_3_CAN_USE_CXX20_FORMAT - this->copy_triangulation_into_hole(map_lower_cavity_vertices_to_ambient_vertices, std::move(outer_map), + tr().copy_triangulation_into_hole(map_lower_cavity_vertices_to_ambient_vertices, std::move(outer_map), lower_inner_map, this->new_cells_output_iterator()); } std::set cells_to_remove{cells_of_lower_cavity.begin(), cells_of_lower_cavity.end()}; @@ -5207,7 +5207,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } // CGAL_assertion(has_all_constrained_edges()); - CGAL_assume(!this->debug().validity() || this->is_valid(true)); + CGAL_assertion(!this->debug().validity() || tr().tds().is_valid(true)); return result; } @@ -5281,7 +5281,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D Facet facet{c, facet_index}; if(orient == CGAL::NEGATIVE) { - facet = this->mirror_facet(facet); + facet = tr().mirror_facet(facet); } CGAL_assertion(same_triple(facet, fh_2d)); return std::make_pair(facet, CGAL::EQUAL); @@ -5465,7 +5465,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } for(int i = 0; i < 3; ++i) { Facet other_f{cell, this->vertex_triple_index(facet_index, i)}; - Facet mirror_f = this->mirror_facet(other_f); + Facet mirror_f = tr().mirror_facet(other_f); if(cells_of_cavity.count(mirror_f.first) == 0) { facets_of_cavity_border.insert(mirror_f); } From 052ca160e550ca2360ae1a7852c2eb31f731cb98 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 15 Sep 2026 17:17:58 +0200 Subject: [PATCH 106/151] debug messages in Triangulation_segment_cell_iterator_3 --- .../Triangulation_segment_traverser_3_impl.h | 115 ++++++++++++++++-- .../CGAL/Triangulation_segment_traverser_3.h | 21 ++-- 2 files changed, 117 insertions(+), 19 deletions(-) diff --git a/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h b/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h index 2f75230ebbd8..bb144a3b51bf 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h +++ b/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h @@ -14,7 +14,23 @@ #include +#include + #include +#include +#include +#include +#include + +#include +#include +#include + +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 +# include +# include +# include +#endif namespace CGAL { @@ -257,8 +273,33 @@ walk_to_next() { walk_to_next_3_inf( inf ); else { +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + std::cerr << "Starting walk_to_next_3 loop with backup: " << debug_simplex(_cur) << '\n'; + std::cerr << "source: " << with_point(_s_vertex) << '\n'; + if(_s_vertex == Vertex_handle()) + std::cerr << " _source is " << _source << '\n'; + std::cerr << "target: " << with_point(_t_vertex) << '\n'; + if(_t_vertex == Vertex_handle()) + std::cerr << " _target is " << _target << '\n'; + std::size_t counter = 0; +#endif const Simplex backup = _cur; do { +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + ++counter; + std::cerr << debug_iterator() << '\n'; + if(counter >= _tr->number_of_cells()) + { + std::cerr << "Exceeded maximum number of cells in walk_to_next_3 loop.\n"; + std::cerr << "_s_vertex:\n" << _tr->point(_s_vertex) << '\n'; + std::cerr << "_t_vertex:\n" << _tr->point(_t_vertex) << '\n'; + std::ofstream dump("dump.binary.cgal", std::ios::binary); + IO::set_binary_mode(dump); + dump << *_tr; + dump.close(); + } + CGAL_assertion(counter < _tr->number_of_cells()); +#endif std::pair p = walk_to_next_3(_prev, _cur); _prev = p.first; _cur = p.second; @@ -335,6 +376,13 @@ std::pair::Simplex, Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& prev, const Simplex& cur) const { + auto returned = [](std::pair p) { +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + std::cerr << "walk_to_next_3 returns the pair:\n " << debug_simplex(p.first) + << "\n " << debug_simplex(p.second) << '\n'; +#endif + return p; + }; const auto cur_cell = cur.cell; std::array vert = {&(cur_cell->vertex(0)->point()), &(cur_cell->vertex(1)->point()), &(cur_cell->vertex(2)->point()), &(cur_cell->vertex(3)->point())}; @@ -463,7 +511,7 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre } if(!degenerate) { - return {prev_after_walk, cur_after_walk}; + return returned({prev_after_walk, cur_after_walk}); } } @@ -475,6 +523,15 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre int pos = 0; // We keep track of which orientations are calculated. bool calc[6] = {false, false, false, false, false, false}; +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + for(int li = 0; li < 4; ++li) + { + auto vertices = _tr->vertices(Facet{cur_cell, li}); + std::cerr << "o[" << li + << "] = " << _tr->orientation(vertices[0]->point(), vertices[1]->point(), vertices[2]->point(), _target) + << '\n'; + } +#endif if(cur.lt == Tr::VERTEX) { // The three planes through the vertex are set to coplanar. @@ -504,7 +561,13 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre pos += li; continue; } +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + std::cerr << "Test facet " << li + << ":\n " <vertices(Facet{cur_cell, li}), With_point_tag{}) << '\n'; +#endif const Point* const backup_vert_li = std::exchange(vert[li], &_target); + auto exit_guard = CGAL::make_scope_exit([&](){ vert[li] = backup_vert_li; }); + bool op_li_is_null = false; if(_t_vertex != Vertex_handle()) { for(int i = 0; i < 4; ++i) { @@ -514,10 +577,12 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre } // Check if the target is on the opposite side of the supporting plane. op[li] = op_li_is_null ? ZERO : _tr->orientation(*vert[0], *vert[1], *vert[2], *vert[3]); +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + std::cerr << " Orientation: " << op[li] << '\n'; +#endif if(op[li] == POSITIVE) pos += li; if(op[li] != NEGATIVE) { - vert[li] = backup_vert_li; continue; } CGAL_assertion_code(incell = false;) @@ -534,6 +599,16 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre if(!calc[oij]) { const Point* const backup_vert_lj = std::exchange(vert[lj], &_source); o[oij] = _tr->orientation(*vert[0], *vert[1], *vert[2], *vert[3]); +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + std::cerr << " edge orientation("; + for(int i = 0; i < 4; ++i) { + if(i == li) std::cerr << "_target"; + else if(i == lj) std::cerr << "_source"; + else std::cerr << with_point(cur_cell->vertex(i)); + if(i < 3) std::cerr << ", "; + } + std::cerr << ") = " << o[oij] << '\n'; +#endif vert[lj] = backup_vert_lj; calc[oij] = true; } @@ -552,11 +627,18 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre } else Or -= o[oij]; } +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + std::cerr << " Or: " << Or << '\n'; + for(int i = 0; i < 6; ++i) { + auto [v1i, v2i] = edgeVertices(i); + std::cerr << " o[" << v1i << "," << v2i << "]: " + << (calc[i] ? o[i] : -999) << '\n'; + } +#endif if(Or == 0) { // Either the target is not inside the pyramid, // or the pyramid is degenerate. - vert[li] = backup_vert_li; continue; } @@ -567,7 +649,11 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre CGAL_assertion(li == outside); CGAL_assertion(!inside); } - return {{cur_cell, Tr::FACET, li}, {next, Tr::FACET, next->index(cur_cell)}}; +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + using Segment_3 = typename Tr::Geom_traits::Segment_3; + CGAL_assertion(do_intersect(Segment_3(_source, _target), _tr->triangle({cur_cell, li}))); +#endif + return returned({{cur_cell, Tr::FACET, li}, {next, Tr::FACET, next->index(cur_cell)}}); } case 2: { if(regular_case) @@ -575,9 +661,9 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre for(int j = 0; j < 4; ++j) { if(li != j && o[5 - edgeIndex(li, j)] == COPLANAR) { Edge opp = opposite_edge(prev.cell, li, j); - return { + return returned({ {cur_cell, Tr::EDGE, opp.second, opp.third}, - {next, Tr::EDGE, next->index(cur_cell->vertex(opp.second)), next->index(cur_cell->vertex(opp.third))}}; + {next, Tr::EDGE, next->index(cur_cell->vertex(opp.second)), next->index(cur_cell->vertex(opp.third))}}); } } CGAL_unreachable(); @@ -588,7 +674,7 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre CGAL_assertion(degenerate); for(int j = 0; j < 4; ++j) { if(li != j && o[5 - edgeIndex(li, j)] == NEGATIVE) { - return {{cur_cell, Tr::VERTEX, j}, {next, Tr::VERTEX, next->index(cur_cell->vertex(j))}}; + return returned({{cur_cell, Tr::VERTEX, j}, {next, Tr::VERTEX, next->index(cur_cell->vertex(j))}}); } } CGAL_unreachable(); @@ -601,8 +687,19 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre } // The target lies inside this cell. +#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + CGAL_assertion(false == _tr->tetrahedron(cur_cell).has_on_unbounded_side(_target)); + if(!incell) { + std::cerr << "_source:\n" << _source << '\n'; + std::cerr << "_target:\n" << _target << '\n'; + std::ofstream dump("dump.binary.cgal", std::ios::binary); + IO::set_binary_mode(dump); + dump << *_tr; + dump.close(); + } +#endif CGAL_assertion( incell ); - return { + return returned({ std::invoke([&]() -> Simplex { switch( op[0] + op[1] + op[2] + op[3] ) { case 4: @@ -633,7 +730,7 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre } }), { Cell_handle() } - }; + }); } template < class Tr, class Inc > diff --git a/Triangulation_3/include/CGAL/Triangulation_segment_traverser_3.h b/Triangulation_3/include/CGAL/Triangulation_segment_traverser_3.h index cdd383c4418c..547ac4d5f120 100644 --- a/Triangulation_3/include/CGAL/Triangulation_segment_traverser_3.h +++ b/Triangulation_3/include/CGAL/Triangulation_segment_traverser_3.h @@ -133,17 +133,12 @@ class Triangulation_segment_cell_iterator_3 template < class Tr2, class Inc2 > struct Rebind { typedef Triangulation_segment_cell_iterator_3 Other; }; -#if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 static auto display_vert(Vertex_handle v) { - std::stringstream os; - os.precision(17); - if(v->time_stamp() == 0) { - os << "inf"; - } else { - os << '#' << v->time_stamp() << "=(" << v->point() << ")"; - } - return os.str(); + return IO::oformat([v](std::ostream& out) -> auto& + { + return out << with_point(v); + }, IO_manip_tag{}); }; static auto display_lt(Locate_type lt) { @@ -180,7 +175,6 @@ class Triangulation_segment_cell_iterator_3 os << " prev: " << debug_simplex(_prev) << "\n cur: " << debug_simplex(_cur); return os.str(); } -#endif // CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 private: typedef Segment_cell_iterator SCI; @@ -493,6 +487,13 @@ class Triangulation_segment_cell_iterator_3 return ( i==0 || j==0 ) ? i+j-1 : i+j; } + inline std::pair edgeVertices( int index ) const { + CGAL_precondition( index>=0 && index<6 ); + return index < 3 + ? std::make_pair( 0, index+1 ) + : std::make_pair( index-2, index-1 ); + } + bool have_same_entry(const Simplex& s1, const Simplex& s2) const; // Compute the orientation of a point compared to the oriented plane supporting a half-facet. From 2e7e446ec4b64c2a508c1fd6708dd60ad973e0a4 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 15 Sep 2026 17:44:24 +0200 Subject: [PATCH 107/151] fix the segment traverser --- .../Triangulation_segment_traverser_3_impl.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h b/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h index bb144a3b51bf..b7138101256b 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h +++ b/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h @@ -552,8 +552,18 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre // For the remembering stochastic walk, we start trying with a random facet. CGAL_assertion_code(bool incell = true;) - for(int li = 0; li < 4; ++li) + int start = 0; + int end = 4; + if(cur.lt == Tr::VERTEX) { + // Then start to look at the facet opposite to the current vertex. + // For the three other facets, the exit type will be `VERTEX` anyway. + start = cur.li; + end = cur.li + 4; + } + + for(int li_ = start; li_ < end; ++li_) { + const int li = li_ % 4; // Skip the previous cell. Cell_handle next = cur_cell->neighbor(li); if(next == prev.cell) { From efc122ee6938f6881cb925da4fa77b21b610c9a7 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 16 Sep 2026 10:57:54 +0200 Subject: [PATCH 108/151] bug fix for CDT_3 It was possible that the two half-cavity share facets that are not in the polygon to constrain. --- ...ing_constrained_Delaunay_triangulation_3.h | 68 +++++++++++++------ .../include/CGAL/Triangulation_3.h | 3 +- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 3c34a4d9c083..f3e4261d6f3d 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -5029,12 +5029,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D throw Next_region{"missing facet in polygon", fh_region[0]}; } - { - std::vector all_cells_in_conflict(cells_of_upper_cavity.size() + cells_of_lower_cavity.size()); - auto it = std::copy(cells_of_upper_cavity.begin(), cells_of_upper_cavity.end(), all_cells_in_conflict.begin()); - std::copy(cells_of_lower_cavity.begin(), cells_of_lower_cavity.end(), it); - insert_in_conflict_visitor.process_cells_in_conflict(all_cells_in_conflict.begin(), all_cells_in_conflict.end()); - } + std::set cells_to_remove{cells_of_lower_cavity.begin(), cells_of_lower_cavity.end()}; + cells_to_remove.insert(cells_of_upper_cavity.begin(), cells_of_upper_cavity.end()); + insert_in_conflict_visitor.process_cells_in_conflict(cells_to_remove.begin(), cells_to_remove.end()); if(this->debug().copy_triangulation_into_hole()) { std::cerr << "# glu the upper triangulation of the cavity\n"; @@ -5053,7 +5050,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D typename T_3::Vertex_triple_Facet_map outer_map; auto add_to_outer_map = [&](typename T_3::Vertex_triple vt, Facet f, - [[maybe_unused]] std::string_view extra = {}) { + [[maybe_unused]] std::string_view extra = {}) + { + if(cells_to_remove.count(f.first) != 0) return false; // the cell of the facet cannot be in the cavity outer_map[vt] = f; CGAL_USE(this); #if CGAL_CDT_3_CAN_USE_CXX20_FORMAT @@ -5065,23 +5064,37 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D with_point(vt[0]), with_point(vt[1]), with_point(vt[2])); - std::stringstream filename; - filename << "dump_upper_outer_map_region_" << face_index << "_" << region_index << ".off"; - std::ofstream out(filename.str()); - out.precision(17); - write_facets(out, *this, std::ranges::views::values(outer_map)); - out.close(); } #endif // CGAL_CDT_3_CAN_USE_CXX20_FORMAT + return true; }; auto fill_outer_map_of_cavity = [&](const auto&, const auto& facets) { + std::vector facets_to_add_to_pseudo_cells; for(auto f : facets) { auto vt = T_3::make_canonical_oriented_triple(f); - add_to_outer_map(vt, f); + if(!add_to_outer_map(vt, f)) { + facets_to_add_to_pseudo_cells.push_back(f); + } } + return facets_to_add_to_pseudo_cells; }; - fill_outer_map_of_cavity(upper_cavity_triangulation, facets_of_upper_cavity); + auto extra_pseudo_facets = fill_outer_map_of_cavity(upper_cavity_triangulation, facets_of_upper_cavity); +#if CGAL_CDT_3_CAN_USE_CXX20_FORMAT + if(this->debug().copy_triangulation_into_hole()) { + std::cerr << "outer_map:\n"; + for(auto [vt, _] : outer_map) { + std::cerr << cdt_3_format(" {:.6}, {:.6}, {:.6})\n", + with_point(vt[0]), with_point(vt[1]), with_point(vt[2])); + } + std::stringstream filename; + filename << "dump_upper_outer_map_region_" << face_index << "_" << region_index << ".off"; + std::ofstream out(filename.str()); + out.precision(17); + write_facets(out, *this, std::ranges::views::values(outer_map)); + out.close(); + } +#endif // CGAL_CDT_3_CAN_USE_CXX20_FORMAT auto add_pseudo_cells_to_outer_map = [&](const auto& tr, const auto& map_cavity_vertices_to_ambient_vertices, bool is_upper_cavity) { // @TODO: comment this piece of code @@ -5119,6 +5132,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto pseudo_cells = add_pseudo_cells_to_outer_map(upper_cavity_triangulation, map_upper_cavity_vertices_to_ambient_vertices, true); + for(auto& facet: extra_pseudo_facets) { + auto verts = tr().vertices(facet); + auto new_cell = this->tds().create_cell(verts[0], verts[1], verts[2], this->infinite_vertex()); + facet = Facet{new_cell, 3}; + T_3::make_canonical_oriented_triple(verts); + add_to_outer_map(verts, facet, "extra"); + } + { const auto upper_inner_map = tr().create_triangulation_inner_map( upper_cavity_triangulation, map_upper_cavity_vertices_to_ambient_vertices, false); @@ -5144,13 +5165,19 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D new_constrained_facets.reserve(pseudo_cells.size()); for(const auto& [c, fh_2d] : pseudo_cells) { const Facet f{c, 3}; - const Facet opposite_f = this->mirror_facet(f); + const Facet opposite_f = tr().mirror_facet(f); new_constrained_facets.emplace_back(opposite_f, fh_2d); CGAL_assertion(static_cast(facet_is_facet_of_cdt_2(*this, opposite_f, cdt_2))); auto vt = T_3::make_canonical_oriented_triple(opposite_f); add_to_outer_map(vt, opposite_f, "pseudo "); this->tds().delete_cell(c); } + for(const auto& facet: extra_pseudo_facets) { + auto opposite_f = tr().mirror_facet(facet); + auto vt = T_3::make_canonical_oriented_triple(opposite_f); + add_to_outer_map(vt, opposite_f, "pseudo "); + this->tds().delete_cell(facet.first); + } fill_outer_map_of_cavity(lower_cavity_triangulation, facets_of_lower_cavity); { const auto lower_inner_map = tr().create_triangulation_inner_map( @@ -5162,6 +5189,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << cdt_3_format(" {:.6}, {:.6}, {:.6})\n", with_point(vt[0]), with_point(vt[1]), with_point(vt[2])); } + std::cerr << "lower_inner_map:\n"; + for(auto [vt, _] : lower_inner_map) { + std::cerr << cdt_3_format(" {:.6}, {:.6}, {:.6})\n", + with_point(vt[0]), with_point(vt[1]), with_point(vt[2])); + } std::stringstream filename; filename << "dump_lower_outer_map_region_" << face_index << "_" << region_index << ".off"; std::ofstream out(filename.str()); @@ -5173,8 +5205,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D tr().copy_triangulation_into_hole(map_lower_cavity_vertices_to_ambient_vertices, std::move(outer_map), lower_inner_map, this->new_cells_output_iterator()); } - std::set cells_to_remove{cells_of_lower_cavity.begin(), cells_of_lower_cavity.end()}; - cells_to_remove.insert(cells_of_upper_cavity.begin(), cells_of_upper_cavity.end()); for(auto c : cells_to_remove) { if(this->debug().copy_triangulation_into_hole()) { std::cerr << "delete cell " << IO::oformat(c) << "\n"; @@ -5184,7 +5214,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto restore_markers = [&](Facet outside_facet) { const auto [outside_cell, outside_face_index] = outside_facet; - const auto mirror_facet = this->mirror_facet(outside_facet); + const auto mirror_facet = tr().mirror_facet(outside_facet); if(outside_cell->ccdt_3_data().is_facet_constrained(outside_face_index)) { const auto polygon_id = outside_cell->ccdt_3_data().face_constraint_index(outside_face_index); const CDT_2& cdt_2 = face_cdt_2(polygon_id); diff --git a/Triangulation_3/include/CGAL/Triangulation_3.h b/Triangulation_3/include/CGAL/Triangulation_3.h index 4006a8fa3245..ffec012f142d 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3.h +++ b/Triangulation_3/include/CGAL/Triangulation_3.h @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -5135,7 +5136,7 @@ copy_triangulation_into_hole(const Vertex_handle_unique_hash_map& vmap, *cit++ = new_ch; - // For the other faces check, if they can also be glued + // For the other faces, check if they can also be glued for(unsigned int index = 0; index < 4; index++) { if(index != i_i) From 41eb6ed9ebdcc22055a32555a3c7671c184a28a6 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 16 Sep 2026 10:58:48 +0200 Subject: [PATCH 109/151] extend the testsuite for CDT_3 with new test cases --- .../CMakeLists.txt | 19 + .../data/bug-flip-1.off | 53 + .../data/bug-flip-2.off | 11 + .../data/bug-flip-3.off | 172 + .../data/bug-flip-4.off | 54 + .../data/bug-move-vertex-1.off | 19 + .../data/bug-move-vertex-2.off | 16 + .../data/bug-move-vertex-3.off | 16 + .../data/bug-move-vertex-4.off | 161 + .../data/bug-move-vertex-5.off | 29 + .../data/bug-move-vertex-6.off | 19 + .../data/bug-move-vertex-7.off | 24 + .../data/bug-segfault.off | 3714 +++++++++++++++++ .../data/bug-split-1.off | 78 + .../Constrained_triangulation_3/data/bug2.off | 19 + .../Constrained_triangulation_3/data/bug5.off | 36 + .../Constrained_triangulation_3/data/bug6.off | 23 + .../Constrained_triangulation_3/data/bug7.off | 560 +++ .../Constrained_triangulation_3/data/bug8.off | 26 + .../Constrained_triangulation_3/data/bug9.off | 949 +++++ 20 files changed, 5998 insertions(+) create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-1.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-2.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-3.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-4.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-1.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-2.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-3.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-4.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-5.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-6.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-7.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-segfault.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-split-1.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug2.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug5.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug6.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug7.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug8.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug9.off diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index 52601668847d..3517c9fb9eb9 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -102,6 +102,25 @@ function(CGAL_add_cdt3_from_local_off_test data_name) CGAL_add_cdt3_from_off_test_aux(${data_name} ${CMAKE_CURRENT_SOURCE_DIR}/data ${ARGN}) endfunction() +CGAL_add_cdt3_from_local_off_test(bug2 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug5 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug6 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-move-vertex-1 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-move-vertex-2 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-move-vertex-3 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-move-vertex-4 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-move-vertex-5 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-move-vertex-6 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-move-vertex-7 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-split-1 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-flip-1 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-flip-2 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-flip-3 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-flip-4 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug7 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug-segfault OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug8 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug9 OPTIONS --read-mesh-with-operator) CGAL_add_cdt3_from_local_off_test(bug-fixed --move-Steiner-vertices-to-the-volume) CGAL_add_cdt3_from_local_off_test(bug3-fixed --move-Steiner-vertices-to-the-volume) CGAL_add_cdt3_from_local_off_test(bug4-fixed --move-Steiner-vertices-to-the-volume) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-1.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-1.off new file mode 100644 index 000000000000..3b4f674dbe51 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-1.off @@ -0,0 +1,53 @@ +OFF +35 14 0 + +0.0323030017 0.040972001899999999 -0.0323030017 +0.031844001300000001 0.040160000299999998 -0.018155999499999999 +0.031725998999999998 0.0398919992 -0.031725998999999998 +0.031470999100000001 0.039147999099999997 -0.018528999800000001 +0.031369999099999997 0.038718998400000003 -0.031369999099999997 +0.0312799998 0.038113001700000003 -0.0312799998 +0.031277999299999998 0.038086000799999999 -0.018721999600000001 +0.031268000599999998 0.037978000900000003 -0.026802999899999999 +0.031263001300000003 0.037099000100000001 -0.029234999800000001 +0.031252000500000002 0.037645999300000003 -0.028056999700000002 +0.03125 0.037500001499999998 -0.03125 +0.018750000700000002 0.037500001499999998 -0.03125 +0.018747 0.037686999899999997 -0.0279399995 +0.018742000700000001 0.037188000999999998 -0.029075000399999999 +0.0187309999 0.037987999600000003 -0.026743000400000001 +0.018721999600000001 0.038086000799999999 -0.018721999600000001 +0.018721999600000001 0.038086000799999999 -0.025546999599999998 +0.018719999099999999 0.038113001700000003 -0.0312799998 +0.018719999099999999 0.036887001199999998 -0.0312799998 +0.0186730009 0.036520998899999997 -0.030092999299999999 +0.018629999800000002 0.038718998400000003 -0.031369999099999997 +0.018629999800000002 0.036281000799999998 -0.031369999099999997 +-0.050000000699999998 0.032086998200000001 -0.040624998500000002 +-0.050000000699999998 0.033080998799999997 -0.041919000400000003 +-0.050000000699999998 0.034375000699999998 -0.042913000999999999 +-0.050000000699999998 0.035881999900000003 -0.043536998299999997 +-0.050000000699999998 0.037500001499999998 -0.043749999300000002 +-0.042697001200000001 0.0323030017 -0.040972001899999999 +-0.042697001200000001 0.034028001099999997 -0.042697001200000001 +-0.041919000400000003 0.033080998799999997 -0.041919000400000003 +-0.043749999300000002 0.037500001499999998 -0.043749999300000002 +-0.043719999500000002 0.036887001199999998 -0.043719999500000002 +-0.043719999500000002 0.038113001700000003 -0.043719999500000002 +-0.043630000199999998 0.036281000799999998 -0.043630000199999998 +-0.043274000299999997 0.035108 -0.043274000299999997 +3 7 9 5 +3 9 8 10 +3 4 3 6 +3 0 1 2 +3 18 21 19 +3 11 18 13 +3 11 12 17 +3 14 16 17 +3 15 20 16 +3 30 32 26 +3 25 33 31 +3 34 24 28 +3 27 29 23 +3 27 23 22 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-2.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-2.off new file mode 100644 index 000000000000..5a281d99df15 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-2.off @@ -0,0 +1,11 @@ +OFF +5 2 0 + +0.018719999099999999 0.0312799998 -0.038113001700000003 +0.018719999099999999 0.018719999099999999 -0.038113001700000003 +0.018629999800000002 0.031369999099999997 -0.038718998400000003 +0.018629999800000002 0.018629999800000002 -0.038718998400000003 +0.018273999900000001 0.018273999900000001 -0.0398919992 +3 3 2 4 +3 1 0 3 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-3.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-3.off new file mode 100644 index 000000000000..481cbcaf4d7c --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-3.off @@ -0,0 +1,172 @@ +OFF +116 52 0 + +0.031335677999999999 0.018755000099999999 0.038650944800000003 +0.050000000699999998 -0.037500001499999998 0.0050889999000000002 +0.050000000699999998 -0.043749000900000001 -0.0125890002 +0.050000000699999998 -0.050000000699999998 0.0050889999000000002 +0.050000000699999998 -0.050000000699999998 -0.0125890002 +0.048792999199999999 -0.043614000100000001 -0.0137949996 +0.0476359986 -0.043248001500000001 -0.0149529995 +0.046569999299999998 -0.042665999400000001 -0.016017999500000001 +0.045637998700000001 -0.041887998599999997 -0.016951000300000001 +0.044874001300000001 -0.0409450009 -0.0177149996 +0.044305998800000003 -0.039870999800000001 -0.0182830002 +0.043749999300000002 -0.037500001499999998 -0.0011609999999999999 +0.0436550006 -0.038587000199999999 -0.001256 +0.0433720015 -0.0396410003 -0.001539 +0.042909998400000003 -0.0406300016 -0.0020020000999999999 +0.042773000899999997 -0.040855001699999997 -0.019816 +0.042282000200000003 -0.041524000499999998 -0.0026289999999999998 +0.0420489982 -0.041786000099999998 -0.020540000900000001 +0.041510000800000002 -0.042293999300000003 -0.0034020001000000001 +0.041163999600000001 -0.042564000900000003 -0.0214249995 +0.040614001499999997 -0.042918998700000002 -0.0042969999 +0.040148001199999998 -0.043161999399999998 -0.022440999699999999 +0.039037000400000001 -0.043558001499999999 -0.0235510003 +0.037872999900000003 -0.043738998500000001 -0.0247159991 +0.032664000999999998 -0.041459001600000003 -0.029924999899999999 +0.032005999200000003 -0.040479999000000003 -0.0305819996 +0.031642001099999997 0.0189449992 0.039680000399999998 +0.031504001500000003 0.0184959993 0.039262998799999997 +0.031341999799999999 -0.050000000699999998 -0.0135690002 +0.031295001500000003 0.018704999199999999 0.0382449999 +0.031060000899999999 0.018609000399999999 0.038819000100000001 +0.0308020003 0.018459999899999999 0.039381999500000001 +0.0305819996 -0.040479999000000003 -0.032005999200000003 +0.029296999800000001 -0.042918998700000002 -0.015614000100000001 +0.028402000699999999 -0.042293999300000003 -0.0165100005 +0.027628999200000001 -0.041524000499999998 -0.0172819998 +0.027499999899999999 0.0062500000999999999 0.037500001499999998 +0.027499999899999999 -0.018750000700000002 0.037500001499999998 +0.027499999899999999 -0.03125 0.037500001499999998 +0.027499999899999999 -0.043749999300000002 0.037500001499999998 +0.027499999899999999 -0.050000000699999998 0.037500001499999998 +0.027001999299999999 -0.0406300016 -0.017909999900000001 +0.026538999800000001 -0.0396410003 -0.018371999300000001 +0.026256000599999999 -0.038587000199999999 -0.018655000299999999 +0.026161000100000002 -0.037500001499999998 -0.018750000700000002 +0.018750000700000002 0.0062500000999999999 0.037500001499999998 +0.018750000700000002 -0.0062500000999999999 0.037500001499999998 +0.018750000700000002 -0.018750000700000002 0.037500001499999998 +0.018750000700000002 -0.03125 0.037500001499999998 +0.018750000700000002 -0.037500001499999998 -0.026161000100000002 +0.018750000700000002 -0.043749999300000002 0.037500001499999998 +0.018750000700000002 -0.050000000699999998 0.037500001499999998 +0.018655000299999999 -0.038587000199999999 -0.026256000599999999 +0.018371999300000001 -0.0396410003 -0.026538999800000001 +0.017909999900000001 -0.0406300016 -0.027001999299999999 +0.0172819998 -0.041524000499999998 -0.027628999200000001 +0.0165100005 -0.042293999300000003 -0.028402000699999999 +0.015614000100000001 -0.042918998700000002 -0.029296999800000001 +0.0146239996 -0.043377999200000003 -0.0302869994 +0.0135690002 -0.043657999500000003 -0.031341999799999999 +0.0135690002 -0.050000000699999998 -0.031341999799999999 +0.0063419998999999999 -0.043657999500000003 -0.038568999600000001 +0.0062500000999999999 0.050000000699999998 0.037500001499999998 +0.0062500000999999999 0.043749999300000002 0.037500001499999998 +0.0062500000999999999 0.0042849997999999997 0.037500001499999998 +0.0062500000999999999 -0.0062500000999999999 0.037500001499999998 +0.0062500000999999999 -0.018750000700000002 0.037500001499999998 +0.0062500000999999999 -0.03125 0.037500001499999998 +0.0062500000999999999 -0.043749999300000002 0.037500001499999998 +0.0062500000999999999 -0.050000000699999998 0.037500001499999998 +0.0060800002000000004 0.03125 0.037500001499999998 +0.005287 -0.043377999200000003 -0.039624001800000003 +0.0042969999 -0.042918998700000002 -0.040614001499999997 +0.0034020001000000001 -0.042293999300000003 -0.041510000800000002 +0.0026289999999999998 -0.041524000499999998 -0.042282000200000003 +0.0024999999 0.050000000699999998 0.050000000699999998 +0.0024999999 0.050000000699999998 0.037500001499999998 +0.0024999999 0.043749999300000002 0.037500001499999998 +0.0024999999 0.043536998299999997 0.039117999399999999 +0.0024999999 0.042913000999999999 0.040624998500000002 +0.0024999999 0.041919000400000003 0.041919000400000003 +0.0024999999 0.040624998500000002 0.042913000999999999 +0.0024999999 0.032086998200000001 0.040624998500000002 +0.0024999999 0.031463000900000003 0.039117999399999999 +0.0024999999 0.03125 0.037500001499999998 +0.0024999999 0.029183000300000001 0.037500001499999998 +0.0024999999 0.0021200001 0.050000000699999998 +0.0024999999 0.0021200001 0.037500001499999998 +0.0024999999 -0.0062500000999999999 0.037500001499999998 +0.0024999999 -0.010882000399999999 0.043536998299999997 +0.0024999999 -0.0125000002 0.043749999300000002 +0.0024999999 -0.014117999900000001 0.043536998299999997 +0.0024999999 -0.015625 0.042913000999999999 +0.0024999999 -0.016919000100000001 0.041919000400000003 +0.0024999999 -0.017913000700000001 0.040624998500000002 +0.0024999999 -0.018536999799999999 0.039117999399999999 +0.0024999999 -0.018750000700000002 0.037500001499999998 +0.0024999999 -0.03125 0.037500001499999998 +0.0024999999 -0.031463000900000003 0.039117999399999999 +0.0024999999 -0.032086998200000001 0.040624998500000002 +0.0024999999 -0.033080998799999997 0.041919000400000003 +0.0024999999 -0.034375000699999998 0.042913000999999999 +0.0024999999 -0.035881999900000003 0.043536998299999997 +0.0024999999 -0.037500001499999998 0.043749999300000002 +0.0024999999 -0.039117999399999999 0.043536998299999997 +0.0024999999 -0.040624998500000002 0.042913000999999999 +0.0024999999 -0.041919000400000003 0.041919000400000003 +0.0024999999 -0.042913000999999999 0.040624998500000002 +0.0024999999 -0.043536998299999997 0.039117999399999999 +0.0024999999 -0.043749999300000002 0.037500001499999998 +0.0024999999 -0.050000000699999998 0.050000000699999998 +0.0024999999 -0.050000000699999998 0.037500001499999998 +-0.0050889999000000002 -0.037500001499999998 -0.050000000699999998 +-0.0050889999000000002 -0.050000000699999998 -0.050000000699999998 +0.031468503199999998 0.0188249983 0.038962498300000002 +0.031351000099999998 0.018776999799999999 0.0392495021 +3 10 15 9 +3 9 17 8 +3 19 7 8 +3 24 25 32 +3 23 4 22 +3 5 4 2 +3 21 22 6 +3 19 21 7 +3 113 71 61 +3 72 113 73 +3 112 74 113 +3 11 1 12 +3 14 13 1 +3 20 18 3 +3 16 1 3 +3 59 58 60 +3 33 28 57 +3 34 33 56 +3 55 54 35 +3 54 41 35 +3 42 53 43 +3 44 52 49 +3 85 83 82 +3 79 78 76 +3 75 81 80 +3 36 45 46 +3 37 48 38 +3 47 48 37 +3 40 39 50 +3 40 50 51 +3 68 111 69 +3 96 67 66 +3 65 64 87 +3 65 87 88 +3 70 84 85 +3 76 77 63 +3 76 63 62 +3 100 93 101 +3 106 111 107 +3 109 108 111 +3 86 90 89 +3 110 105 104 +3 90 110 103 +3 102 101 91 +3 101 92 91 +3 94 99 98 +3 96 98 97 +3 95 98 96 +3 0 27 29 +3 26 115 31 +3 0 30 115 +3 0 115 114 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-4.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-4.off new file mode 100644 index 000000000000..10b3673abd1f --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-flip-4.off @@ -0,0 +1,54 @@ +OFF +35 15 0 + +0.050000000699999998 0.039585001799999998 0.018392000299999999 +0.050000000699999998 0.038086000799999999 0.031277999299999998 +0.050000000699999998 0.038086000799999999 0.018721999600000001 +0.050000000699999998 0.0113570001 0.018644999700000001 +0.050000000699999998 0.0109219998 0.031452000100000002 +0.050000000699999998 0.0098919999000000002 0.018179999700000001 +0.050000000699999998 -0.010882000399999999 0.031463000900000003 +0.050000000699999998 -0.010882000399999999 0.018536999799999999 +0.050000000699999998 -0.0125000002 0.018750000700000002 +0.043731998699999997 0.037978000900000003 -0.026802999899999999 +0.043721999999999997 0.038086000799999999 -0.025546999599999998 +0.043719999500000002 0.038113001700000003 -0.0312799998 +0.033236999099999998 0.0307030007 -0.032930001600000001 +0.033236999099999998 0.0170699991 -0.032930001600000001 +0.033080998799999997 0.033080998799999997 -0.033080998799999997 +0.033080998799999997 0.016919000100000001 -0.033080998799999997 +0.0323030017 0.017697000899999999 -0.034028001099999997 +0.018629999800000002 0.031369999099999997 -0.038718998400000003 +0.018629999800000002 0.018629999800000002 -0.038718998400000003 +0.018273999900000001 0.031725998999999998 -0.035108 +0.018273999900000001 0.031725998999999998 -0.0398919992 +0.018273999900000001 0.018273999900000001 -0.035108 +0.018273999900000001 0.018273999900000001 -0.0398919992 +0.017697000899999999 0.0323030017 -0.034028001099999997 +0.017697000899999999 0.0323030017 -0.040972001899999999 +0.017697000899999999 0.017697000899999999 -0.034028001099999997 +0.017697000899999999 0.017697000899999999 -0.040972001899999999 +0.016919000100000001 0.033080998799999997 -0.033080998799999997 +0.016919000100000001 0.016919000100000001 -0.033080998799999997 +0.016919000100000001 0.016919000100000001 -0.041919000400000003 +0.015971999600000002 0.0323030017 -0.0323030017 +0.015971999600000002 0.017697000899999999 -0.0323030017 +0.013718999900000001 0.018629999800000002 -0.043630000199999998 +0.0131130004 0.0312799998 -0.043719999500000002 +0.0131130004 0.018719999099999999 -0.043719999500000002 +3 30 27 31 +3 27 23 28 +3 5 3 4 +3 16 14 15 +3 12 13 15 +3 23 21 25 +3 28 23 25 +3 19 21 23 +3 9 10 11 +3 0 1 2 +3 8 7 6 +3 32 33 34 +3 26 24 29 +3 20 26 22 +3 18 17 22 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-1.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-1.off new file mode 100644 index 000000000000..99f68ff91709 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-1.off @@ -0,0 +1,19 @@ +OFF +12 3 0 + +6.0047800000000002 6.0240600000000004 -10.007999999999999 +6.0072099999999997 4.00481 4.00481 +6.0240600000000004 6.0240600000000004 -10.007999999999999 +6.0072099999999997 2.0024000000000002 4.0064099999999998 +6.0048000000000004 6.0048000000000004 6.0048000000000004 +10 10 -10 +5.9999900000000004 10.007999999999999 -9.9999800000000008 +6.0116199999999997 2.00387 0.336254 +6.0060099999999998 6.0048000000000004 5.0039999999999996 +10.007999999999999 5.9999900000000004 4.9999900000000004 +6.0240600000000004 6.0047800000000002 -10.007999999999999 +10.007999999999999 5.9999900000000004 -9.9999800000000008 +4 6 5 2 0 +4 8 10 11 9 +7 4 2 10 8 7 3 1 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-2.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-2.off new file mode 100644 index 000000000000..831612722189 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-2.off @@ -0,0 +1,16 @@ +OFF +8 4 0 + +0.22012599999999999 3.9465400000000001e-10 -0.17452799999999999 +0.22254199999999999 -0.024537199999999999 -0.17452799999999999 +0.22969899999999999 -0.048132000000000001 -0.17452799999999999 +0.24132200000000001 -0.069877800000000004 -0.17452799999999999 +0.22012599999999999 1.99686e-10 0.18239 +0.22253899999999999 -0.0245212 0.18239 +0.24129200000000001 -0.069834099999999996 0.18239 +0.229686 -0.048100400000000001 0.18239 +3 1 0 4 +3 5 2 1 +3 7 2 5 +3 6 3 7 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-3.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-3.off new file mode 100644 index 000000000000..1b241375090d --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-3.off @@ -0,0 +1,16 @@ +OFF +8 4 0 + +0.050000000699999998 -0.037500001499999998 0.0050889999000000002 +0.050000000699999998 -0.050000000699999998 0.0050889999000000002 +0.042909998400000003 -0.0406300016 -0.0020020000999999999 +0.042282000200000003 -0.041524000499999998 -0.0026289999999999998 +0.041510000800000002 -0.042293999300000003 -0.0034020001000000001 +0.040614001499999997 -0.042918998700000002 -0.0042969999 +0.039624001800000003 -0.043377999200000003 -0.005287 +0.038568999600000001 -0.050000000699999998 -0.0063419998999999999 +3 2 0 3 +3 7 6 1 +3 6 5 1 +3 5 4 1 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-4.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-4.off new file mode 100644 index 000000000000..6044eda4d312 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-4.off @@ -0,0 +1,161 @@ +OFF +117 40 0 + +-23.07103157043457 -7.6288971900939941 2.1894557476043701 +-23.033435821533203 -7.7408347129821777 3.2313618659973145 +-23.299070358276367 -6.8942909240722656 3.0988237857818604 +17.360824312482563 -15.377271730072644 -136 +8.432548990055011 -18.353391063456634 -88 +13.392701946959207 -19.345430841251297 -84 +16.864809016792144 -10.913092729996661 -131 +18.352854903863403 -8.4329932855100047 -174 +7.4405183986741683 -19.345430841251297 -72 +14.880747834030466 -13.889212063380651 -117 +2.9763807374603921 -19.841450730148626 -79 +3.4723960331508117 -20.337470619045959 -110 +2.9763807374603921 -19.841450730148626 -109 +5.45645721591249 -19.345430841251297 -104 +22.320977269386759 -7.4409535077153421 -112 +7.4405183986741683 -22.321550174635284 -104 +12.896686651268787 -15.873291618969978 -87 +5.9524725116029096 -22.817570063532614 -105 +6.9445031029837487 -18.849410952353963 -105 +5.45645721591249 -19.345430841251297 -112 +6.9445031029837487 -18.849410952353963 -127 +1.4883348503891298 -23.313589952429947 -78 +3.4723960331508117 -23.313589952429947 -100 +3.9684113288412313 -22.817570063532614 -101 +2.9763807374603921 -19.841450730148626 -83 +10.416610172816689 -17.361351285661968 -96 +9.9205948771262698 -17.857371174559301 -95 +3.9684113288412313 -19.841450730148626 -85 +-22.320399342751017 -7.4409535077153421 2 +-21.824384047060597 -8.929013174407336 3 +-22.320399342751017 -7.4409535077153421 4 +-21.824384047060597 -7.9369733966126734 3 +-22.320399342751017 -6.4489137299206796 2 +-22.816414638441437 -5.9528938410233483 1 +-22.816414638441437 -4.9608540632286857 3 +-22.320399342751017 -5.456873952126017 2 +-22.816414638441437 -5.9528938410233483 3 +22.816992565077179 -5.9528938410233483 -113 +10.416610172816689 -17.361351285661968 -110 +17.360824312482563 -10.41707284109933 -132 +14.384732538340046 -18.353391063456634 -78 +7.9365336943645879 -18.849410952353963 -77 +5.45645721591249 -19.345430841251297 -90 +6.9445031029837487 -18.849410952353963 -89 +1.4883348503891298 -23.313589952429947 -58 +1.4883348503891298 -22.321550174635284 -58 +3.4723960331508117 -21.329510396840622 -74 +5.9524725116029096 -22.817570063532614 -95 +5.9524725116029096 -22.817570063532614 -83 +5.45645721591249 -19.345430841251297 -88 +8.432548990055011 -18.353391063456634 -86 +13.392701946959207 -15.377271730072644 -88 +13.888717242649626 -18.849410952353963 -77 +13.888717242649626 -14.881251841175313 -87 +12.400671355578368 -19.345430841251297 -84 +14.384732538340046 -14.385231952277982 -102 +1.4883348503891298 -20.337470619045959 -82 +8.432548990055011 -18.353391063456634 -128 +7.9365336943645879 -18.849410952353963 -127 +17.856839608172983 -9.9210529522019986 -131 +18.352854903863403 -8.4329932855100047 -176 +17.856839608172983 -9.9210529522019986 -175 +18.352854903863403 -14.385231952277982 -136 +22.320977269386759 -7.4409535077153421 -114 +14.384732538340046 -14.385231952277982 -116 +15.376763129720885 -13.393192174483319 -116 +17.856839608172983 -14.881251841175313 -135 +13.392701946959207 -15.377271730072644 -102 +5.45645721591249 -19.345430841251297 -84 +13.888717242649626 -14.881251841175313 -101 +8.9285642857454306 -18.849410952353963 -77 +7.4405183986741683 -19.345430841251297 -76 +6.9445031029837487 -18.849410952353963 -113 +2.9763807374603921 -19.841450730148626 -111 +9.9205948771262698 -17.857371174559301 -109 +9.4245795814358502 -18.353391063456634 -96 +7.4405183986741683 -22.321550174635284 -84 +10.416610172816689 -17.361351285661968 -106 +9.4245795814358502 -18.353391063456634 -106 +9.9205948771262698 -17.857371174559301 -107 +10.912625468507109 -20.833490507943289 -99 +11.904656059887948 -19.841450730148626 -99 +7.9365336943645879 -18.849410952353963 -87 +11.904656059887948 -19.841450730148626 -105 +11.408640764197528 -20.337470619045959 -104 +1.4883348503891298 -20.337470619045959 -84 +5.45645721591249 -19.345430841251297 -114 +11.408640764197528 -20.337470619045959 -110 +11.904656059887948 -19.841450730148626 -109 +6.4484878072933292 -22.321550174635284 -94 +7.4405183986741683 -22.321550174635284 -82 +11.904656059887948 -19.841450730148626 -83 +8.432548990055011 -19.345430841251297 -72 +8.432548990055011 -20.337470619045959 -72 +14.880747834030466 -17.857371174559301 -77 +0.49630425900829067 -22.321550174635284 -58 +11.904656059887948 -19.841450730148626 -119 +11.408640764197528 -20.337470619045959 -120 +12.400671355578368 -19.345430841251297 -120 +0.99231955469871047 -22.817570063532614 -77 +0.49630425900829067 -23.313589952429947 -78 +4.4644266245316508 -21.329510396840622 -74 +4.4644266245316508 -22.321550174635284 -74 +3.9684113288412313 -19.841450730148626 -79 +3.4723960331508117 -20.337470619045959 -78 +6.4484878072933292 -19.345430841251297 -104 +10.912625468507109 -20.833490507943289 -109 +11.408640764197528 -20.337470619045959 -100 +4.9604419202220704 -19.841450730148626 -85 +8.9285642857454306 -21.825530285737951 -103 +8.9285642857454306 -21.825530285737951 -105 +9.4245795814358502 -18.353391063456634 -110 +5.9524725116029096 -22.817570063532614 -93 +4.9604419202220704 -22.817570063532614 -101 +10.912625468507109 -20.833490507943289 -105 +5.45645721591249 -22.321550174635284 -106 +5.9524725116029096 -22.817570063532614 -107 +3 2 0 1 +3 31 30 29 +3 36 35 34 +3 33 36 32 +3 36 28 32 +3 58 20 57 +3 7 61 60 +3 37 63 14 +3 9 65 64 +3 6 39 59 +3 66 62 3 +3 5 54 91 +3 82 4 50 +3 8 93 92 +3 52 94 40 +3 16 51 53 +3 43 49 42 +3 70 71 41 +3 45 95 44 +3 98 97 96 +3 100 99 21 +3 90 76 48 +3 85 24 56 +3 102 46 101 +3 104 10 103 +3 55 69 67 +3 11 12 73 +3 18 105 13 +3 87 106 88 +3 80 81 107 +3 68 27 108 +3 110 15 109 +3 78 79 77 +3 111 38 74 +3 25 26 75 +3 47 112 89 +3 72 19 86 +3 22 113 23 +3 84 83 114 +3 116 115 17 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-5.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-5.off new file mode 100644 index 000000000000..1227221be945 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-5.off @@ -0,0 +1,29 @@ +OFF +22 3 0 + +6 10 10 +-6.0072099999999997 4.00481 4.00481 +6.0048000000000004 6.0072099999999997 4.0031999999999996 +6.0072099999999997 4.00481 4.00481 +-6.0072099999999997 2.0024000000000002 4.0064099999999998 +6.0072099999999997 2.0024000000000002 4.0064099999999998 +6.0048000000000004 6.0048000000000004 6.0048000000000004 +6.0048000000000004 6.0048000000000004 10.007999999999999 +5.9999900000000004 10.007999999999999 5.9999900000000004 +5.9999900000000004 10.007999999999999 3.9999899999999999 +-6.0048000000000004 6.0060099999999998 5.0006000000000004 +-6.0110000000000001 0.85517500000000002 4.0073299999999996 +6.0109899999999996 0.86136599999999997 4.0073299999999996 +-6.0116300000000003 2.0038800000000001 0.33143699999999998 +-6.0060099999999998 6.0060099999999998 5.0006000000000004 +6.0116199999999997 2.00387 0.336254 +-10.02 0 -2.0080200000000001 +10.02 0 -1.9999899999999999 +-10.007999999999999 6.0240600000000004 5.0200500000000003 +6.0060099999999998 6.0048000000000004 5.0039999999999996 +10.007999999999999 5.9999900000000004 4.9999900000000004 +-6.0047899999999998 6.0192399999999999 5.0160400000000003 +10 16 18 21 10 14 13 15 19 20 17 +6 11 12 3 5 4 1 +8 9 2 6 3 12 7 0 8 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-6.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-6.off new file mode 100644 index 000000000000..99f68ff91709 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-6.off @@ -0,0 +1,19 @@ +OFF +12 3 0 + +6.0047800000000002 6.0240600000000004 -10.007999999999999 +6.0072099999999997 4.00481 4.00481 +6.0240600000000004 6.0240600000000004 -10.007999999999999 +6.0072099999999997 2.0024000000000002 4.0064099999999998 +6.0048000000000004 6.0048000000000004 6.0048000000000004 +10 10 -10 +5.9999900000000004 10.007999999999999 -9.9999800000000008 +6.0116199999999997 2.00387 0.336254 +6.0060099999999998 6.0048000000000004 5.0039999999999996 +10.007999999999999 5.9999900000000004 4.9999900000000004 +6.0240600000000004 6.0047800000000002 -10.007999999999999 +10.007999999999999 5.9999900000000004 -9.9999800000000008 +4 6 5 2 0 +4 8 10 11 9 +7 4 2 10 8 7 3 1 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-7.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-7.off new file mode 100644 index 000000000000..c2b09c3542c3 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-move-vertex-7.off @@ -0,0 +1,24 @@ +OFF +16 4 0 + +6.0047800000000002 6.0240600000000004 -10.007999999999999 +6.0047899999999998 6.0168400000000002 -4.00319 +6.0048000000000004 6.0072099999999997 4.0031999999999996 +6.0072099999999997 4.00481 4.00481 +6.0240600000000004 6.0240600000000004 -10.007999999999999 +6.0072099999999997 2.0024000000000002 4.0064099999999998 +6.0048000000000004 6.0048000000000004 6.0048000000000004 +4.0031999999999996 6.0072099999999997 4.0031999999999996 +-4.00319 6.0168400000000002 -4.00319 +5.9999900000000004 10.007999999999999 -9.9999800000000008 +5.9999900000000004 10.007999999999999 -3.9999899999999999 +6.0116199999999997 2.00387 0.336254 +6.0060099999999998 6.0048000000000004 5.0039999999999996 +10.007999999999999 5.9999900000000004 4.9999900000000004 +6.0240600000000004 6.0047800000000002 -10.007999999999999 +10.007999999999999 5.9999900000000004 -9.9999800000000008 +4 12 14 15 13 +7 6 4 14 12 11 5 3 +7 8 1 0 4 6 2 7 +4 9 0 1 10 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-segfault.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-segfault.off new file mode 100644 index 000000000000..08f3a6a0e6b1 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-segfault.off @@ -0,0 +1,3714 @@ +OFF +1398 2312 0 + +26.597300000000001 36.607999999999997 97.103999999999999 +26.567900000000002 36.567549999999997 96.959999999999994 +36.567549999999997 26.567900000000002 96.959999999999994 +-50 0 96.239999999999995 +-47.552849999999999 15.450850000000001 96.239999999999995 +36.877704999999999 25.524087000000002 86.799999999999997 +41.277892999999999 27.766093999999999 86.799999999999997 +38.239677999999998 22.851068000000001 91.996151999999995 +43.429470999999999 9.9159480000000002 23.853847999999999 +44.368076000000002 3.9898180000000001 17.746151999999999 +44.368076000000002 3.9898180000000001 23.853847999999999 +-27.740373000000002 41.290998000000002 95 +-29.389250000000001 40.450850000000003 96.239999999999995 +-15.450850000000001 47.552849999999999 96.239999999999995 +36.607999999999997 26.597300000000001 97.103999999999999 +-49.715021999999998 1.7992950000000001 20.800000000000001 +38.239677999999998 22.851068000000001 81.603847999999999 +29.359850000000002 40.410400000000003 96.384 +29.359850000000002 40.410400000000003 96.480000000000004 +40.410400000000003 29.359850000000002 96.384 +-25.093077000000001 42.639868999999997 34.246152000000002 +-25.093077000000001 42.639868999999997 40.353847999999999 +-19.747043000000001 45.363821000000002 34.246152000000002 +13.983000000000001 43.035299999999999 97.103999999999999 +-48.307125999999997 10.688492 25.996151999999999 +-48.307125999999997 10.688492 32.103847999999999 +-47.837826999999997 13.651558 20.800000000000001 +40.410400000000003 29.359850000000002 96.480000000000004 +-29.389250000000001 40.450850000000003 0 +-27.766093999999999 41.277892999999999 12.550000000000001 +-49.245724000000003 4.7623610000000003 15.603847999999999 +42.639868999999997 25.093077000000001 91.996151999999995 +15.4354 47.505249999999997 96.384 +15.4354 47.505249999999997 96.480000000000004 +-17.074024999999999 46.725797 12.550000000000001 +-15.450850000000001 47.552849999999999 0 +-19.747043000000001 45.363821000000002 7.3538480000000002 +-47.552849999999999 15.450850000000001 0 +-25.093077000000001 42.639868999999997 7.3538480000000002 +-49.245724000000003 4.7623610000000003 32.103847999999999 +-49.245724000000003 4.7623610000000003 25.996151999999999 +-49.715021999999998 1.7992950000000001 37.299999999999997 +36.877704999999999 25.524087000000002 20.800000000000001 +36.405749999999998 26.45035 95.760000000000005 +36.405749999999998 26.45035 4.7999999999999998 +-48.307125999999997 10.688492 15.603847999999999 +36.877704999999999 25.524087000000002 37.299999999999997 +-19.747043000000001 45.363821000000002 17.746151999999999 +-19.747043000000001 45.363821000000002 23.853847999999999 +38.239677999999998 22.851068000000001 15.603847999999999 +36.877704999999999 25.524087000000002 70.299999999999997 +-25.093077000000001 42.639868999999997 56.853847999999999 +-25.093077000000001 42.639868999999997 50.746152000000002 +-27.766093999999999 41.277892999999999 62.049999999999997 +49.450000000000003 0 97.488 +47.02975 -15.280900000000001 97.488 +40.963622999999998 17.505030000000001 32.103847999999999 +42.325595999999997 14.832011 20.800000000000001 +42.325595999999997 14.832011 37.299999999999997 +-19.747043000000001 45.363821000000002 50.746152000000002 +49.25 0 97.680000000000007 +42.797550000000001 13.905749999999999 95.760000000000005 +42.797550000000001 13.905749999999999 4.7999999999999998 +-27.766093999999999 41.277892999999999 78.549999999999997 +-25.093077000000001 42.639868999999997 67.246151999999995 +47.02975 15.280900000000001 97.488 +40.963622999999998 17.505030000000001 9.4961520000000004 +42.639868999999997 25.093077000000001 9.4961520000000004 +40.963622999999998 17.505030000000001 15.603847999999999 +-27.766093999999999 41.277892999999999 29.050000000000001 +40.005899999999997 29.065999999999999 97.488 +46.839550000000003 15.219099999999999 97.680000000000007 +-47.837826999999997 13.651558 37.299999999999997 +41.461371 27.405999000000001 5 +38.239677999999998 22.851068000000001 9.4961520000000004 +38.239677999999998 22.851068000000001 25.996151999999999 +38.239677999999998 22.851068000000001 32.103847999999999 +-25.093077000000001 42.639868999999997 23.853847999999999 +29.065999999999999 40.005899999999997 97.488 +39.844099999999997 28.948399999999999 97.680000000000007 +-49.245724000000003 4.7623610000000003 58.996152000000002 +42.142118000000004 15.192107 5 +36.877704999999999 25.524087000000002 53.799999999999997 +46.75 0 98.304000000000002 +44.319249999999997 -14.4002 98.256 +44.4619 -14.44655 98.304000000000002 +-25.093077000000001 42.639868999999997 73.353847999999999 +-19.747043000000001 45.363821000000002 67.246151999999995 +-49.245724000000003 4.7623610000000003 42.496152000000002 +-49.245724000000003 4.7623610000000003 48.603847999999999 +44.4619 14.44655 98.304000000000002 +44.319249999999997 14.4002 98.256 +46.600000000000001 0 98.256 +-27.766093999999999 41.277892999999999 45.549999999999997 +15.280900000000001 47.02975 97.488 +-49.715021999999998 1.7992950000000001 53.799999999999997 +40.963622999999998 17.505030000000001 25.996151999999999 +-17.074024999999999 46.725797 29.050000000000001 +37.061183 25.163990999999999 5 +42.325595999999997 14.832011 53.799999999999997 +40.963622999999998 17.505030000000001 42.496152000000002 +37.821550000000002 27.478950000000001 98.304000000000002 +37.700200000000002 27.390799999999999 98.256 +0 49.450000000000003 97.488 +29.389250000000001 40.450850000000003 96.239999999999995 +15.450850000000001 47.552849999999999 96.239999999999995 +0 49.25 97.680000000000007 +-48.307125999999997 10.688492 42.496152000000002 +37.041370000000001 32.798729999999999 17.746151999999999 +33.549370000000003 29.306730000000002 17.746151999999999 +32.798729999999999 37.041370000000001 17.746151999999999 +27.478950000000001 37.821550000000002 98.304000000000002 +27.390799999999999 37.700200000000002 98.256 +-25.093077000000001 42.639868999999997 17.746151999999999 +-15.280900000000001 47.02975 97.488 +29.306730000000002 33.549370000000003 17.746151999999999 +38.239677999999998 22.851068000000001 42.496152000000002 +38.239677999999998 22.851068000000001 48.603847999999999 +-19.747043000000001 45.363821000000002 40.353847999999999 +-15.219099999999999 46.839550000000003 97.680000000000007 +30.677409000000001 39.162691000000002 12.550000000000001 +14.44655 44.4619 98.304000000000002 +14.4002 44.319249999999997 98.256 +-17.074024999999999 46.725797 78.549999999999997 +-17.074024999999999 46.725797 62.049999999999997 +-29.065999999999999 40.005899999999997 97.488 +27.185409 35.670690999999998 12.550000000000001 +38.239677999999998 22.851068000000001 58.996152000000002 +-28.948399999999999 39.844099999999997 97.680000000000007 +39.162691000000002 30.677409000000001 12.550000000000001 +35.670690999999998 27.185409 12.550000000000001 +38.239677999999998 22.851068000000001 75.496151999999995 +0 46.75 98.304000000000002 +0 46.600000000000001 98.256 +-17.074024999999999 46.725797 45.549999999999997 +-39.844099999999997 28.948399999999999 97.680000000000007 +37.041370000000001 32.798729999999999 7.3538480000000002 +-14.44655 44.4619 98.304000000000002 +-40.005899999999997 29.065999999999999 97.488 +-47.02975 15.280900000000001 97.488 +33.549370000000003 29.306730000000002 7.3538480000000002 +40.963622999999998 17.505030000000001 65.103847999999999 +40.963622999999998 17.505030000000001 58.996152000000002 +42.325595999999997 14.832011 70.299999999999997 +-14.4002 44.319249999999997 98.256 +32.798729999999999 37.041370000000001 7.3538480000000002 +-27.478950000000001 37.821550000000002 98.304000000000002 +-27.390799999999999 37.700200000000002 98.256 +-19.747043000000001 45.363821000000002 56.853847999999999 +-49.450000000000003 0 97.488 +29.306730000000002 33.549370000000003 7.3538480000000002 +38.239677999999998 22.851068000000001 65.103847999999999 +-49.25 0 97.680000000000007 +-37.700200000000002 27.390799999999999 98.256 +-44.4619 14.44655 98.304000000000002 +-44.319249999999997 14.4002 98.256 +-25.093077000000001 42.639868999999997 83.746151999999995 +33.549370000000003 29.306730000000002 23.853847999999999 +29.306730000000002 33.549370000000003 23.853847999999999 +-30.677409000000001 39.162691000000002 37.299999999999997 +-30.677409000000001 39.162691000000002 20.800000000000001 +-32.798729999999999 37.041370000000001 32.103847999999999 +19.747043000000001 45.363821000000002 25.996151999999999 +22.851068000000001 38.239677999999998 25.996151999999999 +17.505030000000001 40.963622999999998 25.996151999999999 +46.744450000000001 -15.1882 97.775999999999996 +-46.75 0 98.304000000000002 +-40.450850000000003 29.389250000000001 96.239999999999995 +-40.410400000000003 29.359850000000002 96.384 +27.766093999999999 41.277892999999999 20.800000000000001 +25.524087000000002 36.877704999999999 20.800000000000001 +25.093077000000001 42.639868999999997 25.996151999999999 +49.149999999999999 0 97.775999999999996 +-40.450850000000003 29.389250000000001 0 +37.041370000000001 32.798729999999999 34.246152000000002 +33.549370000000003 29.306730000000002 34.246152000000002 +32.798729999999999 37.041370000000001 34.246152000000002 +-37.821550000000002 27.478950000000001 98.304000000000002 +-19.747043000000001 45.363821000000002 83.746151999999995 +33.549370000000003 29.306730000000002 56.853847999999999 +29.306730000000002 33.549370000000003 56.853847999999999 +33.549370000000003 29.306730000000002 50.746152000000002 +-40.963622999999998 17.505030000000001 17.746151999999999 +-42.325595999999997 14.832011 12.550000000000001 +-45.363821000000002 19.747043000000001 17.746151999999999 +25.093077000000001 42.639868999999997 15.603847999999999 +46.744450000000001 15.1882 97.775999999999996 +30.677409000000001 39.162691000000002 29.050000000000001 +29.306730000000002 33.549370000000003 34.246152000000002 +-19.747043000000001 45.363821000000002 73.353847999999999 +35.670690999999998 27.185409 45.549999999999997 +35.670690999999998 27.185409 62.049999999999997 +14.477449999999999 44.557000000000002 98.304000000000002 +-46.725797 17.074024999999999 12.550000000000001 +22.851068000000001 38.239677999999998 15.603847999999999 +0 50 0 +27.185409 35.670690999999998 29.050000000000001 +29.306730000000002 33.549370000000003 40.353847999999999 +-40.963622999999998 17.505030000000001 7.3538480000000002 +-45.363821000000002 19.747043000000001 7.3538480000000002 +19.747043000000001 45.363821000000002 15.603847999999999 +39.763199999999998 28.88965 97.775999999999996 +15.450850000000001 47.552849999999999 0 +39.162691000000002 30.677409000000001 29.050000000000001 +35.670690999999998 27.185409 29.050000000000001 +-17.099747000000001 46.712691999999997 95 +0 49.950000000000003 96.384 +0 49.950000000000003 96.480000000000004 +27.537749999999999 37.902450000000002 98.304000000000002 +17.505030000000001 40.963622999999998 15.603847999999999 +28.88965 39.763199999999998 97.775999999999996 +29.389250000000001 40.450850000000003 0 +-38.239677999999998 22.851068000000001 17.746151999999999 +-42.639868999999997 25.093077000000001 17.746151999999999 +17.074024999999999 46.725797 37.299999999999997 +19.747043000000001 45.363821000000002 42.496152000000002 +17.505030000000001 40.963622999999998 42.496152000000002 +40.450850000000003 29.389250000000001 0 +-43.130400000000002 14.0139 97.200000000000003 +-45.399999999999999 0 97.296000000000006 +-45.350000000000001 0 97.200000000000003 +37.041370000000001 32.798729999999999 23.853847999999999 +-19.747043000000001 45.363821000000002 89.853847999999999 +-25.093077000000001 42.639868999999997 89.853847999999999 +17.505030000000001 40.963622999999998 91.996151999999995 +26.45035 36.405749999999998 95.760000000000005 +13.905749999999999 42.797550000000001 95.760000000000005 +37.902450000000002 27.537749999999999 98.304000000000002 +14.832011 42.325595999999997 37.299999999999997 +40.963622999999998 17.505030000000001 75.496151999999995 +22.851068000000001 38.239677999999998 9.4961520000000004 +26.45035 36.405749999999998 4.7999999999999998 +-41.277892999999999 27.766093999999999 12.550000000000001 +-36.877704999999999 25.524087000000002 12.550000000000001 +17.505030000000001 40.963622999999998 32.103847999999999 +19.747043000000001 45.363821000000002 32.103847999999999 +15.1882 46.744450000000001 97.775999999999996 +50 0 0 +32.798729999999999 37.041370000000001 23.853847999999999 +13.905749999999999 42.797550000000001 4.7999999999999998 +14.832011 42.325595999999997 20.800000000000001 +44.557000000000002 14.477449999999999 98.304000000000002 +47.552849999999999 -15.450850000000001 0 +17.505030000000001 40.963622999999998 75.496151999999995 +22.851068000000001 38.239677999999998 75.496151999999995 +17.505030000000001 40.963622999999998 81.603847999999999 +-38.239677999999998 22.851068000000001 7.3538480000000002 +25.093077000000001 42.639868999999997 42.496152000000002 +22.851068000000001 38.239677999999998 42.496152000000002 +0 49.149999999999999 97.775999999999996 +40.450850000000003 -29.389250000000001 0 +37.041370000000001 32.798729999999999 50.746152000000002 +32.798729999999999 37.041370000000001 50.746152000000002 +29.306730000000002 33.549370000000003 50.746152000000002 +30.677409000000001 39.162691000000002 45.549999999999997 +-42.325595999999997 14.832011 29.050000000000001 +-45.363821000000002 19.747043000000001 23.853847999999999 +-46.725797 17.074024999999999 29.050000000000001 +22.851068000000001 38.239677999999998 32.103847999999999 +-39.763199999999998 28.88965 97.775999999999996 +-46.725797 17.074024999999999 78.549999999999997 +-45.363821000000002 19.747043000000001 83.746151999999995 +-36.688899999999997 26.65605 97.200000000000003 +-43.177950000000003 14.029350000000001 97.296000000000006 +-30.677409000000001 39.162691000000002 53.799999999999997 +-32.798729999999999 37.041370000000001 48.603847999999999 +14.832011 42.325595999999997 86.799999999999997 +-44.557000000000002 14.477449999999999 98.304000000000002 +-38.239677999999998 22.851068000000001 34.246152000000002 +-40.963622999999998 17.505030000000001 34.246152000000002 +-42.639868999999997 25.093077000000001 34.246152000000002 +17.074024999999999 46.725797 53.799999999999997 +19.747043000000001 45.363821000000002 58.996152000000002 +17.505030000000001 40.963622999999998 58.996152000000002 +-46.744450000000001 15.1882 97.775999999999996 +-46.712691999999997 17.099747000000001 95 +39.162691000000002 30.677409000000001 45.549999999999997 +37.041370000000001 32.798729999999999 40.353847999999999 +-39.162691000000002 30.677409000000001 20.800000000000001 +-26.685449999999999 36.729349999999997 97.296000000000006 +-36.729349999999997 26.685449999999999 97.296000000000006 +37.041370000000001 32.798729999999999 67.246151999999995 +33.549370000000003 29.306730000000002 67.246151999999995 +32.798729999999999 37.041370000000001 67.246151999999995 +45.555599999999998 14.8019 98.352000000000004 +45.412950000000002 14.755549999999999 98.400000000000006 +47.75 0 98.400000000000006 +-26.65605 36.688899999999997 97.200000000000003 +29.306730000000002 33.549370000000003 67.246151999999995 +38.751899999999999 28.154900000000001 98.352000000000004 +-37.041370000000001 32.798729999999999 42.496152000000002 +-37.041370000000001 32.798729999999999 48.603847999999999 +-32.798729999999999 37.041370000000001 42.496152000000002 +30.677409000000001 39.162691000000002 62.049999999999997 +38.630549999999999 28.066749999999999 98.400000000000006 +-39.162691000000002 30.677409000000001 53.799999999999997 +-39.162691000000002 30.677409000000001 70.299999999999997 +-37.041370000000001 32.798729999999999 58.996152000000002 +27.185409 35.670690999999998 62.049999999999997 +27.185409 35.670690999999998 78.549999999999997 +28.154900000000001 38.751899999999999 98.352000000000004 +28.066749999999999 38.630549999999999 98.400000000000006 +-39.162691000000002 30.677409000000001 37.299999999999997 +27.185409 35.670690999999998 45.549999999999997 +0 46.850000000000001 98.304000000000002 +-29.359850000000002 40.410400000000003 96.384 +25.524087000000002 36.877704999999999 53.799999999999997 +22.851068000000001 38.239677999999998 48.603847999999999 +-45.363821000000002 19.747043000000001 34.246152000000002 +14.832011 42.325595999999997 53.799999999999997 +-41.277892999999999 27.766093999999999 29.050000000000001 +-36.877704999999999 25.524087000000002 29.050000000000001 +17.505030000000001 40.963622999999998 48.603847999999999 +19.747043000000001 45.363821000000002 48.603847999999999 +-49.149999999999999 0 97.775999999999996 +25.524087000000002 36.877704999999999 37.299999999999997 +38.226050000000001 27.772849999999998 98.400000000000006 +44.937399999999997 14.601050000000001 98.400000000000006 +27.772849999999998 38.226050000000001 98.400000000000006 +-38.239677999999998 22.851068000000001 23.853847999999999 +25.093077000000001 42.639868999999997 58.996152000000002 +22.851068000000001 38.239677999999998 58.996152000000002 +-42.639868999999997 25.093077000000001 7.3538480000000002 +14.601050000000001 44.937399999999997 98.400000000000006 +-42.639868999999997 25.093077000000001 23.853847999999999 +0 47.25 98.400000000000006 +14.755549999999999 45.412950000000002 98.400000000000006 +-40.963622999999998 17.505030000000001 23.853847999999999 +27.766093999999999 41.277892999999999 53.799999999999997 +-45.363821000000002 19.747043000000001 40.353847999999999 +-46.725797 17.074024999999999 45.549999999999997 +17.505030000000001 40.963622999999998 65.103847999999999 +14.832011 42.325595999999997 70.299999999999997 +-38.226050000000001 27.772849999999998 98.400000000000006 +-27.772849999999998 38.226050000000001 98.400000000000006 +-28.066749999999999 38.630549999999999 98.400000000000006 +-46.725797 17.074024999999999 62.049999999999997 +-45.363821000000002 19.747043000000001 50.746152000000002 +47.25 0 98.400000000000006 +-40.963622999999998 17.505030000000001 50.746152000000002 +-42.325595999999997 14.832011 45.549999999999997 +25.093077000000001 42.639868999999997 48.603847999999999 +-45.412950000000002 14.755549999999999 98.400000000000006 +-47.75 0 98.400000000000006 +-47.25 0 98.400000000000006 +-40.963622999999998 17.505030000000001 40.353847999999999 +-45.5 0 97.391999999999996 +-42.639868999999997 25.093077000000001 56.853847999999999 +-41.277892999999999 27.766093999999999 45.549999999999997 +-42.639868999999997 25.093077000000001 50.746152000000002 +-42.639868999999997 25.093077000000001 40.353847999999999 +-38.630549999999999 28.066749999999999 98.400000000000006 +-38.239677999999998 22.851068000000001 50.746152000000002 +17.074024999999999 46.725797 70.299999999999997 +19.747043000000001 45.363821000000002 75.496151999999995 +-45.363821000000002 19.747043000000001 56.853847999999999 +43.891249999999999 14.261150000000001 98.016000000000005 +43.796149999999997 14.23025 97.920000000000002 +46.049999999999997 0 97.920000000000002 +42.639868999999997 -25.093077000000001 7.3538480000000002 +-14.755549999999999 45.412950000000002 98.400000000000006 +-36.877704999999999 25.524087000000002 45.549999999999997 +19.747043000000001 45.363821000000002 65.103847999999999 +-14.601050000000001 44.937399999999997 98.400000000000006 +37.336150000000004 27.126300000000001 98.016000000000005 +37.255249999999997 27.067499999999999 97.920000000000002 +45.363821000000002 -19.747043000000001 7.3538480000000002 +22.851068000000001 38.239677999999998 91.996151999999995 +25.524087000000002 36.877704999999999 86.799999999999997 +0 47.75 98.400000000000006 +-38.239677999999998 22.851068000000001 40.353847999999999 +25.093077000000001 42.639868999999997 75.496151999999995 +-45.363821000000002 19.747043000000001 89.853847999999999 +-42.639868999999997 25.093077000000001 83.746151999999995 +25.524087000000002 36.877704999999999 70.299999999999997 +-37.255249999999997 27.067499999999999 97.920000000000002 +-43.891249999999999 14.261150000000001 98.016000000000005 +-43.796149999999997 14.23025 97.920000000000002 +46.725797 -17.074024999999999 12.550000000000001 +22.851068000000001 38.239677999999998 65.103847999999999 +27.766093999999999 41.277892999999999 70.299999999999997 +-46.149999999999999 0 98.016000000000005 +42.639868999999997 -25.093077000000001 23.853847999999999 +42.639868999999997 -25.093077000000001 17.746151999999999 +45.363821000000002 -19.747043000000001 17.746151999999999 +27.126300000000001 37.336150000000004 98.016000000000005 +27.067499999999999 37.255249999999997 97.920000000000002 +47.552849999999999 -15.450850000000001 96.239999999999995 +-40.963622999999998 17.505030000000001 67.246151999999995 +-42.325595999999997 14.832011 62.049999999999997 +-45.363821000000002 19.747043000000001 67.246151999999995 +25.093077000000001 42.639868999999997 65.103847999999999 +46.725797 -17.074024999999999 29.050000000000001 +45.363821000000002 -19.747043000000001 23.853847999999999 +22.851068000000001 38.239677999999998 81.603847999999999 +45.412950000000002 -14.755549999999999 98.400000000000006 +-40.963622999999998 17.505030000000001 56.853847999999999 +-43.273049999999998 14.06025 97.391999999999996 +-14.23025 43.796149999999997 97.920000000000002 +-27.126300000000001 37.336150000000004 98.016000000000005 +-27.067499999999999 37.255249999999997 97.920000000000002 +-38.239677999999998 22.851068000000001 67.246151999999995 +-42.639868999999997 25.093077000000001 67.246151999999995 +17.074024999999999 46.725797 86.799999999999997 +19.747043000000001 45.363821000000002 91.996151999999995 +-37.336150000000004 27.126300000000001 98.016000000000005 +46.725797 -17.074024999999999 45.549999999999997 +45.363821000000002 -19.747043000000001 34.246152000000002 +-41.277892999999999 27.766093999999999 78.549999999999997 +-41.277892999999999 27.766093999999999 62.049999999999997 +-36.877704999999999 25.524087000000002 62.049999999999997 +19.747043000000001 45.363821000000002 81.603847999999999 +17.505030000000001 40.963622999999998 9.4961520000000004 +-38.239677999999998 22.851068000000001 56.853847999999999 +25.093077000000001 42.639868999999997 91.996151999999995 +-14.029350000000001 43.177950000000003 97.296000000000006 +-26.744250000000001 36.810250000000003 97.391999999999996 +27.766093999999999 41.277892999999999 86.799999999999997 +-36.810250000000003 26.744250000000001 97.391999999999996 +25.163990999999999 37.061183 5 +15.192107 42.142118000000004 5 +44.937399999999997 -14.601050000000001 98.400000000000006 +-40.963622999999998 17.505030000000001 83.746151999999995 +-42.325595999999997 14.832011 78.549999999999997 +25.093077000000001 42.639868999999997 81.603847999999999 +-45.363821000000002 19.747043000000001 73.353847999999999 +-44.937399999999997 14.601050000000001 98.400000000000006 +-40.963622999999998 17.505030000000001 73.353847999999999 +45.399999999999999 0 97.296000000000006 +43.177950000000003 -14.029350000000001 97.296000000000006 +45.5 0 97.391999999999996 +-38.239677999999998 22.851068000000001 83.746151999999995 +25.093077000000001 42.639868999999997 9.4961520000000004 +19.747043000000001 45.363821000000002 9.4961520000000004 +43.273049999999998 -14.06025 97.391999999999996 +-42.639868999999997 25.093077000000001 73.353847999999999 +-15.4354 47.505249999999997 96.384 +-15.4354 47.505249999999997 96.480000000000004 +0 45.399999999999999 97.296000000000006 +-14.06025 43.273049999999998 97.391999999999996 +-36.877704999999999 25.524087000000002 78.549999999999997 +17.434121000000001 46.542318999999999 5 +1.055264 44.832863000000003 95 +0 45 95.760000000000005 +43.273049999999998 14.06025 97.391999999999996 +43.177950000000003 14.029350000000001 97.296000000000006 +-42.639868999999997 25.093077000000001 89.853847999999999 +3.9898180000000001 44.368076000000002 23.853847999999999 +1.0267520000000001 44.837378999999999 29.050000000000001 +3.9898180000000001 44.368076000000002 17.746151999999999 +-38.239677999999998 22.851068000000001 73.353847999999999 +27.405999000000001 41.461371 5 +3.9898180000000001 44.368076000000002 50.746152000000002 +9.9159480000000002 43.429470999999999 50.746152000000002 +3.9898180000000001 44.368076000000002 56.853847999999999 +-37.902450000000002 27.537749999999999 98.304000000000002 +-44.699649999999998 14.5238 98.352000000000004 +14.029350000000001 43.177950000000003 97.296000000000006 +14.0139 43.130400000000002 97.200000000000003 +26.65605 36.688899999999997 97.200000000000003 +3.9898180000000001 44.368076000000002 7.3538480000000002 +1.0267520000000001 44.837378999999999 12.550000000000001 +0 45 4.7999999999999998 +-47 0 98.352000000000004 +-46.850000000000001 0 98.304000000000002 +-41.290998000000002 27.740373000000002 95 +12.879013 42.960169 12.550000000000001 +0 45.5 97.391999999999996 +9.9159480000000002 43.429470999999999 7.3538480000000002 +-27.625900000000001 38.023800000000001 98.352000000000004 +-38.023800000000001 27.625900000000001 98.352000000000004 +-40.963622999999998 17.505030000000001 89.853847999999999 +-42.312489999999997 14.857732 95 +0 50 96.239999999999995 +1.0267520000000001 44.837378999999999 62.049999999999997 +1.0267520000000001 44.837378999999999 78.549999999999997 +-27.537749999999999 37.902450000000002 98.304000000000002 +-36.890810999999999 25.498366000000001 95 +10.688492 48.307125999999997 17.746151999999999 +9.9159480000000002 43.429470999999999 17.746151999999999 +4.7623610000000003 49.245724000000003 17.746151999999999 +-14.477449999999999 44.557000000000002 98.304000000000002 +3.9898180000000001 44.368076000000002 83.746151999999995 +9.9159480000000002 43.429470999999999 83.746151999999995 +3.9898180000000001 44.368076000000002 89.853847999999999 +-14.5238 44.699649999999998 98.352000000000004 +-38.239677999999998 22.851068000000001 89.853847999999999 +1.7992950000000001 49.715021999999998 12.550000000000001 +-47.505249999999997 15.4354 96.384 +12.879013 42.960169 29.050000000000001 +36.729349999999997 26.685449999999999 97.296000000000006 +36.688899999999997 26.65605 97.200000000000003 +43.130400000000002 14.0139 97.200000000000003 +9.9159480000000002 43.429470999999999 67.246151999999995 +12.879013 42.960169 62.049999999999997 +12.879013 42.960169 78.549999999999997 +0 47 98.352000000000004 +13.651558 47.837826999999997 12.550000000000001 +-49.950000000000003 0 96.384 +14.5238 44.699649999999998 98.352000000000004 +-40.410400000000003 29.359850000000002 96.480000000000004 +10.688492 48.307125999999997 7.3538480000000002 +36.810250000000003 26.744250000000001 97.391999999999996 +-44.368076000000002 3.9898180000000001 25.996151999999999 +-44.837378999999999 1.0267520000000001 20.800000000000001 +-29.359850000000002 40.410400000000003 96.480000000000004 +26.685449999999999 36.729349999999997 97.296000000000006 +27.625900000000001 38.023800000000001 98.352000000000004 +-47.505249999999997 15.4354 96.480000000000004 +4.7623610000000003 49.245724000000003 7.3538480000000002 +-44.368076000000002 3.9898180000000001 15.603847999999999 +3.9898180000000001 44.368076000000002 67.246151999999995 +3.9898180000000001 44.368076000000002 73.353847999999999 +38.023800000000001 27.625900000000001 98.352000000000004 +12.879013 42.960169 45.549999999999997 +9.9159480000000002 43.429470999999999 34.246152000000002 +-49.950000000000003 0 96.480000000000004 +-43.429470999999999 9.9159480000000002 25.996151999999999 +46.126249999999999 14.987299999999999 98.207999999999998 +39.237299999999998 28.5076 98.207999999999998 +46.031149999999997 14.9564 98.256 +39.156399999999998 28.448799999999999 98.256 +10.688492 48.307125999999997 34.246152000000002 +4.7623610000000003 49.245724000000003 34.246152000000002 +-42.960169 12.879013 20.800000000000001 +3.9898180000000001 44.368076000000002 34.246152000000002 +46.850000000000001 0 98.304000000000002 +44.557000000000002 -14.477449999999999 98.304000000000002 +47 0 98.352000000000004 +-44.368076000000002 3.9898180000000001 91.996151999999995 +-42.797550000000001 13.905749999999999 95.760000000000005 +-45 0 95.760000000000005 +1.7992950000000001 49.715021999999998 29.050000000000001 +-43.429470999999999 9.9159480000000002 15.603847999999999 +1.0267520000000001 44.837378999999999 45.549999999999997 +44.699649999999998 14.5238 98.352000000000004 +-43.429470999999999 9.9159480000000002 9.4961520000000004 +-42.797550000000001 13.905749999999999 4.7999999999999998 +14.987299999999999 46.126249999999999 98.207999999999998 +14.9564 46.031149999999997 98.256 +28.448799999999999 39.156399999999998 98.256 +47.901049 -13.252389000000001 5 +-45 0 4.7999999999999998 +13.651558 47.837826999999997 29.050000000000001 +0 48.5 98.207999999999998 +49.715021999999998 -1.7992950000000001 20.800000000000001 +49.245724000000003 -4.7623610000000003 15.603847999999999 +44.699649999999998 -14.5238 98.352000000000004 +-44.368076000000002 3.9898180000000001 75.496151999999995 +-43.429470999999999 9.9159480000000002 75.496151999999995 +-44.368076000000002 3.9898180000000001 81.603847999999999 +0 48.399999999999999 98.256 +47.837826999999997 -13.651558 20.800000000000001 +48.307125999999997 -10.688492 15.603847999999999 +-42.960169 12.879013 37.299999999999997 +-43.429470999999999 9.9159480000000002 32.103847999999999 +10.688492 48.307125999999997 23.853847999999999 +-44.368076000000002 3.9898180000000001 42.496152000000002 +-44.837378999999999 1.0267520000000001 37.299999999999997 +-14.987299999999999 46.126249999999999 98.207999999999998 +49.245724000000003 -4.7623610000000003 25.996151999999999 +49.245724000000003 -4.7623610000000003 32.103847999999999 +9.9159480000000002 43.429470999999999 23.853847999999999 +-14.9564 46.031149999999997 98.256 +-44.368076000000002 3.9898180000000001 32.103847999999999 +4.7623610000000003 49.245724000000003 23.853847999999999 +-28.5076 39.237299999999998 98.207999999999998 +48.307125999999997 -10.688492 25.996151999999999 +47.837826999999997 -13.651558 37.299999999999997 +46.649299999999997 -15.157299999999999 97.872 +-43.023390999999997 12.479844 5 +49.049999999999997 0 97.872 +-43.429470999999999 9.9159480000000002 42.496152000000002 +-39.237299999999998 28.5076 98.207999999999998 +-39.156399999999998 28.448799999999999 98.256 +49.245724000000003 -4.7623610000000003 48.603847999999999 +48.307125999999997 -10.688492 48.603847999999999 +48.307125999999997 -10.688492 42.496152000000002 +9.9159480000000002 43.429470999999999 56.853847999999999 +-28.448799999999999 39.156399999999998 98.256 +48.307125999999997 -10.688492 32.103847999999999 +9.9159480000000002 43.429470999999999 89.853847999999999 +10.688492 48.307125999999997 50.746152000000002 +4.7623610000000003 49.245724000000003 50.746152000000002 +46.649299999999997 15.157299999999999 97.872 +-46.126249999999999 14.987299999999999 98.207999999999998 +50 0 96.239999999999995 +-46.031149999999997 14.9564 98.256 +49.715021999999998 -1.7992950000000001 37.299999999999997 +9.9159480000000002 43.429470999999999 40.353847999999999 +3.9898180000000001 44.368076000000002 40.353847999999999 +-42.960169 12.879013 53.799999999999997 +-43.429470999999999 9.9159480000000002 48.603847999999999 +1.7992950000000001 49.715021999999998 45.549999999999997 +39.682299999999998 28.830850000000002 97.872 +-46.25 0 98.063999999999993 +47.837826999999997 -13.651558 53.799999999999997 +47.837826999999997 -13.651558 70.299999999999997 +28.830850000000002 39.682299999999998 97.872 +13.651558 47.837826999999997 45.549999999999997 +12.850501 42.964685000000003 95 +49.245724000000003 -4.7623610000000003 91.996151999999995 +49.715021999999998 -1.7992950000000001 86.799999999999997 +-44.837378999999999 1.0267520000000001 53.799999999999997 +10.688492 48.307125999999997 40.353847999999999 +15.157299999999999 46.649299999999997 97.872 +-44.368076000000002 3.9898180000000001 58.996152000000002 +49.245724000000003 -4.7623610000000003 58.996152000000002 +49.715021999999998 -1.7992950000000001 53.799999999999997 +49.715021999999998 -1.7992950000000001 70.299999999999997 +49.245724000000003 -4.7623610000000003 65.103847999999999 +48.307125999999997 -10.688492 58.996152000000002 +9.9159480000000002 43.429470999999999 73.353847999999999 +-44.368076000000002 3.9898180000000001 65.103847999999999 +-44.837378999999999 1.0267520000000001 70.299999999999997 +4.7623610000000003 49.245724000000003 40.353847999999999 +0 49.049999999999997 97.872 +-44.368076000000002 3.9898180000000001 48.603847999999999 +-15.1882 46.744450000000001 97.775999999999996 +49.245724000000003 -4.7623610000000003 42.496152000000002 +-15.157299999999999 46.649299999999997 97.872 +-43.429470999999999 9.9159480000000002 58.996152000000002 +48.307125999999997 -10.688492 65.103847999999999 +-28.88965 39.763199999999998 97.775999999999996 +48.307125999999997 -10.688492 75.496151999999995 +49.245724000000003 -4.7623610000000003 75.496151999999995 +48.307125999999997 -10.688492 81.603847999999999 +10.688492 48.307125999999997 67.246151999999995 +4.7623610000000003 49.245724000000003 67.246151999999995 +-28.830850000000002 39.682299999999998 97.872 +-39.682299999999998 28.830850000000002 97.872 +1.7992950000000001 49.715021999999998 62.049999999999997 +49.651800999999999 -2.1984650000000001 5 +13.651558 47.837826999999997 62.049999999999997 +-46.649299999999997 15.157299999999999 97.872 +-9.9159480000000002 43.429470999999999 91.996151999999995 +-13.905749999999999 42.797550000000001 95.760000000000005 +-9.9159480000000002 43.429470999999999 75.496151999999995 +-3.9898180000000001 44.368076000000002 75.496151999999995 +-9.9159480000000002 43.429470999999999 81.603847999999999 +-42.960169 12.879013 70.299999999999997 +10.688492 48.307125999999997 56.853847999999999 +4.7623610000000003 49.245724000000003 56.853847999999999 +-1.0267520000000001 44.837378999999999 37.299999999999997 +-3.9898180000000001 44.368076000000002 32.103847999999999 +-1.0267520000000001 44.837378999999999 20.800000000000001 +46.25 0 98.063999999999993 +43.891249999999999 -14.261150000000001 98.016000000000005 +49.245724000000003 -4.7623610000000003 81.603847999999999 +-9.9159480000000002 43.429470999999999 32.103847999999999 +-12.879013 42.960169 37.299999999999997 +-9.9159480000000002 43.429470999999999 25.996151999999999 +-13.905749999999999 42.797550000000001 4.7999999999999998 +-9.9159480000000002 43.429470999999999 15.603847999999999 +-12.879013 42.960169 20.800000000000001 +10.688492 48.307125999999997 83.746151999999995 +4.7623610000000003 49.245724000000003 83.746151999999995 +43.986350000000002 14.29205 98.063999999999993 +46.149999999999999 0 98.016000000000005 +-3.9898180000000001 44.368076000000002 9.4961520000000004 +-1.4259219999999999 44.774157000000002 5 +-43.429470999999999 9.9159480000000002 65.103847999999999 +1.7992950000000001 49.715021999999998 78.549999999999997 +-12.879013 42.960169 86.799999999999997 +-44.368076000000002 3.9898180000000001 9.4961520000000004 +13.651558 47.837826999999997 78.549999999999997 +48.307125999999997 -10.688492 91.996151999999995 +-1.0267520000000001 44.837378999999999 53.799999999999997 +-3.9898180000000001 44.368076000000002 48.603847999999999 +-3.9898180000000001 44.368076000000002 42.496152000000002 +44.794750000000001 -14.5547 98.352000000000004 +0 46.149999999999999 98.016000000000005 +14.261150000000001 43.891249999999999 98.016000000000005 +-12.879013 42.960169 53.799999999999997 +38.104700000000001 27.684699999999999 98.352000000000004 +44.794750000000001 14.5547 98.352000000000004 +-44.774157000000002 1.4259219999999999 5 +4.7623610000000003 49.245724000000003 73.353847999999999 +48.307125999999997 -10.688492 9.4961520000000004 +-9.9159480000000002 43.429470999999999 65.103847999999999 +-12.879013 42.960169 70.299999999999997 +-9.9159480000000002 43.429470999999999 58.996152000000002 +27.684699999999999 38.104700000000001 98.352000000000004 +-14.261150000000001 43.891249999999999 98.016000000000005 +4.7623610000000003 49.245724000000003 89.853847999999999 +1.827807 49.710507 95 +49.245724000000003 -4.7623610000000003 9.4961520000000004 +-9.9159480000000002 43.429470999999999 42.496152000000002 +13.623046 47.842343 95 +47.837826999999997 13.651558 12.550000000000001 +47.552849999999999 15.450850000000001 96.239999999999995 +10.688492 48.307125999999997 89.853847999999999 +49.715021999999998 1.7992950000000001 62.049999999999997 +49.245724000000003 4.7623610000000003 67.246151999999995 +49.715021999999998 1.7992950000000001 78.549999999999997 +-43.320599999999999 14.075699999999999 97.488 +47.837826999999997 13.651558 29.050000000000001 +49.715021999999998 1.7992950000000001 12.550000000000001 +49.245724000000003 4.7623610000000003 17.746151999999999 +49.245724000000003 4.7623610000000003 23.853847999999999 +48.307125999999997 10.688492 7.3538480000000002 +47.837826999999997 13.651558 78.549999999999997 +47.837826999999997 13.651558 62.049999999999997 +49.245724000000003 4.7623610000000003 7.3538480000000002 +-10.688492 48.307125999999997 25.996151999999999 +49.245724000000003 4.7623610000000003 50.746152000000002 +48.307125999999997 10.688492 50.746152000000002 +48.307125999999997 10.688492 56.853847999999999 +-13.651558 47.837826999999997 20.800000000000001 +-26.773599999999998 36.850700000000003 97.488 +-10.688492 48.307125999999997 15.603847999999999 +-36.850700000000003 26.773599999999998 97.488 +42.325595999999997 -14.832011 45.549999999999997 +45.363821000000002 19.747043000000001 48.603847999999999 +46.725797 17.074024999999999 53.799999999999997 +46.725797 17.074024999999999 37.299999999999997 +-1.0267520000000001 44.837378999999999 86.799999999999997 +-3.9898180000000001 44.368076000000002 91.996151999999995 +-1.7992950000000001 49.715021999999998 86.799999999999997 +-39.601399999999998 28.772099999999998 97.920000000000002 +-44.368076000000002 -3.9898180000000001 7.3538480000000002 +-44.837378999999999 -1.0267520000000001 12.550000000000001 +40.963622999999998 -17.505030000000001 50.746152000000002 +46.725797 17.074024999999999 20.800000000000001 +-4.7623610000000003 49.245724000000003 91.996151999999995 +-46.554200000000002 15.1264 97.920000000000002 +46.350000000000001 0 98.159999999999997 +41.277892999999999 27.766093999999999 70.299999999999997 +42.639868999999997 25.093077000000001 65.103847999999999 +42.639868999999997 25.093077000000001 58.996152000000002 +-3.9898180000000001 44.368076000000002 81.603847999999999 +-49.049999999999997 0 97.872 +-44.368076000000002 -3.9898180000000001 17.746151999999999 +44.081449999999997 -14.322950000000001 98.159999999999997 +45.363821000000002 19.747043000000001 75.496151999999995 +-4.7623610000000003 49.245724000000003 81.603847999999999 +-48.950000000000003 0 97.920000000000002 +44.081449999999997 14.322950000000001 98.159999999999997 +42.639868999999997 25.093077000000001 75.496151999999995 +-43.429470999999999 -9.9159480000000002 7.3538480000000002 +-10.688492 48.307125999999997 81.603847999999999 +14.322950000000001 44.081449999999997 98.159999999999997 +40.450850000000003 29.389250000000001 96.239999999999995 +-2.1984650000000001 49.651800999999999 5 +-43.429470999999999 -9.9159480000000002 17.746151999999999 +27.243849999999998 37.497950000000003 98.159999999999997 +41.277892999999999 27.766093999999999 53.799999999999997 +-4.7623610000000003 49.245724000000003 9.4961520000000004 +-45.75 0 97.680000000000007 +-44.081449999999997 14.322950000000001 98.159999999999997 +42.639868999999997 25.093077000000001 48.603847999999999 +42.639868999999997 25.093077000000001 42.496152000000002 +-12.479844 43.023390999999997 5 +-44.368076000000002 -3.9898180000000001 23.853847999999999 +-44.837378999999999 -1.0267520000000001 29.050000000000001 +-46.350000000000001 0 98.159999999999997 +45.363821000000002 19.747043000000001 32.103847999999999 +-13.252389000000001 47.901049 5 +0 46.350000000000001 98.159999999999997 +45.363821000000002 19.747043000000001 58.996152000000002 +-29.306730000000002 33.549370000000003 15.603847999999999 +-33.549370000000003 29.306730000000002 15.603847999999999 +-29.306730000000002 33.549370000000003 9.4961520000000004 +-44.368076000000002 -3.9898180000000001 34.246152000000002 +46.725797 17.074024999999999 70.299999999999997 +45.363821000000002 19.747043000000001 65.103847999999999 +-35.670690999999998 27.185409 53.799999999999997 +-33.549370000000003 29.306730000000002 58.996152000000002 +-35.670690999999998 27.185409 70.299999999999997 +-42.797550000000001 -13.905749999999999 4.7999999999999998 +-17.505030000000001 40.963622999999998 17.746151999999999 +-22.851068000000001 38.239677999999998 17.746151999999999 +48.307125999999997 10.688492 83.746151999999995 +47.837826999999997 13.651558 45.549999999999997 +48.307125999999997 10.688492 17.746151999999999 +49.715021999999998 1.7992950000000001 29.050000000000001 +48.307125999999997 10.688492 23.853847999999999 +49.245724000000003 4.7623610000000003 56.853847999999999 +48.307125999999997 10.688492 40.353847999999999 +48.307125999999997 10.688492 34.246152000000002 +-48.399999999999999 0 98.256 +-45.888500000000001 14.91005 98.304000000000002 +48.307125999999997 10.688492 67.246151999999995 +48.307125999999997 10.688492 73.353847999999999 +49.245724000000003 4.7623610000000003 34.246152000000002 +49.245724000000003 4.7623610000000003 40.353847999999999 +49.715021999999998 1.7992950000000001 45.549999999999997 +48.399999999999999 0 98.256 +45.888500000000001 -14.91005 98.304000000000002 +46.031149999999997 -14.9564 98.256 +48.25 0 98.304000000000002 +47.505249999999997 -15.4354 96.384 +45.888500000000001 14.91005 98.304000000000002 +49.245724000000003 4.7623610000000003 73.353847999999999 +39.035049999999998 28.36065 98.304000000000002 +28.36065 39.035049999999998 98.304000000000002 +47.842343 13.623046 95 +40.963622999999998 -17.505030000000001 7.3538480000000002 +49.710507 1.827807 95 +38.239677999999998 -22.851068000000001 7.3538480000000002 +42.325595999999997 -14.832011 12.550000000000001 +14.91005 45.888500000000001 98.304000000000002 +49.245724000000003 4.7623610000000003 83.746151999999995 +0 48.25 98.304000000000002 +48.307125999999997 10.688492 89.853847999999999 +49.245724000000003 4.7623610000000003 89.853847999999999 +40.963622999999998 -17.505030000000001 17.746151999999999 +38.239677999999998 -22.851068000000001 17.746151999999999 +40.963622999999998 -17.505030000000001 23.853847999999999 +38.239677999999998 -22.851068000000001 23.853847999999999 +42.325595999999997 -14.832011 29.050000000000001 +41.277892999999999 27.766093999999999 20.800000000000001 +40.963622999999998 -17.505030000000001 34.246152000000002 +41.277892999999999 27.766093999999999 37.299999999999997 +42.639868999999997 25.093077000000001 15.603847999999999 +45.363821000000002 19.747043000000001 42.496152000000002 +42.639868999999997 25.093077000000001 25.996151999999999 +42.639868999999997 25.093077000000001 32.103847999999999 +45.363821000000002 19.747043000000001 25.996151999999999 +40.963622999999998 -17.505030000000001 40.353847999999999 +37.497950000000003 27.243849999999998 98.159999999999997 +-14.322950000000001 44.081449999999997 98.159999999999997 +-27.243849999999998 37.497950000000003 98.159999999999997 +-37.497950000000003 27.243849999999998 98.159999999999997 +38.954149999999998 28.301850000000002 98.304000000000002 +45.793349999999997 14.879149999999999 98.304000000000002 +28.301850000000002 38.954149999999998 98.304000000000002 +14.879149999999999 45.793349999999997 98.304000000000002 +45.363821000000002 19.747043000000001 15.603847999999999 +0 48.149999999999999 98.304000000000002 +-38.954149999999998 28.301850000000002 98.304000000000002 +-28.301850000000002 38.954149999999998 98.304000000000002 +48.149999999999999 0 98.304000000000002 +-48.149999999999999 0 98.304000000000002 +49.950000000000003 0 96.384 +30.697821999999999 39.142277999999997 95 +-14.879149999999999 45.793349999999997 98.304000000000002 +32.798729999999999 37.041370000000001 40.353847999999999 +43.429470999999999 -9.9159480000000002 15.603847999999999 +42.960169 -12.879013 20.800000000000001 +44.368076000000002 -3.9898180000000001 15.603847999999999 +44.837378999999999 -1.0267520000000001 20.800000000000001 +32.798729999999999 37.041370000000001 56.853847999999999 +44.368076000000002 -3.9898180000000001 25.996151999999999 +30.677409000000001 39.162691000000002 78.549999999999997 +43.429470999999999 -9.9159480000000002 25.996151999999999 +43.429470999999999 -9.9159480000000002 32.103847999999999 +42.960169 -12.879013 37.299999999999997 +32.798729999999999 37.041370000000001 73.353847999999999 +44.368076000000002 -3.9898180000000001 32.103847999999999 +44.837378999999999 -1.0267520000000001 37.299999999999997 +45.793349999999997 -14.879149999999999 98.304000000000002 +-45.793349999999997 14.879149999999999 98.304000000000002 +44.368076000000002 -3.9898180000000001 42.496152000000002 +39.162691000000002 30.677409000000001 78.549999999999997 +39.162691000000002 30.677409000000001 62.049999999999997 +43.429470999999999 -9.9159480000000002 42.496152000000002 +37.041370000000001 32.798729999999999 56.853847999999999 +43.429470999999999 -9.9159480000000002 48.603847999999999 +42.960169 -12.879013 53.799999999999997 +-46.5 0 98.207999999999998 +44.368076000000002 -3.9898180000000001 48.603847999999999 +44.837378999999999 -1.0267520000000001 53.799999999999997 +32.798729999999999 37.041370000000001 83.746151999999995 +37.041370000000001 32.798729999999999 83.746151999999995 +44.368076000000002 -3.9898180000000001 58.996152000000002 +37.041370000000001 32.798729999999999 73.353847999999999 +43.429470999999999 -9.9159480000000002 58.996152000000002 +39.142277999999997 30.697821999999999 95 +43.429470999999999 -9.9159480000000002 65.103847999999999 +37.041370000000001 32.798729999999999 89.853847999999999 +32.798729999999999 37.041370000000001 89.853847999999999 +42.960169 -12.879013 70.299999999999997 +44.368076000000002 -3.9898180000000001 65.103847999999999 +44.837378999999999 -1.0267520000000001 70.299999999999997 +47.505249999999997 -15.4354 96.480000000000004 +49.950000000000003 0 96.480000000000004 +44.368076000000002 -3.9898180000000001 75.496151999999995 +42.797550000000001 -13.905749999999999 4.7999999999999998 +36.405749999999998 -26.45035 4.7999999999999998 +46.5 0 98.207999999999998 +43.429470999999999 -9.9159480000000002 75.496151999999995 +44.224150000000002 -14.369300000000001 98.207999999999998 +44.224150000000002 14.369300000000001 98.207999999999998 +42.797550000000001 -13.905749999999999 95.760000000000005 +37.619300000000003 27.332000000000001 98.207999999999998 +17.074024999999999 46.725797 20.800000000000001 +25.093077000000001 42.639868999999997 32.103847999999999 +44.368076000000002 -3.9898180000000001 81.603847999999999 +27.332000000000001 37.619300000000003 98.207999999999998 +43.429470999999999 -9.9159480000000002 81.603847999999999 +44.837378999999999 -1.0267520000000001 86.799999999999997 +14.369300000000001 44.224150000000002 98.207999999999998 +0 46.5 98.207999999999998 +44.368076000000002 -3.9898180000000001 91.996151999999995 +-44.224150000000002 14.369300000000001 98.207999999999998 +43.429470999999999 -9.9159480000000002 91.996151999999995 +-14.369300000000001 44.224150000000002 98.207999999999998 +44.774157000000002 -1.4259219999999999 5 +-27.332000000000001 37.619300000000003 98.207999999999998 +27.766093999999999 41.277892999999999 37.299999999999997 +43.023390999999997 -12.479844 5 +43.429470999999999 -9.9159480000000002 9.4961520000000004 +-37.619300000000003 27.332000000000001 98.207999999999998 +42.325595999999997 -14.832011 62.049999999999997 +40.963622999999998 -17.505030000000001 56.853847999999999 +44.368076000000002 -3.9898180000000001 9.4961520000000004 +47.505249999999997 15.4354 96.384 +43.429470999999999 9.9159480000000002 17.746151999999999 +42.960169 12.879013 12.550000000000001 +44.837378999999999 1.0267520000000001 12.550000000000001 +44.368076000000002 3.9898180000000001 7.3538480000000002 +43.429470999999999 9.9159480000000002 7.3538480000000002 +43.429470999999999 9.9159480000000002 34.246152000000002 +42.960169 12.879013 29.050000000000001 +45.650700000000001 -14.832800000000001 98.352000000000004 +44.368076000000002 3.9898180000000001 34.246152000000002 +48 0 98.352000000000004 +44.837378999999999 1.0267520000000001 29.050000000000001 +45.650700000000001 14.832800000000001 98.352000000000004 +38.832799999999999 28.213699999999999 98.352000000000004 +28.213699999999999 38.832799999999999 98.352000000000004 +47.505249999999997 15.4354 96.480000000000004 +14.832800000000001 45.650700000000001 98.352000000000004 +45 0 4.7999999999999998 +43.429470999999999 9.9159480000000002 50.746152000000002 +0 48 98.352000000000004 +42.960169 12.879013 45.549999999999997 +44.368076000000002 3.9898180000000001 50.746152000000002 +-14.832800000000001 45.650700000000001 98.352000000000004 +45 0 95.760000000000005 +44.837378999999999 1.0267520000000001 45.549999999999997 +-28.213699999999999 38.832799999999999 98.352000000000004 +-38.832799999999999 28.213699999999999 98.352000000000004 +44.368076000000002 3.9898180000000001 40.353847999999999 +-45.650700000000001 14.832800000000001 98.352000000000004 +43.429470999999999 9.9159480000000002 40.353847999999999 +-48 0 98.352000000000004 +-46.600000000000001 0 98.256 +43.429470999999999 9.9159480000000002 67.246151999999995 +42.960169 12.879013 62.049999999999997 +44.368076000000002 3.9898180000000001 67.246151999999995 +44.837378999999999 1.0267520000000001 62.049999999999997 +44.368076000000002 3.9898180000000001 56.853847999999999 +43.429470999999999 9.9159480000000002 56.853847999999999 +43.429470999999999 9.9159480000000002 83.746151999999995 +42.960169 12.879013 78.549999999999997 +44.368076000000002 3.9898180000000001 83.746151999999995 +44.837378999999999 1.0267520000000001 78.549999999999997 +44.368076000000002 3.9898180000000001 73.353847999999999 +43.429470999999999 9.9159480000000002 73.353847999999999 +10.688492 48.307125999999997 73.353847999999999 +44.832863000000003 1.055264 95 +42.964685000000003 12.850501 95 +43.429470999999999 9.9159480000000002 89.853847999999999 +44.368076000000002 3.9898180000000001 89.853847999999999 +-13.651558 47.837826999999997 70.299999999999997 +-13.651558 47.837826999999997 86.799999999999997 +-4.7623610000000003 49.245724000000003 25.996151999999999 +-4.7623610000000003 49.245724000000003 32.103847999999999 +-1.7992950000000001 49.715021999999998 20.800000000000001 +-38.751899999999999 28.154900000000001 98.352000000000004 +-28.154900000000001 38.751899999999999 98.352000000000004 +-10.688492 48.307125999999997 32.103847999999999 +-13.651558 47.837826999999997 37.299999999999997 +-4.7623610000000003 49.245724000000003 15.603847999999999 +-47.899999999999999 0 98.352000000000004 +-4.7623610000000003 49.245724000000003 65.103847999999999 +-1.7992950000000001 49.715021999999998 53.799999999999997 +-4.7623610000000003 49.245724000000003 58.996152000000002 +-13.651558 47.837826999999997 53.799999999999997 +-14.8019 45.555599999999998 98.352000000000004 +-1.7992950000000001 49.715021999999998 37.299999999999997 +-10.688492 48.307125999999997 75.496151999999995 +0 47.899999999999999 98.352000000000004 +-10.688492 48.307125999999997 58.996152000000002 +-10.688492 48.307125999999997 65.103847999999999 +14.8019 45.555599999999998 98.352000000000004 +-4.7623610000000003 49.245724000000003 48.603847999999999 +-10.688492 48.307125999999997 42.496152000000002 +-10.688492 48.307125999999997 48.603847999999999 +47.899999999999999 0 98.352000000000004 +-4.7623610000000003 49.245724000000003 42.496152000000002 +45.555599999999998 -14.8019 98.352000000000004 +-4.7623610000000003 49.245724000000003 75.496151999999995 +-1.7992950000000001 49.715021999999998 70.299999999999997 +40.963622999999998 17.505030000000001 48.603847999999999 +-10.688492 48.307125999999997 91.996151999999995 +-10.688492 48.307125999999997 9.4961520000000004 +-45.555599999999998 14.8019 98.352000000000004 +-37.041370000000001 32.798729999999999 91.996151999999995 +33.549370000000003 29.306730000000002 40.353847999999999 +-32.798729999999999 37.041370000000001 15.603847999999999 +-30.963183000000001 38.876916999999999 5 +-32.798729999999999 37.041370000000001 9.4961520000000004 +27.205822000000001 35.650278 95 +-37.041370000000001 32.798729999999999 65.103847999999999 +-37.041370000000001 32.798729999999999 25.996151999999999 +-32.798729999999999 37.041370000000001 25.996151999999999 +-37.041370000000001 32.798729999999999 32.103847999999999 +29.306730000000002 33.549370000000003 83.746151999999995 +33.549370000000003 29.306730000000002 83.746151999999995 +29.306730000000002 33.549370000000003 89.853847999999999 +-37.041370000000001 32.798729999999999 15.603847999999999 +35.670690999999998 27.185409 78.549999999999997 +29.306730000000002 33.549370000000003 73.353847999999999 +-32.798729999999999 37.041370000000001 65.103847999999999 +-32.798729999999999 37.041370000000001 58.996152000000002 +-37.041370000000001 32.798729999999999 81.603847999999999 +-37.041370000000001 32.798729999999999 75.496151999999995 +-39.162691000000002 30.677409000000001 86.799999999999997 +-32.798729999999999 37.041370000000001 75.496151999999995 +-30.677409000000001 39.162691000000002 86.799999999999997 +-30.677409000000001 39.162691000000002 70.299999999999997 +-32.798729999999999 37.041370000000001 81.603847999999999 +-32.798729999999999 37.041370000000001 91.996151999999995 +33.549370000000003 29.306730000000002 89.853847999999999 +33.549370000000003 29.306730000000002 73.353847999999999 +-37.041370000000001 32.798729999999999 9.4961520000000004 +-38.876916999999999 30.963183000000001 5 +35.650278 27.205822000000001 95 +-3.9898180000000001 44.368076000000002 15.603847999999999 +-3.9898180000000001 44.368076000000002 25.996151999999999 +47.100000000000001 0 98.352000000000004 +-35.670690999999998 27.185409 20.800000000000001 +-36.405749999999998 26.45035 4.7999999999999998 +-26.45035 36.405749999999998 4.7999999999999998 +-26.45035 36.405749999999998 95.760000000000005 +-27.185409 35.670690999999998 20.800000000000001 +-44.837378999999999 -1.0267520000000001 45.549999999999997 +-27.471183 35.384917000000002 5 +-36.405749999999998 26.45035 95.760000000000005 +-33.549370000000003 29.306730000000002 9.4961520000000004 +-33.549370000000003 29.306730000000002 25.996151999999999 +-35.670690999999998 27.185409 37.299999999999997 +-35.384917000000002 27.471183 5 +-33.549370000000003 29.306730000000002 42.496152000000002 +-9.9159480000000002 43.429470999999999 48.603847999999999 +-3.9898180000000001 44.368076000000002 58.996152000000002 +-1.0267520000000001 44.837378999999999 70.299999999999997 +-3.9898180000000001 44.368076000000002 65.103847999999999 +-9.9159480000000002 43.429470999999999 9.4961520000000004 +-25.524087000000002 36.877704999999999 12.550000000000001 +-14.832011 42.325595999999997 12.550000000000001 +-17.505030000000001 40.963622999999998 7.3538480000000002 +-22.851068000000001 38.239677999999998 7.3538480000000002 +-17.505030000000001 40.963622999999998 34.246152000000002 +-22.851068000000001 38.239677999999998 34.246152000000002 +-25.524087000000002 36.877704999999999 29.050000000000001 +-14.832011 42.325595999999997 29.050000000000001 +-17.505030000000001 40.963622999999998 23.853847999999999 +-22.851068000000001 38.239677999999998 23.853847999999999 +-17.505030000000001 40.963622999999998 50.746152000000002 +-22.851068000000001 38.239677999999998 50.746152000000002 +-25.524087000000002 36.877704999999999 45.549999999999997 +-14.832011 42.325595999999997 45.549999999999997 +-17.505030000000001 40.963622999999998 40.353847999999999 +-22.851068000000001 38.239677999999998 40.353847999999999 +-17.505030000000001 40.963622999999998 67.246151999999995 +-22.851068000000001 38.239677999999998 67.246151999999995 +-25.524087000000002 36.877704999999999 62.049999999999997 +-14.832011 42.325595999999997 62.049999999999997 +-17.505030000000001 40.963622999999998 56.853847999999999 +-22.851068000000001 38.239677999999998 56.853847999999999 +-17.505030000000001 40.963622999999998 83.746151999999995 +-22.851068000000001 38.239677999999998 83.746151999999995 +-25.524087000000002 36.877704999999999 78.549999999999997 +-14.832011 42.325595999999997 78.549999999999997 +-17.505030000000001 40.963622999999998 73.353847999999999 +-22.851068000000001 38.239677999999998 73.353847999999999 +-25.498366000000001 36.890810999999999 95 +-22.851068000000001 38.239677999999998 89.853847999999999 +-14.857732 42.312489999999997 95 +-17.505030000000001 40.963622999999998 89.853847999999999 +-29.306730000000002 33.549370000000003 25.996151999999999 +-29.306730000000002 33.549370000000003 42.496152000000002 +-27.185409 35.670690999999998 37.299999999999997 +-29.306730000000002 33.549370000000003 32.103847999999999 +-33.549370000000003 29.306730000000002 32.103847999999999 +-29.306730000000002 33.549370000000003 58.996152000000002 +-27.185409 35.670690999999998 53.799999999999997 +-29.306730000000002 33.549370000000003 48.603847999999999 +-33.549370000000003 29.306730000000002 48.603847999999999 +-29.306730000000002 33.549370000000003 75.496151999999995 +-33.549370000000003 29.306730000000002 75.496151999999995 +-27.185409 35.670690999999998 70.299999999999997 +-29.306730000000002 33.549370000000003 65.103847999999999 +-33.549370000000003 29.306730000000002 65.103847999999999 +-29.306730000000002 33.549370000000003 91.996151999999995 +-33.549370000000003 29.306730000000002 91.996151999999995 +-35.670690999999998 27.185409 86.799999999999997 +-27.185409 35.670690999999998 86.799999999999997 +-29.306730000000002 33.549370000000003 81.603847999999999 +-33.549370000000003 29.306730000000002 81.603847999999999 +-15.41995 47.457700000000003 96.623999999999995 +-29.330500000000001 40.369950000000003 96.623999999999995 +-40.369950000000003 29.330500000000001 96.623999999999995 +-47.457700000000003 15.41995 96.623999999999995 +-49.899999999999999 0 96.623999999999995 +-26.45035 -36.405749999999998 4.7999999999999998 +-36.405749999999998 -26.45035 4.7999999999999998 +-13.905749999999999 -42.797550000000001 4.7999999999999998 +0 -45 4.7999999999999998 +13.905749999999999 -42.797550000000001 4.7999999999999998 +26.45035 -36.405749999999998 4.7999999999999998 +-42.797550000000001 13.905749999999999 96.239999999999995 +-45 0 96.239999999999995 +-36.405749999999998 26.45035 96.239999999999995 +0 49.899999999999999 96.623999999999995 +-26.45035 36.405749999999998 96.239999999999995 +-15.41995 47.457700000000003 96.719999999999999 +0 49.899999999999999 96.719999999999999 +-29.330500000000001 40.369950000000003 96.719999999999999 +-40.369950000000003 29.330500000000001 96.719999999999999 +-47.457700000000003 15.41995 96.719999999999999 +-49.899999999999999 0 96.719999999999999 +-45.049999999999997 0 96.384 +-42.845100000000002 13.921200000000001 96.384 +-26.479749999999999 36.446199999999997 96.384 +-36.446199999999997 26.479749999999999 96.384 +47.457700000000003 -15.41995 96.623999999999995 +49.899999999999999 0 96.623999999999995 +45 0 96.239999999999995 +42.797550000000001 -13.905749999999999 96.239999999999995 +47.457700000000003 15.41995 96.623999999999995 +42.797550000000001 13.905749999999999 96.239999999999995 +40.369950000000003 29.330500000000001 96.623999999999995 +29.330500000000001 40.369950000000003 96.623999999999995 +36.405749999999998 26.45035 96.239999999999995 +15.41995 47.457700000000003 96.623999999999995 +26.45035 36.405749999999998 96.239999999999995 +13.905749999999999 42.797550000000001 96.239999999999995 +0 45 96.239999999999995 +-13.905749999999999 42.797550000000001 96.239999999999995 +15.41995 47.457700000000003 96.719999999999999 +15.404500000000001 47.410150000000002 96.864000000000004 +0 49.850000000000001 96.864000000000004 +-15.404500000000001 47.410150000000002 96.864000000000004 +-29.301100000000002 40.329500000000003 96.864000000000004 +-40.329500000000003 29.301100000000002 96.864000000000004 +-47.410150000000002 15.404500000000001 96.864000000000004 +-49.850000000000001 0 96.864000000000004 +-45.049999999999997 0 96.480000000000004 +-42.845100000000002 13.921200000000001 96.480000000000004 +-36.446199999999997 26.479749999999999 96.480000000000004 +-26.479749999999999 36.446199999999997 96.480000000000004 +-13.921200000000001 42.845100000000002 96.384 +-13.921200000000001 42.845100000000002 96.480000000000004 +47.457700000000003 -15.41995 96.719999999999999 +49.899999999999999 0 96.719999999999999 +45.049999999999997 0 96.384 +42.845100000000002 -13.921200000000001 96.384 +47.457700000000003 15.41995 96.719999999999999 +42.845100000000002 13.921200000000001 96.384 +40.369950000000003 29.330500000000001 96.719999999999999 +36.446199999999997 26.479749999999999 96.384 +29.330500000000001 40.369950000000003 96.719999999999999 +26.479749999999999 36.446199999999997 96.384 +13.921200000000001 42.845100000000002 96.384 +0 45.049999999999997 96.384 +29.301100000000002 40.329500000000003 96.864000000000004 +29.271699999999999 40.289050000000003 96.959999999999994 +15.389049999999999 47.3626 96.959999999999994 +0 49.799999999999997 96.959999999999994 +-15.389049999999999 47.3626 96.959999999999994 +-29.271699999999999 40.289050000000003 96.959999999999994 +-40.289050000000003 29.271699999999999 96.959999999999994 +-47.3626 15.389049999999999 96.959999999999994 +-49.799999999999997 0 96.959999999999994 +-45.100000000000001 0 96.623999999999995 +-42.892650000000003 13.93665 96.623999999999995 +-26.5091 36.486649999999997 96.623999999999995 +-36.486649999999997 26.5091 96.623999999999995 +0 45.049999999999997 96.480000000000004 +-13.93665 42.892650000000003 96.623999999999995 +47.410150000000002 -15.404500000000001 96.864000000000004 +49.850000000000001 0 96.864000000000004 +42.845100000000002 -13.921200000000001 96.480000000000004 +45.049999999999997 0 96.480000000000004 +42.845100000000002 13.921200000000001 96.480000000000004 +47.410150000000002 15.404500000000001 96.864000000000004 +36.446199999999997 26.479749999999999 96.480000000000004 +40.329500000000003 29.301100000000002 96.864000000000004 +26.479749999999999 36.446199999999997 96.480000000000004 +13.921200000000001 42.845100000000002 96.480000000000004 +40.289050000000003 29.271699999999999 96.959999999999994 +29.2423 40.248600000000003 97.103999999999999 +40.248600000000003 29.2423 97.103999999999999 +15.3736 47.315049999999999 97.103999999999999 +0 49.75 97.103999999999999 +-15.3736 47.315049999999999 97.103999999999999 +-29.2423 40.248600000000003 97.103999999999999 +-40.248600000000003 29.2423 97.103999999999999 +-47.315049999999999 15.3736 97.103999999999999 +-49.75 0 97.103999999999999 +-45.100000000000001 0 96.719999999999999 +-42.892650000000003 13.93665 96.719999999999999 +-36.486649999999997 26.5091 96.719999999999999 +-26.5091 36.486649999999997 96.719999999999999 +-13.93665 42.892650000000003 96.719999999999999 +0 45.100000000000001 96.623999999999995 +0 45.100000000000001 96.719999999999999 +47.3626 -15.389049999999999 96.959999999999994 +45.100000000000001 0 96.623999999999995 +42.892650000000003 -13.93665 96.623999999999995 +49.799999999999997 0 96.959999999999994 +42.892650000000003 13.93665 96.623999999999995 +36.486649999999997 26.5091 96.623999999999995 +47.3626 15.389049999999999 96.959999999999994 +26.5091 36.486649999999997 96.623999999999995 +13.93665 42.892650000000003 96.623999999999995 +47.315049999999999 15.3736 97.103999999999999 +47.219949999999997 15.342700000000001 97.200000000000003 +40.167700000000004 29.18355 97.200000000000003 +29.18355 40.167700000000004 97.200000000000003 +15.342700000000001 47.219949999999997 97.200000000000003 +0 49.649999999999999 97.200000000000003 +-15.342700000000001 47.219949999999997 97.200000000000003 +-29.18355 40.167700000000004 97.200000000000003 +-40.167700000000004 29.18355 97.200000000000003 +-47.219949999999997 15.342700000000001 97.200000000000003 +-49.649999999999999 0 97.200000000000003 +-45.149999999999999 0 96.864000000000004 +-42.940199999999997 13.9521 96.864000000000004 +-26.538499999999999 36.527099999999997 96.864000000000004 +-36.527099999999997 26.538499999999999 96.864000000000004 +-13.9521 42.940199999999997 96.864000000000004 +13.93665 42.892650000000003 96.719999999999999 +0 45.149999999999999 96.864000000000004 +42.892650000000003 -13.93665 96.719999999999999 +45.100000000000001 0 96.719999999999999 +47.315049999999999 -15.3736 97.103999999999999 +42.892650000000003 13.93665 96.719999999999999 +49.75 0 97.103999999999999 +36.486649999999997 26.5091 96.719999999999999 +26.5091 36.486649999999997 96.719999999999999 +49.649999999999999 0 97.200000000000003 +49.600000000000001 0 97.296000000000006 +47.172400000000003 15.327249999999999 97.296000000000006 +40.127249999999997 29.154150000000001 97.296000000000006 +29.154150000000001 40.127249999999997 97.296000000000006 +15.327249999999999 47.172400000000003 97.296000000000006 +0 49.600000000000001 97.296000000000006 +-15.327249999999999 47.172400000000003 97.296000000000006 +-29.154150000000001 40.127249999999997 97.296000000000006 +-40.127249999999997 29.154150000000001 97.296000000000006 +-47.172400000000003 15.327249999999999 97.296000000000006 +-49.600000000000001 0 97.296000000000006 +-45.200000000000003 0 96.959999999999994 +-42.987749999999998 13.967549999999999 96.959999999999994 +-26.567900000000002 36.567549999999997 96.959999999999994 +-36.567549999999997 26.567900000000002 96.959999999999994 +-13.967549999999999 42.987749999999998 96.959999999999994 +13.9521 42.940199999999997 96.864000000000004 +0 45.200000000000003 96.959999999999994 +45.149999999999999 0 96.864000000000004 +42.940199999999997 -13.9521 96.864000000000004 +42.940199999999997 13.9521 96.864000000000004 +47.219949999999997 -15.342700000000001 97.200000000000003 +36.527099999999997 26.538499999999999 96.864000000000004 +26.538499999999999 36.527099999999997 96.864000000000004 +47.172400000000003 -15.327249999999999 97.296000000000006 +47.077300000000001 -15.29635 97.391999999999996 +49.5 0 97.391999999999996 +47.077300000000001 15.29635 97.391999999999996 +40.046349999999997 29.09535 97.391999999999996 +29.09535 40.046349999999997 97.391999999999996 +15.29635 47.077300000000001 97.391999999999996 +0 49.5 97.391999999999996 +-15.29635 47.077300000000001 97.391999999999996 +-29.09535 40.046349999999997 97.391999999999996 +-40.046349999999997 29.09535 97.391999999999996 +-47.077300000000001 15.29635 97.391999999999996 +-49.5 0 97.391999999999996 +-45.25 0 97.103999999999999 +-43.035299999999999 13.983000000000001 97.103999999999999 +-26.597300000000001 36.607999999999997 97.103999999999999 +-36.607999999999997 26.597300000000001 97.103999999999999 +-13.983000000000001 43.035299999999999 97.103999999999999 +13.967549999999999 42.987749999999998 96.959999999999994 +0 45.25 97.103999999999999 +45.200000000000003 0 96.959999999999994 +42.987749999999998 -13.967549999999999 96.959999999999994 +42.987749999999998 13.967549999999999 96.959999999999994 +-14.0139 43.130400000000002 97.200000000000003 +0 45.350000000000001 97.200000000000003 +45.25 0 97.103999999999999 +43.035299999999999 -13.983000000000001 97.103999999999999 +45.350000000000001 0 97.200000000000003 +43.130400000000002 -14.0139 97.200000000000003 +43.035299999999999 13.983000000000001 97.103999999999999 +45.549999999999997 0 97.488 +43.320599999999999 -14.075699999999999 97.488 +43.320599999999999 14.075699999999999 97.488 +-14.075699999999999 43.320599999999999 97.488 +36.850700000000003 26.773599999999998 97.488 +14.06025 43.273049999999998 97.391999999999996 +0 45.549999999999997 97.488 +26.744250000000001 36.810250000000003 97.391999999999996 +26.773599999999998 36.850700000000003 97.488 +46.554200000000002 -15.1264 97.920000000000002 +48.950000000000003 0 97.920000000000002 +46.554200000000002 15.1264 97.920000000000002 +39.601399999999998 28.772099999999998 97.920000000000002 +28.772099999999998 39.601399999999998 97.920000000000002 +15.1264 46.554200000000002 97.920000000000002 +0 48.950000000000003 97.920000000000002 +-15.1264 46.554200000000002 97.920000000000002 +-28.772099999999998 39.601399999999998 97.920000000000002 +-43.510849999999998 14.137549999999999 97.680000000000007 +45.75 0 97.680000000000007 +43.510849999999998 -14.137549999999999 97.680000000000007 +-26.891200000000001 37.012549999999997 97.680000000000007 +-37.012549999999997 26.891200000000001 97.680000000000007 +43.510849999999998 14.137549999999999 97.680000000000007 +37.012549999999997 26.891200000000001 97.680000000000007 +-14.137549999999999 43.510849999999998 97.680000000000007 +26.891200000000001 37.012549999999997 97.680000000000007 +14.075699999999999 43.320599999999999 97.488 +0 45.75 97.680000000000007 +14.137549999999999 43.510849999999998 97.680000000000007 +46.459099999999999 -15.095499999999999 98.016000000000005 +48.850000000000001 0 98.016000000000005 +46.459099999999999 15.095499999999999 98.016000000000005 +39.520499999999998 28.7133 98.016000000000005 +28.7133 39.520499999999998 98.016000000000005 +15.095499999999999 46.459099999999999 98.016000000000005 +0 48.850000000000001 98.016000000000005 +-15.095499999999999 46.459099999999999 98.016000000000005 +-28.7133 39.520499999999998 98.016000000000005 +-39.520499999999998 28.7133 98.016000000000005 +-46.459099999999999 15.095499999999999 98.016000000000005 +-48.850000000000001 0 98.016000000000005 +-45.850000000000001 0 97.775999999999996 +-43.60595 14.16845 97.775999999999996 +45.850000000000001 0 97.775999999999996 +43.60595 -14.16845 97.775999999999996 +43.60595 14.16845 97.775999999999996 +-26.949950000000001 37.093449999999997 97.775999999999996 +-37.093449999999997 26.949950000000001 97.775999999999996 +37.093449999999997 26.949950000000001 97.775999999999996 +26.949950000000001 37.093449999999997 97.775999999999996 +-14.16845 43.60595 97.775999999999996 +14.16845 43.60595 97.775999999999996 +0 45.850000000000001 97.775999999999996 +46.363999999999997 -15.0646 98.063999999999993 +48.75 0 98.063999999999993 +46.363999999999997 15.0646 98.063999999999993 +39.439599999999999 28.65455 98.063999999999993 +28.65455 39.439599999999999 98.063999999999993 +15.0646 46.363999999999997 98.063999999999993 +0 48.75 98.063999999999993 +-15.0646 46.363999999999997 98.063999999999993 +-28.65455 39.439599999999999 98.063999999999993 +-39.439599999999999 28.65455 98.063999999999993 +-46.363999999999997 15.0646 98.063999999999993 +-48.75 0 98.063999999999993 +-45.950000000000003 0 97.872 +45.950000000000003 0 97.872 +43.701050000000002 -14.199350000000001 97.872 +-43.701050000000002 14.199350000000001 97.872 +43.701050000000002 14.199350000000001 97.872 +37.174349999999997 27.008749999999999 97.872 +-27.008749999999999 37.174349999999997 97.872 +-37.174349999999997 27.008749999999999 97.872 +27.008749999999999 37.174349999999997 97.872 +14.199350000000001 43.701050000000002 97.872 +-14.199350000000001 43.701050000000002 97.872 +0 45.950000000000003 97.872 +46.268900000000002 -15.0337 98.159999999999997 +48.649999999999999 0 98.159999999999997 +46.268900000000002 15.0337 98.159999999999997 +39.358699999999999 28.595749999999999 98.159999999999997 +28.595749999999999 39.358699999999999 98.159999999999997 +15.0337 46.268900000000002 98.159999999999997 +0 48.649999999999999 98.159999999999997 +-15.0337 46.268900000000002 98.159999999999997 +-28.595749999999999 39.358699999999999 98.159999999999997 +-39.358699999999999 28.595749999999999 98.159999999999997 +-46.268900000000002 15.0337 98.159999999999997 +-48.649999999999999 0 98.159999999999997 +-46.049999999999997 0 97.920000000000002 +43.796149999999997 -14.23025 97.920000000000002 +14.23025 43.796149999999997 97.920000000000002 +0 46.049999999999997 97.920000000000002 +46.126249999999999 -14.987299999999999 98.207999999999998 +48.5 0 98.207999999999998 +28.5076 39.237299999999998 98.207999999999998 +-48.5 0 98.207999999999998 +14.5547 44.794750000000001 98.352000000000004 +0 47.100000000000001 98.352000000000004 +-14.5547 44.794750000000001 98.352000000000004 +-27.684699999999999 38.104700000000001 98.352000000000004 +-38.104700000000001 27.684699999999999 98.352000000000004 +-44.794750000000001 14.5547 98.352000000000004 +-47.100000000000001 0 98.352000000000004 +-45 0 95.760000000000005 +42.797550000000001 -13.905749999999999 95.760000000000005 +3 274 152 314 +3 37 193 199 +3 170 225 315 +3 316 317 285 +3 270 310 268 +3 309 312 271 +3 37 199 173 +3 170 194 231 +3 318 316 294 +3 319 311 310 +3 320 321 272 +3 232 173 322 +3 228 216 309 +3 323 318 301 +3 324 319 310 +3 272 321 273 +3 173 232 167 +3 228 309 226 +3 325 323 326 +3 327 319 324 +3 328 306 320 +3 329 308 330 +3 331 332 273 +3 333 334 335 +3 256 327 324 +3 320 306 321 +3 330 336 337 +3 245 266 243 +3 317 338 286 +3 339 340 337 +3 328 341 306 +3 193 4 257 +3 307 216 248 +3 337 340 330 +3 306 341 307 +3 193 257 184 +3 307 312 216 +3 342 343 344 +3 345 329 340 +3 307 341 313 +3 263 346 219 +3 347 348 349 +3 315 306 248 +3 340 329 330 +3 312 307 313 +3 350 308 329 +3 315 225 306 +3 351 333 335 +3 352 339 349 +3 353 354 243 +3 310 232 324 +3 309 273 332 +3 351 342 333 +3 349 339 337 +3 332 353 243 +3 347 337 355 +3 356 357 358 +3 359 250 242 +3 332 266 226 +3 335 334 360 +3 348 361 352 +3 331 362 353 +3 232 310 167 +3 309 332 226 +3 334 363 360 +3 349 348 352 +3 332 331 353 +3 355 337 336 +3 364 365 357 +3 366 359 242 +3 367 368 225 +3 360 363 369 +3 370 361 348 +3 371 244 354 +3 261 372 373 +3 321 306 374 +3 369 325 326 +3 350 370 348 +3 354 244 243 +3 336 4 260 +3 375 376 377 +3 378 366 242 +3 379 273 321 +3 326 323 301 +3 345 370 350 +3 380 374 371 +3 350 270 308 +3 376 381 377 +3 382 383 384 +3 306 225 374 +3 301 318 294 +3 329 345 350 +3 371 374 244 +3 257 4 330 +3 385 386 365 +3 387 378 242 +3 368 374 225 +3 294 316 285 +3 388 389 390 +3 380 391 374 +3 324 213 184 +3 364 385 365 +3 392 393 378 +3 332 243 266 +3 285 317 286 +3 390 389 336 +3 374 391 379 +3 336 260 390 +3 394 245 244 +3 286 338 395 +3 396 355 389 +3 379 391 362 +3 257 330 308 +3 367 225 224 +3 389 355 336 +3 331 379 362 +3 280 397 263 +3 348 310 350 +3 398 399 400 +3 387 392 378 +3 368 394 244 +3 363 325 369 +3 401 388 402 +3 403 404 224 +3 397 346 263 +3 270 350 310 +3 399 405 375 +3 406 407 392 +3 379 331 273 +3 402 388 390 +3 266 403 224 +3 402 408 409 +3 374 379 321 +3 409 410 401 +3 245 411 403 +3 310 348 167 +3 412 230 209 +3 402 409 401 +3 266 245 403 +3 347 349 337 +3 244 374 368 +3 413 410 409 +3 414 367 404 +3 256 184 257 +3 239 412 209 +3 347 413 409 +3 404 367 224 +3 415 416 279 +3 330 4 336 +3 312 309 216 +3 396 413 347 +3 417 368 414 +3 416 418 280 +3 324 184 256 +3 239 231 419 +3 355 396 347 +3 414 368 367 +3 279 416 280 +3 373 167 408 +3 239 420 412 +3 338 421 395 +3 422 423 261 +3 417 424 368 +3 418 397 280 +3 390 425 402 +3 387 406 392 +3 239 419 420 +3 426 333 342 +3 261 423 260 +3 368 424 394 +3 348 347 409 +3 258 234 164 +3 427 425 423 +3 394 424 411 +3 348 409 167 +3 163 170 258 +3 423 425 260 +3 245 394 411 +3 428 429 430 +3 425 390 260 +3 194 209 230 +3 431 422 373 +3 432 230 433 +3 430 429 434 +3 435 402 425 +3 436 437 206 +3 373 422 261 +3 433 230 412 +3 438 439 415 +3 372 4 275 +3 206 437 207 +3 408 440 431 +3 441 433 412 +3 439 416 415 +3 409 408 167 +3 442 226 443 +3 373 408 431 +3 420 441 412 +3 444 445 428 +3 446 373 372 +3 447 448 449 +3 344 426 342 +3 450 440 408 +3 451 419 432 +3 430 444 428 +3 261 4 372 +3 452 453 454 +3 455 456 267 +3 435 450 408 +3 432 419 230 +3 457 458 459 +3 408 402 435 +3 460 461 462 +3 267 463 464 +3 427 450 435 +3 419 451 441 +3 457 438 458 +3 465 275 167 +3 239 226 466 +3 456 463 267 +3 425 427 435 +3 420 419 441 +3 467 439 438 +3 373 446 167 +3 462 239 468 +3 469 470 455 +3 471 372 472 +3 473 13 206 +3 467 438 457 +3 446 465 167 +3 474 475 443 +3 476 469 455 +3 472 372 275 +3 473 206 32 +3 322 173 199 +3 462 468 460 +3 470 456 455 +3 477 472 465 +3 478 479 480 +3 213 324 232 +3 462 461 443 +3 481 469 476 +3 465 472 275 +3 480 479 449 +3 167 4 168 +3 482 483 484 +3 304 485 481 +3 486 477 465 +3 487 480 449 +3 168 4 488 +3 466 226 489 +3 485 469 481 +3 446 486 465 +3 461 487 449 +3 490 491 492 +3 4 3 488 +3 493 494 495 +3 496 485 304 +3 471 486 446 +3 497 466 478 +3 445 490 492 +3 488 3 498 +3 449 479 447 +3 499 192 208 +3 168 500 305 +3 497 501 466 +3 444 502 445 +3 503 504 40 +3 461 448 443 +3 499 496 192 +3 305 500 505 +3 466 501 468 +3 506 459 491 +3 40 504 15 +3 461 449 448 +3 507 208 227 +3 488 508 168 +3 468 501 509 +3 490 506 491 +3 510 30 504 +3 511 493 512 +3 513 507 227 +3 168 508 500 +3 460 468 509 +3 504 30 15 +3 489 514 515 +3 507 499 208 +3 498 516 488 +3 460 509 487 +3 506 457 459 +3 517 503 24 +3 518 519 520 +3 393 382 384 +3 489 226 514 +3 513 227 241 +3 488 516 508 +3 461 460 487 +3 24 503 40 +3 519 521 520 +3 384 378 393 +3 522 515 523 +3 26 524 517 +3 523 515 525 +3 24 26 517 +3 484 442 443 +3 526 527 528 +3 529 530 531 +3 532 523 525 +3 533 524 26 +3 448 534 443 +3 528 535 526 +3 533 536 537 +3 448 532 525 +3 45 533 26 +3 538 539 540 +3 541 242 237 +3 534 452 474 +3 542 504 1396 +3 543 489 522 +3 510 533 45 +3 538 544 539 +3 545 546 237 +3 466 468 239 +3 528 527 547 +3 548 549 550 +3 522 489 515 +3 30 510 45 +3 544 551 539 +3 552 242 553 +3 483 495 226 +3 554 555 524 +3 543 556 489 +3 557 558 88 +3 544 559 551 +3 560 545 561 +3 514 226 494 +3 537 530 524 +3 489 556 562 +3 88 558 41 +3 551 559 563 +3 387 242 552 +3 454 474 452 +3 564 558 503 +3 562 556 565 +3 564 39 558 +3 559 566 563 +3 552 567 568 +3 475 482 443 +3 542 510 504 +3 447 562 565 +3 172 569 165 +3 558 39 41 +3 534 474 443 +3 536 570 537 +3 447 565 532 +3 571 569 172 +3 572 557 107 +3 566 573 574 +3 575 576 577 +3 494 578 453 +3 504 558 1396 +3 448 447 532 +3 172 186 571 +3 107 557 88 +3 579 566 574 +3 580 567 560 +3 581 484 483 +3 504 503 558 +3 582 453 583 +3 186 584 571 +3 72 554 572 +3 573 585 574 +3 586 545 237 +3 583 453 452 +3 186 201 584 +3 107 72 572 +3 574 585 587 +3 588 561 545 +3 589 590 515 +3 591 592 572 +3 593 583 452 +3 201 594 584 +3 555 554 72 +3 376 595 381 +3 596 597 387 +3 494 226 495 +3 555 503 517 +3 534 593 452 +3 210 598 594 +3 25 555 72 +3 568 577 596 +3 514 589 515 +3 524 530 554 +3 599 514 582 +3 201 210 594 +3 564 555 25 +3 387 552 568 +3 600 226 442 +3 524 533 537 +3 582 514 453 +3 210 236 598 +3 39 564 25 +3 601 602 586 +3 483 226 600 +3 558 557 603 +3 599 604 514 +3 236 605 598 +3 606 603 80 +3 607 608 609 +3 483 600 581 +3 558 603 1396 +3 514 604 589 +3 236 249 605 +3 80 603 95 +3 610 611 607 +3 495 612 493 +3 613 614 606 +3 589 604 615 +3 249 616 605 +3 617 89 603 +3 586 588 545 +3 590 589 615 +3 249 618 616 +3 603 89 95 +3 608 619 588 +3 489 562 466 +3 211 104 120 +3 552 838 839 +3 803 829 801 +3 129 217 136 +3 553 546 840 +3 801 827 795 +3 211 145 217 +3 838 553 840 +3 795 826 794 +3 145 136 217 +3 546 545 841 +3 794 824 792 +3 108 221 129 +3 840 546 841 +3 792 825 790 +3 217 129 742 +3 841 545 560 +3 790 832 788 +3 842 252 293 +3 843 841 560 +3 252 842 251 +3 560 567 843 +3 836 829 803 +3 293 844 283 +3 843 567 845 +3 120 104 187 +3 567 552 845 +3 120 187 110 +3 845 552 839 +3 221 108 238 +3 580 846 568 +3 110 238 108 +3 568 846 847 +3 283 848 281 +3 580 561 849 +3 254 293 252 +3 846 580 849 +3 129 203 742 +3 561 588 850 +3 832 851 788 +3 837 176 254 +3 849 561 850 +3 852 830 781 +3 187 104 254 +3 850 588 619 +3 238 110 187 +3 853 850 619 +3 277 174 837 +3 619 577 853 +3 281 854 855 +3 853 577 856 +3 174 277 203 +3 577 568 856 +3 203 276 742 +3 856 568 847 +3 857 251 842 +3 576 858 596 +3 833 852 781 +3 254 104 293 +3 596 858 859 +3 749 860 755 +3 187 254 176 +3 576 575 861 +3 276 857 855 +3 858 576 861 +3 276 855 742 +3 575 608 862 +3 844 104 863 +3 861 575 862 +3 857 276 251 +3 862 608 607 +3 864 742 854 +3 865 862 607 +3 854 281 866 +3 607 611 865 +3 866 281 848 +3 865 611 867 +3 855 854 742 +3 611 596 867 +3 835 742 868 +3 867 596 859 +3 863 104 835 +3 622 869 597 +3 870 863 871 +3 597 869 872 +3 863 835 871 +3 622 610 873 +3 868 742 870 +3 869 622 873 +3 864 863 870 +3 610 609 874 +3 864 870 742 +3 873 610 874 +3 875 791 834 +3 145 211 120 +3 874 609 625 +3 876 875 834 +3 293 104 844 +3 877 874 625 +3 806 808 800 +3 276 203 277 +3 625 624 877 +3 799 878 879 +3 726 733 880 +3 848 283 844 +3 877 624 881 +3 800 878 797 +3 880 733 882 +3 203 129 221 +3 883 737 726 +3 353 105 403 +3 880 883 726 +3 414 105 104 +3 884 878 800 +3 885 820 737 +3 202 105 886 +3 883 885 737 +3 171 887 169 +3 626 648 888 +3 889 745 820 +3 202 886 200 +3 890 626 888 +3 885 889 820 +3 451 211 202 +3 648 602 891 +3 892 741 745 +3 235 162 214 +3 888 648 891 +3 889 892 745 +3 451 185 211 +3 891 602 601 +3 810 800 808 +3 893 758 741 +3 211 169 104 +3 894 891 601 +3 884 800 810 +3 892 893 741 +3 887 162 235 +3 601 666 894 +3 823 895 749 +3 886 105 214 +3 894 666 896 +3 895 860 749 +3 886 214 162 +3 893 897 758 +3 391 328 320 +3 758 897 821 +3 271 105 353 +3 541 632 898 +3 810 812 819 +3 821 899 822 +3 169 900 104 +3 901 541 898 +3 897 899 821 +3 411 354 403 +3 678 541 902 +3 884 810 712 +3 899 903 823 +3 391 272 362 +3 902 541 901 +3 822 899 823 +3 341 215 313 +3 898 632 686 +3 904 712 905 +3 903 895 823 +3 214 105 271 +3 906 898 686 +3 808 806 809 +3 887 171 162 +3 686 678 906 +3 362 272 353 +3 906 678 902 +3 341 247 215 +3 690 907 834 +3 328 900 341 +3 690 742 907 +3 247 341 900 +3 689 774 908 +3 900 328 104 +3 909 689 908 +3 712 722 905 +3 424 371 411 +3 698 9 774 +3 884 712 904 +3 353 403 354 +3 774 9 908 +3 214 271 215 +3 697 910 698 +3 391 320 272 +3 698 910 9 +3 271 353 272 +3 697 703 910 +3 712 810 819 +3 417 371 424 +3 910 703 911 +3 328 391 380 +3 911 703 700 +3 328 380 104 +3 912 911 700 +3 414 104 417 +3 912 700 689 +3 403 105 404 +3 909 912 689 +3 371 417 380 +3 696 779 913 +3 417 104 380 +3 914 696 913 +3 832 915 851 +3 354 411 371 +3 784 916 779 +3 917 915 832 +3 404 105 414 +3 779 916 913 +3 832 825 917 +3 433 200 432 +3 775 918 784 +3 825 919 917 +3 441 200 433 +3 784 918 916 +3 797 878 799 +3 825 824 919 +3 202 200 441 +3 775 699 918 +3 824 920 919 +3 432 185 451 +3 918 699 10 +3 826 921 920 +3 900 169 887 +3 10 699 776 +3 807 809 806 +3 824 826 920 +3 169 211 185 +3 8 10 776 +3 834 907 922 +3 826 827 921 +3 313 215 271 +3 8 776 696 +3 876 834 922 +3 827 923 921 +3 451 202 441 +3 914 8 696 +3 901 924 878 +3 827 829 923 +3 185 432 200 +3 773 706 925 +3 902 838 906 +3 829 926 923 +3 685 473 105 +3 927 773 925 +3 839 838 878 +3 829 836 926 +3 523 615 522 +3 705 928 706 +3 873 862 865 +3 926 836 929 +3 195 473 487 +3 706 928 925 +3 930 924 841 +3 836 831 929 +3 497 202 501 +3 786 931 705 +3 888 877 890 +3 929 831 932 +3 195 509 202 +3 705 931 928 +3 906 924 898 +3 831 830 933 +3 509 501 202 +3 786 785 931 +3 849 845 846 +3 932 831 933 +3 478 556 497 +3 931 785 934 +3 861 856 858 +3 830 852 933 +3 202 497 105 +3 934 785 778 +3 846 845 847 +3 933 852 935 +3 642 583 631 +3 936 934 778 +3 884 839 878 +3 852 833 935 +3 583 642 582 +3 936 778 773 +3 847 859 856 +3 935 833 937 +3 631 662 628 +3 927 936 773 +3 849 843 845 +3 895 938 860 +3 487 473 532 +3 702 782 939 +3 487 532 480 +3 940 702 939 +3 930 841 850 +3 556 478 565 +3 693 941 782 +3 869 867 872 +3 480 565 478 +3 782 941 939 +3 884 847 839 +3 628 677 627 +3 692 942 693 +3 839 847 845 +3 593 631 583 +3 693 942 941 +3 861 853 856 +3 497 543 105 +3 692 777 942 +3 850 853 861 +3 615 523 593 +3 942 777 943 +3 930 850 862 +3 532 473 593 +3 943 777 707 +3 894 930 891 +3 565 480 532 +3 944 943 707 +3 604 522 615 +3 944 707 702 +3 627 665 633 +3 940 944 702 +3 884 859 847 +3 522 604 543 +3 701 772 945 +3 874 862 873 +3 543 599 105 +3 946 701 945 +3 930 862 874 +3 641 582 642 +3 802 947 772 +3 891 877 888 +3 593 473 631 +3 772 947 945 +3 862 850 861 +3 532 593 523 +3 694 948 802 +3 859 872 867 +3 599 641 633 +3 802 948 947 +3 858 856 859 +3 599 633 105 +3 694 793 948 +3 884 872 859 +3 880 882 92 +3 662 473 656 +3 948 793 949 +3 877 891 874 +3 92 882 84 +3 641 599 582 +3 949 793 783 +3 930 874 891 +3 91 883 880 +3 655 105 665 +3 950 949 783 +3 881 890 877 +3 92 91 880 +3 665 627 951 +3 950 783 701 +3 102 885 883 +3 951 627 677 +3 946 950 701 +3 930 894 1397 +3 91 102 883 +3 633 665 105 +3 798 952 796 +3 894 896 1397 +3 112 889 885 +3 685 105 688 +3 796 952 953 +3 873 865 867 +3 102 112 885 +3 656 473 685 +3 954 804 796 +3 898 924 901 +3 122 892 889 +3 691 656 684 +3 953 954 796 +3 838 901 878 +3 112 122 889 +3 656 685 684 +3 798 805 952 +3 840 924 906 +3 133 893 892 +3 688 105 691 +3 952 805 955 +3 840 906 838 +3 122 133 892 +3 655 656 691 +3 955 805 804 +3 838 902 901 +3 133 144 893 +3 655 691 105 +3 954 955 804 +3 841 924 840 +3 893 144 897 +3 509 195 487 +3 742 19 907 +3 850 841 849 +3 903 155 895 +3 631 473 662 +3 17 19 104 +3 843 849 841 +3 155 938 895 +3 599 543 604 +3 104 19 742 +3 873 867 869 +3 897 147 899 +3 677 628 662 +3 811 816 75 +3 907 19 27 +3 144 147 897 +3 543 497 556 +3 42 811 75 +3 922 907 27 +3 147 153 903 +3 956 13 957 +3 49 814 811 +3 911 924 910 +3 899 147 903 +3 724 13 473 +3 42 49 811 +3 909 61 62 +3 153 155 903 +3 35 13 708 +3 818 96 816 +3 954 61 945 +3 958 959 960 +3 816 96 75 +3 945 61 946 +3 35 708 710 +3 723 57 818 +3 10 910 918 +3 743 195 35 +3 818 57 96 +3 930 910 924 +3 961 962 932 +3 963 704 964 +3 723 828 57 +3 909 62 912 +3 743 965 195 +3 57 828 68 +3 924 911 62 +3 195 960 473 +3 68 828 814 +3 911 912 62 +3 935 937 966 +3 959 704 963 +3 49 68 814 +3 914 61 909 +3 708 13 964 +3 813 751 116 +3 946 61 940 +3 933 961 932 +3 708 964 704 +3 46 813 116 +3 947 954 945 +3 933 935 961 +3 967 968 969 +3 76 817 813 +3 946 939 950 +3 932 962 929 +3 970 13 956 +3 46 76 813 +3 944 928 943 +3 962 971 929 +3 960 972 473 +3 815 100 751 +3 931 916 918 +3 929 971 926 +3 740 973 957 +3 751 100 116 +3 930 918 910 +3 926 974 923 +3 967 975 976 +3 715 58 815 +3 913 927 914 +3 923 977 921 +3 978 979 980 +3 815 58 100 +3 914 908 8 +3 921 300 920 +3 964 13 970 +3 715 756 58 +3 9 8 908 +3 920 289 919 +3 959 958 704 +3 58 756 56 +3 927 61 914 +3 919 284 917 +3 976 975 956 +3 56 756 817 +3 908 914 909 +3 917 981 915 +3 978 982 979 +3 76 56 817 +3 953 61 954 +3 981 983 915 +3 968 972 978 +3 746 729 127 +3 939 946 940 +3 982 978 972 +3 82 746 127 +3 942 948 941 +3 972 968 473 +3 117 750 746 +3 940 925 944 +3 971 974 926 +3 735 984 740 +3 82 117 746 +3 936 916 934 +3 956 957 973 +3 759 142 729 +3 930 931 918 +3 974 977 923 +3 964 970 979 +3 729 142 127 +3 940 61 927 +3 967 969 975 +3 714 99 759 +3 927 913 936 +3 977 300 921 +3 970 956 975 +3 759 99 142 +3 949 941 948 +3 718 984 735 +3 714 713 99 +3 930 942 931 +3 300 289 920 +3 968 967 985 +3 99 713 986 +3 934 916 931 +3 968 985 473 +3 986 713 750 +3 950 941 949 +3 289 284 919 +3 724 473 718 +3 117 986 750 +3 954 947 955 +3 957 13 987 +3 727 738 131 +3 930 948 942 +3 284 981 917 +3 984 718 985 +3 50 727 131 +3 943 928 942 +3 718 473 985 +3 151 728 727 +3 931 942 928 +3 973 740 984 +3 50 151 727 +3 930 61 953 +3 987 13 724 +3 734 229 738 +3 930 953 952 +3 988 710 747 +3 738 229 131 +3 930 952 947 +3 966 989 935 +3 757 710 988 +3 764 143 734 +3 952 955 947 +3 35 710 757 +3 734 143 229 +3 948 930 947 +3 989 961 935 +3 747 965 743 +3 764 765 143 +3 925 940 927 +3 155 166 938 +3 972 960 959 +3 143 765 141 +3 910 10 9 +3 960 195 965 +3 141 765 728 +3 928 944 925 +3 980 979 970 +3 151 141 728 +3 941 950 939 +3 743 35 757 +3 6 31 7 +3 916 936 913 +3 965 747 710 +3 67 66 74 +3 5 131 16 +3 29 69 113 +3 98 73 74 +3 74 49 44 +3 92 84 83 +3 113 77 47 +3 108 130 109 +3 986 100 99 +3 121 132 122 +3 148 59 51 +3 176 175 188 +3 117 100 986 +3 147 146 153 +3 123 87 189 +3 303 254 253 +3 81 62 66 +3 97 34 48 +3 276 190 251 +3 62 81 44 +3 990 167 12 +3 190 277 991 +3 49 66 68 +3 992 993 994 +3 991 277 837 +3 76 75 96 +3 992 28 993 +3 197 991 837 +3 995 43 225 +3 981 395 983 +3 993 28 173 +3 197 837 254 +3 158 196 115 +3 286 395 981 +3 996 297 296 +3 303 197 254 +3 253 181 180 +3 981 284 286 +3 160 28 992 +3 855 191 281 +3 231 140 150 +3 289 300 294 +3 278 302 997 +3 281 191 282 +3 231 126 225 +3 300 977 301 +3 161 998 999 +3 855 857 191 +3 1000 1001 1002 +3 977 326 301 +3 1003 173 278 +3 191 857 179 +3 130 43 204 +3 977 974 326 +3 998 161 160 +3 282 191 1004 +3 974 369 326 +3 160 159 12 +3 1005 299 288 +3 974 971 369 +3 1006 264 1007 +3 115 109 158 +3 369 971 360 +3 1008 1009 1010 +3 126 196 225 +3 971 962 360 +3 1009 1008 1011 +3 126 115 196 +3 360 962 335 +3 302 167 295 +3 288 282 1005 +3 962 961 351 +3 999 997 302 +3 204 190 175 +3 335 962 351 +3 265 292 291 +3 204 43 190 +3 961 989 351 +3 992 994 1003 +3 197 303 188 +3 351 989 342 +3 1012 12 1013 +3 298 288 299 +3 989 966 342 +3 292 265 159 +3 1002 995 225 +3 342 966 343 +3 296 1010 1009 +3 196 303 225 +3 159 264 12 +3 303 253 298 +3 1011 1012 1013 +3 130 140 44 +3 296 167 1010 +3 1001 1004 43 +3 177 146 476 +3 297 996 1007 +3 190 43 191 +3 1014 1011 1008 +3 180 298 253 +3 264 1006 1013 +3 299 1000 225 +3 267 464 166 +3 264 1013 12 +3 303 298 225 +3 1015 12 1012 +3 191 179 181 +3 455 177 476 +3 1015 990 12 +3 1016 1002 1001 +3 455 267 177 +3 1006 1007 996 +3 1017 1005 282 +3 476 146 481 +3 1012 1011 1014 +3 991 197 175 +3 146 137 481 +3 1010 167 990 +3 191 43 1004 +3 481 137 304 +3 1018 1003 994 +3 190 991 175 +3 304 132 192 +3 1019 1003 1018 +3 1020 43 995 +3 192 121 208 +3 173 1003 1019 +3 1001 43 1020 +3 208 111 227 +3 993 173 1019 +3 1001 1020 1016 +3 227 101 241 +3 295 167 296 +3 1004 1017 282 +3 241 90 526 +3 302 295 290 +3 1000 1002 225 +3 526 83 527 +3 291 290 295 +3 204 157 130 +3 83 85 527 +3 997 999 998 +3 315 258 170 +3 90 83 526 +3 322 246 232 +3 231 225 170 +3 198 246 322 +3 234 228 164 +3 199 198 322 +3 239 209 240 +3 269 255 308 +3 230 419 231 +3 308 255 257 +3 240 228 226 +3 166 154 267 +3 327 256 255 +3 512 475 511 +3 496 304 192 +3 372 471 446 +3 1021 659 462 +3 621 591 640 +3 652 654 636 +3 661 606 621 +3 644 651 1022 +3 1023 670 338 +3 542 664 510 +3 645 443 643 +3 338 317 1023 +3 617 603 557 +3 645 1021 462 +3 317 675 1023 +3 542 537 570 +3 650 687 673 +3 317 316 675 +3 542 676 664 +3 761 1024 1025 +3 542 721 720 +3 1026 1027 1028 +3 763 754 1029 +3 762 1030 1026 +3 721 754 732 +3 1025 1024 1031 +3 721 1396 754 +3 1032 762 761 +3 754 753 732 +3 1024 1033 1034 +3 754 1396 1029 +3 1025 1030 1035 +3 720 739 769 +3 1034 1036 766 +3 744 732 753 +3 179 857 842 +3 180 179 842 +3 180 842 293 +3 298 180 293 +3 864 1001 863 +3 863 1001 1000 +3 844 863 1000 +3 299 844 1000 +3 854 1004 864 +3 864 1004 1001 +3 854 866 1004 +3 1004 866 1017 +3 1017 866 848 +3 1005 1017 848 +3 1005 848 844 +3 299 1005 844 +3 868 1020 835 +3 835 1020 995 +3 1002 871 835 +3 995 1002 835 +3 868 870 1020 +3 1020 870 1016 +3 1016 870 871 +3 1002 1016 871 +3 105 32 17 +3 105 473 32 +3 886 162 164 +3 240 886 164 +3 209 200 886 +3 240 209 886 +3 171 163 162 +3 215 248 216 +3 900 315 247 +3 247 315 248 +3 900 887 315 +3 315 887 258 +3 258 887 235 +3 478 466 479 +3 633 641 494 +3 494 641 578 +3 665 951 495 +3 495 951 612 +3 612 951 677 +3 512 612 677 +3 1022 651 958 +3 958 651 704 +3 645 1022 960 +3 960 1022 958 +3 645 960 1021 +3 1021 960 965 +3 653 1021 965 +3 710 653 965 +3 979 687 650 +3 964 979 650 +3 650 649 963 +3 964 650 963 +3 669 687 982 +3 982 687 979 +3 643 669 972 +3 972 669 982 +3 643 972 644 +3 644 972 959 +3 649 644 959 +3 963 649 959 +3 975 681 673 +3 970 975 673 +3 673 1037 980 +3 970 673 980 +3 1038 681 969 +3 969 681 975 +3 667 1038 968 +3 968 1038 969 +3 667 968 668 +3 668 968 978 +3 1037 668 978 +3 980 1037 978 +3 973 637 680 +3 956 973 680 +3 680 679 976 +3 956 680 976 +3 638 637 984 +3 984 637 973 +3 1039 638 985 +3 985 638 984 +3 1039 985 1040 +3 1040 985 967 +3 679 1040 967 +3 976 679 967 +3 987 635 663 +3 957 987 663 +3 663 639 740 +3 957 663 740 +3 717 635 724 +3 724 635 987 +3 659 1041 747 +3 747 1041 988 +3 988 1041 752 +3 757 988 752 +3 47 771 113 +3 113 771 1042 +3 29 113 1042 +3 1043 770 34 +3 34 770 47 +3 1043 34 1044 +3 1044 34 36 +3 1045 1044 36 +3 38 1045 36 +3 1042 1045 38 +3 29 1042 38 +3 1046 1047 22 +3 22 1047 20 +3 20 1047 1048 +3 69 20 1048 +3 1049 1046 97 +3 97 1046 22 +3 1049 97 1050 +3 1050 97 48 +3 1051 1050 48 +3 77 1051 48 +3 1048 1051 77 +3 69 1048 77 +3 1052 1053 59 +3 59 1053 52 +3 52 1053 1054 +3 93 52 1054 +3 1055 1052 134 +3 134 1052 59 +3 1055 134 1056 +3 1056 134 118 +3 1057 1056 118 +3 21 1057 118 +3 1054 1057 21 +3 93 1054 21 +3 1058 1059 87 +3 87 1059 64 +3 64 1059 1060 +3 53 64 1060 +3 1061 1058 124 +3 124 1058 87 +3 1061 124 1062 +3 1062 124 148 +3 1063 1062 148 +3 51 1063 148 +3 1060 1063 51 +3 53 1060 51 +3 1064 1065 178 +3 178 1065 156 +3 156 1065 1066 +3 63 156 1066 +3 1067 1064 123 +3 123 1064 178 +3 1067 123 1068 +3 1068 123 189 +3 1069 1068 189 +3 86 1069 189 +3 1066 1069 86 +3 63 1066 86 +3 1070 1071 223 +3 11 1070 223 +3 1072 1070 205 +3 205 1070 11 +3 1072 205 1073 +3 1073 205 222 +3 1071 1073 222 +3 223 1071 222 +3 1074 1033 998 +3 998 1033 997 +3 997 1033 1024 +3 278 997 1024 +3 1028 1074 160 +3 160 1074 998 +3 1028 160 760 +3 760 160 992 +3 761 760 992 +3 1003 761 992 +3 1024 761 1003 +3 278 1024 1003 +3 1075 1036 292 +3 292 1036 290 +3 290 1036 1034 +3 302 290 1034 +3 1076 1075 159 +3 159 1075 292 +3 1076 159 1077 +3 1077 159 161 +3 1078 1077 161 +3 999 1078 161 +3 1034 1078 999 +3 302 1034 999 +3 1079 767 1007 +3 1007 767 297 +3 297 767 766 +3 295 297 766 +3 1080 1079 264 +3 264 1079 1007 +3 1080 264 1081 +3 1081 264 265 +3 1082 1081 265 +3 291 1082 265 +3 766 1082 291 +3 295 766 291 +3 1083 1084 1011 +3 1011 1084 1009 +3 1009 1084 768 +3 296 1009 768 +3 1085 1083 1013 +3 1013 1083 1011 +3 1085 1013 1086 +3 1086 1013 1006 +3 1087 1086 1006 +3 996 1087 1006 +3 768 1087 996 +3 296 768 996 +3 1088 1089 1015 +3 1015 1089 990 +3 990 1089 1090 +3 1010 990 1090 +3 1091 1088 1012 +3 1012 1088 1015 +3 1091 1012 1092 +3 1092 1012 1014 +3 1093 1092 1014 +3 1008 1093 1014 +3 1090 1093 1008 +3 1010 1090 1008 +3 762 1032 994 +3 994 1032 1018 +3 1018 1032 1035 +3 1019 1018 1035 +3 1030 762 993 +3 993 762 994 +3 1035 1030 993 +3 1019 1035 993 +3 305 505 436 +3 436 505 437 +3 472 1031 530 +3 537 183 530 +3 427 423 388 +3 246 198 1025 +3 1025 1031 233 +3 422 471 530 +3 537 198 183 +3 537 1025 198 +3 183 255 530 +3 183 182 255 +3 423 422 530 +3 370 345 269 +3 319 182 212 +3 233 1031 311 +3 327 255 182 +3 255 340 530 +3 396 389 339 +3 370 269 268 +3 311 361 268 +3 311 1031 361 +3 410 413 352 +3 340 339 389 +3 340 389 530 +3 389 423 530 +3 345 340 269 +3 233 246 1025 +3 413 396 339 +3 361 1031 410 +3 401 410 440 +3 389 388 423 +3 422 431 471 +3 413 339 352 +3 486 471 431 +3 440 450 401 +3 361 370 268 +3 410 1031 440 +3 450 427 388 +3 352 361 410 +3 477 1031 472 +3 431 1031 477 +3 431 477 486 +3 431 440 1031 +3 450 388 401 +3 311 319 233 +3 319 327 182 +3 212 233 319 +3 255 269 340 +3 471 472 530 +3 437 505 1094 +3 1094 505 1095 +3 500 1096 505 +3 505 1096 1095 +3 500 508 1096 +3 1096 508 1097 +3 508 516 1097 +3 1097 516 1098 +3 537 542 769 +3 1099 537 1100 +3 1100 537 769 +3 1101 537 1099 +3 1102 537 1101 +3 1103 537 1102 +3 1104 537 1103 +3 879 537 1104 +3 878 537 879 +3 924 537 878 +3 62 537 924 +3 44 537 62 +3 231 537 44 +3 239 537 231 +3 462 537 239 +3 652 537 462 +3 1026 537 652 +3 1025 537 1026 +3 530 1105 1106 +3 531 530 1106 +3 1031 1107 1105 +3 530 1031 1105 +3 590 534 525 +3 474 511 475 +3 612 512 493 +3 482 484 443 +3 453 514 494 +3 525 515 590 +3 462 443 645 +3 668 1037 687 +3 643 667 669 +3 643 443 667 +3 673 681 680 +3 680 663 636 +3 673 680 636 +3 717 716 443 +3 1038 667 1039 +3 1040 681 1038 +3 667 443 1039 +3 716 1039 443 +3 680 637 663 +3 730 639 638 +3 717 443 635 +3 716 730 638 +3 1040 679 681 +3 1039 1040 1038 +3 1041 659 653 +3 638 1039 716 +3 652 1041 653 +3 1037 673 687 +3 652 462 660 +3 652 752 1041 +3 652 660 752 +3 644 649 651 +3 1022 645 644 +3 1021 653 659 +3 1070 636 1027 +3 1051 1048 771 +3 1053 1052 1063 +3 1045 1042 1026 +3 652 636 1043 +3 1026 652 1044 +3 1060 1066 1027 +3 1026 1044 1045 +3 1026 1042 1027 +3 1065 1064 1071 +3 1043 636 1049 +3 1058 1061 1067 +3 1069 1066 1059 +3 771 770 1051 +3 1042 1048 1027 +3 1042 771 1048 +3 1059 1058 1069 +3 1049 1055 1046 +3 1049 636 1055 +3 1057 1054 1047 +3 1060 1059 1066 +3 1071 1070 1027 +3 1048 1054 1027 +3 1054 1053 1060 +3 1043 1044 652 +3 1064 1067 636 +3 1055 636 1061 +3 1063 1060 1053 +3 1066 1065 1027 +3 1054 1060 1027 +3 1061 1062 1052 +3 1073 1071 1064 +3 1068 1069 1058 +3 1056 1057 1046 +3 1061 636 1067 +3 1055 1056 1046 +3 1072 636 1070 +3 1064 636 1072 +3 1064 1072 1073 +3 1067 1068 1058 +3 1065 1071 1027 +3 1049 1050 1043 +3 1050 1051 770 +3 770 1043 1050 +3 1048 1047 1054 +3 1062 1063 1052 +3 1052 1055 1061 +3 1047 1046 1057 +3 1089 1027 1031 +3 1076 1077 1028 +3 1087 768 767 +3 1081 1082 1075 +3 1028 1027 1076 +3 1028 760 1026 +3 1083 1085 1091 +3 1033 1074 1078 +3 1024 1034 1031 +3 1076 1080 1075 +3 1076 1027 1080 +3 1082 766 1036 +3 768 1090 1031 +3 1034 766 1031 +3 768 1084 1090 +3 1080 1081 1075 +3 1080 1027 1085 +3 1090 1089 1031 +3 1088 1091 1027 +3 767 1079 1087 +3 766 768 1031 +3 1078 1034 1033 +3 1091 1092 1083 +3 1088 1027 1089 +3 1093 1090 1084 +3 1092 1093 1083 +3 1086 1087 1079 +3 1085 1086 1079 +3 1091 1085 1027 +3 1084 1083 1093 +3 1025 1032 761 +3 1025 1026 1030 +3 1025 1035 1032 +3 760 762 1026 +3 1077 1078 1074 +3 1074 1028 1077 +3 1079 1080 1085 +3 1036 1075 1082 +3 207 437 1108 +3 1108 437 1094 +3 1027 1109 1107 +3 1031 1027 1107 +3 1094 1110 1108 +3 1108 1110 1111 +3 1095 1112 1094 +3 1094 1112 1110 +3 1096 1113 1095 +3 1095 1113 1112 +3 1097 1114 1096 +3 1096 1114 1113 +3 1098 1115 1097 +3 1097 1115 1114 +3 1105 1116 1106 +3 1107 1117 1105 +3 1117 1116 1105 +3 1118 1119 1107 +3 1109 1118 1107 +3 1119 1117 1107 +3 876 1120 875 +3 1121 1120 876 +3 876 922 1121 +3 1122 930 1123 +3 1123 930 1397 +3 922 1124 1121 +3 922 27 1124 +3 1122 1125 930 +3 930 1125 61 +3 27 1126 1124 +3 1127 1126 18 +3 18 1126 27 +3 1125 1128 61 +3 61 1128 43 +3 18 33 1127 +3 33 1129 1127 +3 33 207 1129 +3 43 1128 1130 +3 225 43 1130 +3 225 1130 1131 +3 226 225 1131 +3 207 1108 1129 +3 226 1131 1132 +3 443 226 1132 +3 443 1132 1133 +3 636 443 1133 +3 636 1133 1109 +3 1027 636 1109 +3 1108 1111 1129 +3 1129 1111 1134 +3 1133 1118 1109 +3 1134 1111 1135 +3 1111 1136 1135 +3 1111 1110 1136 +3 1136 1110 1137 +3 1110 1112 1137 +3 1137 1112 1138 +3 1113 1139 1112 +3 1112 1139 1138 +3 1113 1114 1139 +3 1139 1114 1140 +3 1114 1115 1140 +3 1140 1115 1141 +3 1142 1116 1117 +3 1143 1142 1117 +3 1143 1117 1119 +3 1144 1143 1119 +3 1145 1118 1146 +3 1147 1145 1146 +3 1146 1118 1133 +3 1144 1119 1118 +3 1145 1144 1118 +3 1148 1120 1121 +3 1149 1148 1121 +3 1122 1123 1150 +3 1150 1123 1151 +3 1121 1124 1152 +3 1149 1121 1152 +3 1153 1125 1122 +3 1150 1153 1122 +3 1124 1126 1154 +3 1152 1124 1154 +3 1155 1128 1125 +3 1153 1155 1125 +3 1127 1156 1126 +3 1126 1156 1154 +3 1129 1134 1127 +3 1127 1134 1156 +3 1157 1130 1128 +3 1155 1157 1128 +3 1158 1131 1130 +3 1157 1158 1130 +3 1159 1132 1131 +3 1158 1159 1131 +3 1159 1146 1132 +3 1132 1146 1133 +3 1156 1134 1160 +3 1134 1135 1160 +3 1160 1135 1161 +3 1135 1162 1161 +3 1135 1136 1162 +3 1136 1163 1162 +3 1136 1137 1163 +3 1163 1137 1164 +3 1137 1138 1164 +3 1164 1138 1165 +3 1139 1166 1138 +3 1138 1166 1165 +3 1139 1140 1166 +3 1166 1140 1167 +3 1140 1141 1167 +3 1167 1141 1168 +3 1143 1169 1142 +3 1144 1170 1143 +3 1170 1169 1143 +3 1171 1172 1144 +3 1145 1171 1144 +3 1172 1170 1144 +3 1147 1171 1145 +3 1173 1174 1147 +3 1147 1146 1159 +3 1173 1147 1159 +3 1174 1171 1147 +3 1149 1175 1148 +3 1176 1175 1149 +3 1149 1152 1176 +3 1151 1177 1150 +3 1150 1177 1178 +3 1153 1150 1179 +3 1179 1150 1178 +3 1152 1180 1176 +3 1152 1154 1180 +3 1155 1153 1181 +3 1181 1153 1179 +3 1154 1182 1180 +3 1160 1182 1156 +3 1156 1182 1154 +3 1183 1157 1155 +3 1181 1183 1155 +3 1184 1158 1157 +3 1183 1184 1157 +3 1173 1159 1158 +3 1184 1173 1158 +3 1161 1185 1160 +3 1160 1185 1182 +3 1186 1187 1161 +3 1161 1187 1185 +3 1161 1162 1186 +3 1162 1188 1186 +3 1162 1163 1188 +3 1163 1189 1188 +3 1163 1164 1189 +3 1189 1164 1190 +3 1164 1165 1190 +3 1190 1165 1191 +3 1166 1192 1165 +3 1165 1192 1191 +3 1166 1167 1192 +3 1192 1167 1193 +3 1167 1168 1193 +3 1193 1168 1194 +3 1195 1169 1170 +3 1196 1195 1170 +3 1196 1170 1172 +3 1197 1196 1172 +3 1198 1171 1174 +3 1199 1198 1174 +3 1197 1172 1171 +3 1198 1197 1171 +3 1200 1174 1173 +3 1200 1173 1184 +3 1199 1174 1200 +3 1201 1199 1200 +3 1176 1202 1175 +3 1178 1177 1203 +3 1203 1177 1204 +3 1205 1202 1176 +3 1176 1180 1205 +3 1206 1179 1178 +3 1203 1206 1178 +3 1207 1181 1179 +3 1206 1207 1179 +3 1180 1208 1205 +3 1180 1182 1208 +3 1209 1183 1181 +3 1207 1209 1181 +3 1182 1185 1208 +3 1210 1184 1183 +3 1209 1210 1183 +3 1210 1200 1184 +3 1208 1185 1211 +3 1185 1187 1211 +3 1211 1187 1212 +3 1187 1213 1212 +3 1186 1214 1213 +3 1187 1186 1213 +3 1186 1188 1214 +3 1188 1215 1214 +3 1188 1189 1215 +3 1189 1216 1215 +3 1189 1190 1216 +3 1216 1190 1217 +3 1190 1191 1217 +3 1217 1191 1218 +3 1191 1192 1219 +3 1218 1191 1219 +3 1192 1193 1219 +3 1219 1193 1220 +3 1193 1194 1220 +3 1220 1194 1221 +3 1196 1222 1195 +3 1197 1223 1196 +3 1223 1222 1196 +3 1224 1225 1197 +3 1198 1224 1197 +3 1225 1223 1197 +3 1199 1224 1198 +3 1201 1226 1199 +3 1226 1224 1199 +3 1201 1200 1210 +3 1227 1201 1210 +3 1228 1226 1201 +3 1228 1201 1227 +3 1204 1229 1203 +3 1203 1229 1230 +3 1205 1231 1202 +3 1206 1203 1232 +3 1232 1203 1230 +3 1233 1231 1205 +3 1205 1208 1233 +3 1207 1206 1234 +3 1234 1206 1232 +3 1235 1209 1207 +3 1234 1235 1207 +3 1208 1211 1233 +3 1227 1210 1209 +3 1235 1227 1209 +3 1233 1211 1236 +3 1211 1212 1236 +3 1236 1212 1237 +3 1212 1238 1237 +3 1212 1213 1238 +3 1213 1239 1238 +3 1240 1239 1214 +3 1214 1239 1213 +3 1214 1215 1240 +3 1215 1241 1240 +3 1215 1216 1241 +3 1216 1242 1241 +3 1216 1217 1242 +3 1242 1217 1243 +3 1217 1218 1243 +3 1243 1218 1244 +3 1219 1245 1218 +3 1218 1245 1244 +3 1219 1220 1245 +3 1245 1220 1246 +3 1220 1221 1246 +3 1246 1221 1247 +3 1223 1248 1222 +3 1225 1249 1223 +3 1249 1248 1223 +3 1226 1250 1224 +3 1250 1251 1225 +3 1224 1250 1225 +3 1251 1249 1225 +3 1228 1252 1226 +3 1252 1250 1226 +3 1253 1227 1235 +3 1253 1228 1227 +3 1254 1252 1228 +3 1254 1228 1253 +3 1230 1229 1255 +3 1255 1229 1256 +3 1257 1232 1230 +3 1255 1257 1230 +3 1233 1258 1231 +3 1259 1234 1232 +3 1257 1259 1232 +3 1236 1258 1233 +3 1260 1235 1234 +3 1259 1260 1234 +3 1260 1253 1235 +3 1236 1261 1258 +3 1237 1261 1236 +3 1237 1262 1261 +3 1263 1262 1237 +3 1237 1238 1263 +3 1238 1264 1263 +3 1238 1239 1264 +3 1239 1265 1264 +3 1240 1266 1265 +3 1239 1240 1265 +3 1240 1241 1266 +3 1241 1267 1266 +3 1241 1242 1267 +3 1242 1268 1267 +3 1242 1243 1268 +3 1268 1243 1269 +3 1243 1244 1269 +3 1269 1244 1270 +3 1244 1245 1271 +3 1270 1244 1271 +3 1245 1246 1271 +3 1271 1246 1272 +3 1246 1247 1272 +3 1272 1247 1273 +3 1249 1274 1248 +3 1251 1275 1249 +3 1275 1274 1249 +3 1252 1276 1250 +3 1276 1277 1251 +3 1250 1276 1251 +3 1277 1275 1251 +3 1254 1278 1252 +3 1278 1276 1252 +3 1279 1253 1260 +3 1279 1254 1253 +3 1280 1278 1254 +3 1280 1254 1279 +3 1255 1256 1281 +3 1281 1256 1282 +3 1283 1257 1255 +3 1281 1283 1255 +3 2 1259 1257 +3 1283 2 1257 +3 1 1260 1259 +3 2 1 1259 +3 1 1279 1260 +3 1263 55 1262 +3 54 55 1263 +3 1263 1264 54 +3 1264 65 54 +3 1264 1265 65 +3 1265 70 65 +3 78 70 1266 +3 1266 70 1265 +3 1266 1267 78 +3 1267 94 78 +3 1267 1268 94 +3 1268 103 94 +3 1268 1269 103 +3 103 1269 114 +3 1269 1270 114 +3 114 1270 125 +3 1271 138 1270 +3 1270 138 125 +3 1271 1272 138 +3 138 1272 139 +3 1272 1273 139 +3 139 1273 149 +3 1275 220 1274 +3 1277 218 1275 +3 218 220 1275 +3 1278 287 1276 +3 287 262 1277 +3 1276 287 1277 +3 262 218 1277 +3 1280 1284 1278 +3 1284 287 1278 +3 23 1279 1 +3 23 1280 1279 +3 1285 1284 1280 +3 1285 1280 23 +3 1281 1282 1286 +3 1286 1282 1287 +3 1286 1287 1288 +3 1288 1287 1289 +3 1290 1283 1281 +3 1286 1290 1281 +3 14 2 1283 +3 1290 14 1283 +3 65 71 60 +3 70 79 71 +3 125 138 135 +3 1284 279 287 +3 1285 415 1284 +3 415 279 1284 +3 1288 1289 428 +3 428 1289 429 +3 458 23 0 +3 458 1285 23 +3 438 415 1285 +3 438 1285 458 +3 492 1290 1286 +3 1288 492 1286 +3 445 492 1288 +3 428 445 1288 +3 491 14 1290 +3 492 491 1290 +3 459 0 14 +3 491 459 14 +3 459 458 0 +3 106 119 249 +3 249 119 618 +3 119 128 618 +3 618 128 623 +3 128 135 259 +3 623 128 259 +3 502 490 445 +3 634 314 731 +3 711 695 418 +3 430 434 1291 +3 1291 434 1292 +3 1293 444 430 +3 1291 1293 430 +3 467 1294 439 +3 1294 709 439 +3 1295 502 444 +3 1293 1295 444 +3 1296 457 506 +3 1296 467 457 +3 1297 1294 467 +3 1297 467 1296 +3 1298 506 490 +3 502 1298 490 +3 1299 1298 502 +3 1295 1299 502 +3 1298 1296 506 +3 571 1300 569 +3 1301 1300 571 +3 571 584 1301 +3 584 1302 1301 +3 584 594 1302 +3 594 1303 1302 +3 598 1304 1303 +3 594 598 1303 +3 598 605 1304 +3 605 1305 1304 +3 605 616 1305 +3 616 1306 1305 +3 616 620 1306 +3 1306 620 1307 +3 620 629 1307 +3 1307 629 1308 +3 629 630 719 +3 1308 629 719 +3 711 1309 695 +3 1309 748 695 +3 1291 1292 1310 +3 1310 1292 1311 +3 1294 1312 709 +3 1312 1313 711 +3 709 1312 711 +3 1313 1309 711 +3 1314 1293 1291 +3 1310 1314 1291 +3 1315 1295 1293 +3 1314 1315 1293 +3 1297 1316 1294 +3 1316 1312 1294 +3 1317 1299 1295 +3 1315 1317 1295 +3 1318 1296 1298 +3 1318 1297 1296 +3 1319 1316 1297 +3 1319 1297 1318 +3 1299 1318 1298 +3 1320 1318 1299 +3 1317 1320 1299 +3 1301 1321 1300 +3 1322 1321 1301 +3 1301 1302 1322 +3 1302 1323 1322 +3 1302 1303 1323 +3 1303 1324 1323 +3 1304 1325 1324 +3 1303 1304 1324 +3 1304 1305 1325 +3 1305 1326 1325 +3 1305 1306 1326 +3 1306 1327 1326 +3 1306 1307 1327 +3 1327 1307 1328 +3 1307 1308 1328 +3 1328 1308 1329 +3 1308 719 1330 +3 1329 1308 1330 +3 719 725 1330 +3 1330 725 1331 +3 725 736 1331 +3 1331 736 1332 +3 1309 1333 748 +3 1313 1334 1309 +3 1334 1333 1309 +3 1310 1311 1335 +3 1335 1311 1336 +3 1337 1314 1310 +3 1335 1337 1310 +3 1316 1338 1312 +3 1338 1339 1313 +3 1312 1338 1313 +3 1339 1334 1313 +3 1340 1315 1314 +3 1337 1340 1314 +3 1341 1317 1315 +3 1340 1341 1315 +3 1319 1342 1316 +3 1342 1338 1316 +3 1343 1320 1317 +3 1341 1343 1317 +3 1320 1319 1318 +3 1344 1342 1319 +3 1344 1319 1320 +3 1343 1344 1320 +3 1322 1345 1321 +3 1346 1345 1322 +3 1322 1323 1346 +3 1323 1347 1346 +3 1323 1324 1347 +3 1324 1348 1347 +3 1325 1349 1348 +3 1324 1325 1348 +3 1325 1326 1349 +3 1326 1350 1349 +3 1326 1327 1350 +3 1327 1351 1350 +3 1327 1328 1351 +3 1351 1328 1352 +3 1328 1329 1352 +3 1352 1329 1353 +3 1329 1330 1354 +3 1353 1329 1354 +3 1330 1331 1354 +3 1354 1331 1355 +3 1331 1332 1355 +3 1355 1332 1356 +3 1334 1357 1333 +3 1335 1336 1358 +3 1358 1336 1359 +3 1339 1360 1334 +3 1360 1357 1334 +3 1361 1337 1335 +3 1358 1361 1335 +3 1362 1340 1337 +3 1361 1362 1337 +3 1342 1363 1338 +3 1363 1364 1339 +3 1338 1363 1339 +3 1364 1360 1339 +3 1365 1341 1340 +3 1362 1365 1340 +3 1366 1343 1341 +3 1365 1366 1341 +3 1344 1367 1342 +3 1367 1363 1342 +3 1368 1344 1343 +3 1366 1368 1343 +3 1368 1367 1344 +3 1346 1369 1345 +3 1370 1369 1346 +3 1346 1347 1370 +3 1347 1371 1370 +3 1347 1348 1371 +3 1348 1372 1371 +3 1349 1373 1372 +3 1348 1349 1372 +3 1349 1350 1373 +3 1350 1374 1373 +3 1350 1351 1374 +3 1351 1375 1374 +3 1351 1352 1375 +3 1375 1352 1376 +3 1352 1353 1376 +3 1376 1353 1377 +3 1353 1354 1378 +3 1377 1353 1378 +3 1354 1355 1378 +3 1378 1355 1379 +3 1355 1356 1379 +3 1379 1356 1380 +3 1360 1381 1357 +3 1358 1359 358 +3 358 1359 1382 +3 357 1361 1358 +3 358 357 1358 +3 1364 377 1360 +3 377 1381 1360 +3 365 1362 1361 +3 357 365 1361 +3 386 1365 1362 +3 365 386 1362 +3 1367 400 1363 +3 400 375 1364 +3 1363 400 1364 +3 375 377 1364 +3 1383 1366 1365 +3 386 1383 1365 +3 1384 1368 1366 +3 1383 1384 1366 +3 1368 398 1367 +3 398 400 1367 +3 1384 398 1368 +3 1370 1385 1369 +3 1386 1385 1370 +3 1370 1371 1386 +3 1371 518 1386 +3 1371 1372 518 +3 1372 519 518 +3 1373 1387 519 +3 1372 1373 519 +3 1373 1374 1387 +3 1374 538 1387 +3 1374 1375 538 +3 1375 544 538 +3 1375 1376 544 +3 544 1376 559 +3 1376 1377 559 +3 559 1377 566 +3 1377 1378 573 +3 566 1377 573 +3 1378 1379 573 +3 573 1379 585 +3 1379 1380 585 +3 585 1380 1388 +3 377 381 1381 +3 358 1382 658 +3 658 1382 647 +3 658 356 358 +3 356 364 357 +3 672 1383 386 +3 385 672 386 +3 400 399 375 +3 405 376 375 +3 671 1384 1383 +3 672 671 1383 +3 671 683 1384 +3 1384 683 398 +3 683 399 398 +3 585 1388 587 +3 587 1388 780 +3 1386 789 1385 +3 787 789 1386 +3 1386 518 787 +3 518 520 787 +3 1387 540 521 +3 519 1387 521 +3 1387 538 540 +3 563 566 579 +3 658 647 646 +3 657 356 658 +3 535 241 526 +3 535 513 241 +3 323 1389 682 +3 323 325 1389 +3 325 1390 1389 +3 325 363 1390 +3 1390 363 1391 +3 363 334 1391 +3 1391 334 1392 +3 334 333 1393 +3 1392 334 1393 +3 333 426 1393 +3 1393 426 1394 +3 426 344 1394 +3 1394 344 1395 +3 470 469 1392 +3 1394 1395 463 +3 1393 470 1392 +3 1393 1394 470 +3 1392 469 1391 +3 469 485 1391 +3 1391 485 1390 +3 1390 496 1389 +3 1389 499 682 +3 682 507 674 +3 674 513 675 +3 675 535 1023 +3 1023 528 670 +3 528 547 670 +3 485 496 1390 +3 496 499 1389 +3 499 507 682 +3 507 513 674 +3 513 535 675 +3 535 528 1023 +3 463 456 1394 +3 456 470 1394 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-split-1.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-split-1.off new file mode 100644 index 000000000000..b032321ed281 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-split-1.off @@ -0,0 +1,78 @@ +OFF +60 14 0 + +10 10 10 +-9.9999199999999995 10.040100000000001 -9.9999199999999995 +-6.0047800000000002 6.0240600000000004 -10.007999999999999 +6.0047800000000002 6.0240600000000004 -10.007999999999999 +6.0047899999999998 6.0168400000000002 -4.00319 +-5.9999900000000004 10.012 3.9999899999999999 +-6 10 10 +6 10 10 +-6.0072099999999997 4.00481 4.00481 +6.0144299999999999 -2.0024000000000002 4.00962 +6.0048000000000004 6.0072099999999997 4.0031999999999996 +6.0072099999999997 4.00481 4.00481 +-6.0240600000000004 6.0240600000000004 -10.007999999999999 +6.0240600000000004 6.0240600000000004 -10.007999999999999 +-6.0072000000000001 -2.0024000000000002 4.00962 +-6.0072099999999997 2.0024000000000002 4.0064099999999998 +-6.0048000000000004 6.0048000000000004 6.0048000000000004 +6.0072099999999997 2.0024000000000002 4.0064099999999998 +6.0048000000000004 6.0048000000000004 6.0048000000000004 +-6.0240900000000002 -10.0321 -10.0321 +10 10 -10 +6.0048000000000004 6.0048000000000004 10.007999999999999 +-10.007999999999999 6.0240600000000004 -10.007999999999999 +-6.0240600000000004 6.0192699999999997 -10.007999999999999 +-6.0240900000000002 -6.0240900000000002 -10.0321 +-6.0192600000000001 -6.0192600000000001 -6.0192600000000001 +-6.0240900000000002 -6.0192699999999997 -10.0321 +-6.0192600000000001 6.0192600000000001 -6.0192600000000001 +-6.0047899999999998 6.0192399999999999 4.00319 +4.0031999999999996 6.0072099999999997 4.0031999999999996 +-6.0047899999999998 6.0192399999999999 -4.00319 +-6.0047899999999998 6.0192399999999999 -6.0047899999999998 +-4.00319 6.0168400000000002 -4.00319 +-5.9999599999999997 10.0321 -9.9999400000000005 +-5.9999599999999997 10.0321 -5.9999599999999997 +5.9999900000000004 10.007999999999999 -9.9999800000000008 +5.9999900000000004 10.007999999999999 -3.9999899999999999 +5.9999900000000004 10.007999999999999 5.9999900000000004 +5.9999900000000004 10.007999999999999 3.9999899999999999 +-6.0048000000000004 6.0060099999999998 5.0006000000000004 +10.019399999999999 -2.00387 0.33785900000000002 +10.0219 -0.92307300000000003 -0.92307300000000003 +6.0144299999999999 -2.0018699999999998 5.3379099999999999 +-6.0110000000000001 0.85517500000000002 4.0073299999999996 +6.0109899999999996 0.86136599999999997 4.0073299999999996 +-6.00481 5.9986300000000004 10.007999999999999 +-6.0116300000000003 2.0038800000000001 0.33143699999999998 +-6.0060099999999998 6.0060099999999998 5.0006000000000004 +6.0116199999999997 2.00387 0.336254 +-6.0116300000000003 -2.0038800000000001 0.33143699999999998 +10.0093 -2.0018699999999998 5.3355100000000002 +-10.02 0 -2.0080200000000001 +10.02 0 -1.9999899999999999 +10 6 10 +-10.007999999999999 6.0240600000000004 5.0200500000000003 +6.0060099999999998 6.0048000000000004 5.0039999999999996 +10.007999999999999 5.9999900000000004 4.9999900000000004 +6.0240600000000004 6.0047800000000002 -10.007999999999999 +10.007999999999999 5.9999900000000004 -9.9999800000000008 +-6.0047899999999998 6.0192399999999999 5.0160400000000003 +4 19 24 26 25 +6 49 40 50 42 9 14 +6 20 0 41 52 56 58 +10 51 54 59 39 47 46 48 55 56 52 +10 22 23 27 31 30 32 29 28 59 54 +3 1 34 33 +9 43 8 16 39 59 28 5 6 45 +6 43 44 11 17 15 8 +8 38 10 18 11 44 21 7 37 +3 21 53 0 +4 55 57 58 56 +7 32 4 3 13 18 10 29 +4 12 2 31 27 +4 35 3 4 36 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug2.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug2.off new file mode 100644 index 000000000000..6ac41106dd82 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug2.off @@ -0,0 +1,19 @@ +OFF +13 2 0 + +-5.9999900000000004 10.012 3.9999899999999999 +-6 10 10 +-6.0072099999999997 4.00481 4.00481 +6.0072099999999997 4.00481 4.00481 +-6.0072099999999997 2.0024000000000002 4.0064099999999998 +-6.0048000000000004 6.0048000000000004 6.0048000000000004 +6.0072099999999997 2.0024000000000002 4.0064099999999998 +-6.0047899999999998 6.0192399999999999 4.00319 +-6.0048000000000004 6.0060099999999998 5.0006000000000004 +-6.0110000000000001 0.85517500000000002 4.0073299999999996 +6.0109899999999996 0.86136599999999997 4.0073299999999996 +-6.00481 5.9986300000000004 10.007999999999999 +-6.0047899999999998 6.0192399999999999 5.0160400000000003 +9 9 2 5 8 12 7 0 1 11 +6 9 10 3 6 4 2 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug5.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug5.off new file mode 100644 index 000000000000..99c3132caa91 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug5.off @@ -0,0 +1,36 @@ +OFF +23 9 0 + +0.22012599999999999 3.9465400000000001e-10 -0.17452799999999999 +0.22254199999999999 -0.024537199999999999 -0.17452799999999999 +0.22969899999999999 -0.048132000000000001 -0.17452799999999999 +0.24132200000000001 -0.069877800000000004 -0.17452799999999999 +0.22012599999999999 1.99686e-10 0.18239 +0.256913 -0.088889099999999999 0.18239 +0.22253899999999999 -0.0245212 0.18239 +0.24129200000000001 -0.069834099999999996 0.18239 +-0.145422 -0.019568700000000001 0.053459100000000002 +-0.14779900000000001 2.56918e-09 0.053459100000000002 +-0.12723300000000001 -0.054221900000000003 0.053459100000000002 +-0.138432 -0.0379998 0.053459100000000002 +-0.075883199999999998 -0.081165600000000004 0.053459100000000002 +0.013348 -0.019563000000000001 0.053459100000000002 +0.0063605900000000002 -0.037990299999999998 0.053459100000000002 +-0.14779900000000001 -2.56918e-09 0.0188679 +0.0133469 -0.019568700000000001 0.0188679 +-0.075903100000000001 -0.081163100000000002 0.0188679 +-0.056192300000000001 -0.081165600000000004 0.0188679 +-0.11249099999999999 -0.067281900000000006 0.0188679 +-0.138436 -0.037990299999999998 0.0188679 +-0.12724199999999999 -0.054210800000000003 0.0188679 +-0.145423 -0.019563000000000001 0.0188679 +3 1 0 4 +3 6 2 1 +3 1 4 6 +3 3 7 5 +3 14 13 16 +3 17 12 18 +3 11 20 8 +3 10 19 21 +3 9 22 15 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug6.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug6.off new file mode 100644 index 000000000000..063553f0abd8 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug6.off @@ -0,0 +1,23 @@ +OFF +13 6 0 + +185.18898585648856 16.966320499379513 -137.07794146876512 +185.19196127302405 16.93088720888337 -136.82186521302503 +185.22200238828486 16.573137108628089 -136.8292854104584 +185.17694071539364 17.10976225901101 -136.81815511430835 +185.20398064811857 16.787752288367066 -137.08161172697817 +183.96211344530562 17.334502712732217 -136.81110800926174 +185.20698183065446 16.752012158755729 -136.82557531174172 +184.18920469320116 17.027738501495833 -137.07471295649836 +184.29442238224937 15.888341147385638 -136.8415239085119 +180.73913860706105 -13.467536383885621 -34.379750337388344 +179.86184934883133 -13.125415608330357 -34.375676328932819 +181.67230056427172 -13.021006010825106 -34.373019686879069 +180.74802078058448 -12.064023272631362 -34.360583543228728 +3 1 5 3 +3 0 7 4 +3 2 5 6 +3 5 2 8 +3 9 11 12 +3 12 10 9 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug7.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug7.off new file mode 100644 index 000000000000..1d4745468eea --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug7.off @@ -0,0 +1,560 @@ +OFF +381 175 0 + +0.050000000699999998 0.050000000699999998 0.050000000699999998 +0.050000000699999998 0.043749999300000002 -0.037500001499999998 +0.050000000699999998 0.043742001099999997 0.037810999900000003 +0.050000000699999998 0.043620001499999998 0.036230001599999999 +0.050000000699999998 0.043536998299999997 -0.035881999900000003 +0.050000000699999998 0.043462999199999998 0.039372000800000001 +0.050000000699999998 0.042913000999999999 -0.034375000699999998 +0.050000000699999998 0.042913000999999999 -0.040624998500000002 +0.050000000699999998 0.042800001800000001 0.040812999000000003 +0.050000000699999998 0.042121999 0.016706999399999999 +0.050000000699999998 0.041919000400000003 -0.033080998799999997 +0.050000000699999998 0.041919000400000003 -0.041919000400000003 +0.050000000699999998 0.041795000399999997 0.042040001600000002 +0.050000000699999998 0.041042998400000003 0.032350998399999997 +0.050000000699999998 0.040957000100000002 0.017706999599999999 +0.050000000699999998 0.040624998500000002 -0.032086998200000001 +0.050000000699999998 0.040624998500000002 -0.042913000999999999 +0.050000000699999998 0.040513999799999999 0.042975000999999999 +0.050000000699999998 0.039632998400000001 0.0316249989 +0.050000000699999998 0.039585001799999998 0.018392000299999999 +0.050000000699999998 0.039117999399999999 -0.031463000900000003 +0.050000000699999998 0.039117999399999999 -0.043536998299999997 +0.050000000699999998 0.039039000900000002 0.043558001499999999 +0.050000000699999998 0.037833999799999997 -0.027458000900000001 +0.050000000699999998 0.037500001499999998 -0.03125 +0.050000000699999998 0.0370969996 -0.0292380005 +0.050000000699999998 0.035923998800000002 -0.0307669993 +0.050000000699999998 0.032614000099999998 -0.0326780006 +0.050000000699999998 0.032086998200000001 -0.034375000699999998 +0.050000000699999998 0.032086998200000001 -0.040624998500000002 +0.050000000699999998 0.031463000900000003 -0.035881999900000003 +0.050000000699999998 0.031463000900000003 -0.039117999399999999 +0.050000000699999998 0.03125 -0.037500001499999998 +0.050000000699999998 0.0307030007 -0.032930001600000001 +0.050000000699999998 0.018717000300000002 -0.036857999900000001 +0.050000000699999998 0.018691999800000001 -0.0383489989 +0.050000000699999998 0.0183879994 -0.035404000400000003 +0.050000000699999998 0.018315000500000001 -0.039790999100000002 +0.050000000699999998 0.017723999899999999 -0.034069001699999997 +0.050000000699999998 0.017606999700000001 -0.041103001700000003 +0.050000000699999998 0.016762999800000001 -0.032930001600000001 +0.050000000699999998 0.0125000002 -0.043749999300000002 +0.050000000699999998 0.0110200001 -0.043572001200000002 +0.050000000699999998 0.0096239997000000001 -0.0430490002 +0.050000000699999998 0.0084309997000000005 -0.0077559999000000003 +0.050000000699999998 0.0083919996 -0.042210001499999997 +0.050000000699999998 0.0082879997999999993 0.0078819999000000005 +0.050000000699999998 0.0073930002000000003 -0.041103001700000003 +0.050000000699999998 0.0067460000999999999 0.0149400001 +0.050000000699999998 0.0066849999000000004 -0.039790999100000002 +0.050000000699999998 0.0066120001000000003 -0.035404000400000003 +0.050000000699999998 0.0063240001999999998 0.0134619996 +0.050000000699999998 0.0063080000999999998 -0.0383489989 +0.050000000699999998 0.0062830001 -0.036857999900000001 +0.050000000699999998 0.0062759998000000003 0.011926000000000001 +0.050000000699999998 -0.0062500000999999999 0.0125000002 +0.050000000699999998 -0.0062830001 -0.036857999900000001 +0.050000000699999998 -0.0063080000999999998 -0.0383489989 +0.050000000699999998 -0.0064630000999999996 0.014117999900000001 +0.050000000699999998 -0.0064630000999999996 0.010882000399999999 +0.050000000699999998 -0.0066120001000000003 -0.035404000400000003 +0.050000000699999998 -0.0066849999000000004 -0.039790999100000002 +0.050000000699999998 -0.0070870001999999996 0.015625 +0.050000000699999998 -0.0072760000000000003 -0.034069001699999997 +0.050000000699999998 -0.0073930002000000003 -0.041103001700000003 +0.050000000699999998 -0.0080810003000000002 0.0080810003000000002 +0.050000000699999998 -0.0083320001000000005 -0.0078419995999999999 +0.050000000699999998 -0.0083919996 -0.042210001499999997 +0.050000000699999998 -0.0093750003999999998 0.0070870001999999996 +0.050000000699999998 -0.0096239997000000001 -0.0430490002 +0.050000000699999998 -0.010882000399999999 0.031463000900000003 +0.050000000699999998 -0.0110200001 -0.043572001200000002 +0.050000000699999998 -0.0125000002 0.03125 +0.050000000699999998 -0.0125000002 0.018750000700000002 +0.050000000699999998 -0.0125000002 -0.043749999300000002 +0.050000000699999998 -0.015625 0.042913000999999999 +0.050000000699999998 -0.016919000100000001 0.041919000400000003 +0.050000000699999998 -0.0183879994 -0.035404000400000003 +0.050000000699999998 -0.018750000700000002 0.0125000002 +0.050000000699999998 -0.019723000000000001 -0.0326780006 +0.050000000699999998 -0.0215039998 -0.031941000400000003 +0.050000000699999998 -0.023033000500000001 -0.0307669993 +0.050000000699999998 -0.024205999400000001 -0.0292380005 +0.050000000699999998 -0.03125 0.0125000002 +0.050000000699999998 -0.031463000900000003 0.014117999900000001 +0.050000000699999998 -0.031463000900000003 -0.035881999900000003 +0.050000000699999998 -0.032086998200000001 0.040624998500000002 +0.050000000699999998 -0.032086998200000001 -0.034375000699999998 +0.050000000699999998 -0.033080998799999997 0.041919000400000003 +0.050000000699999998 -0.033080998799999997 0.033080998799999997 +0.050000000699999998 -0.033080998799999997 0.016919000100000001 +0.050000000699999998 -0.034375000699999998 0.042913000999999999 +0.050000000699999998 -0.034375000699999998 0.032086998200000001 +0.043719999500000002 0.043719999500000002 -0.036887001199999998 +0.043630000199999998 0.043630000199999998 -0.036281000799999998 +0.043630000199999998 0.038718998400000003 -0.043630000199999998 +0.043274000299999997 0.043274000299999997 -0.035108 +0.043274000299999997 0.0398919992 -0.031725998999999998 +0.043274000299999997 0.0398919992 -0.043274000299999997 +0.042697001200000001 0.042697001200000001 -0.034028001099999997 +0.042697001200000001 0.042697001200000001 -0.040972001899999999 +0.042697001200000001 0.040972001899999999 -0.0323030017 +0.042697001200000001 0.040972001899999999 -0.042697001200000001 +0.041919000400000003 0.041919000400000003 -0.033080998799999997 +0.041919000400000003 0.041919000400000003 -0.041919000400000003 +0.033080998799999997 0.041919000400000003 -0.041919000400000003 +0.033080998799999997 0.033080998799999997 -0.033080998799999997 +0.0323030017 0.042697001200000001 -0.040972001899999999 +0.0323030017 0.034028001099999997 -0.0323030017 +0.0323030017 0.0323030017 -0.034028001099999997 +0.031725998999999998 0.043274000299999997 -0.0398919992 +0.031725998999999998 0.0398919992 -0.031725998999999998 +0.031725998999999998 0.035108 -0.031725998999999998 +0.031369999099999997 0.043630000199999998 -0.038718998400000003 +0.031369999099999997 0.038718998400000003 -0.031369999099999997 +0.031369999099999997 0.036281000799999998 -0.031369999099999997 +0.0312799998 0.038113001700000003 -0.0312799998 +0.0312799998 0.036887001199999998 -0.0312799998 +-0.0323030017 0.017697000899999999 -0.034028001099999997 +0.03125 0.037500001499999998 -0.03125 +0.018750000700000002 0.037500001499999998 -0.03125 +0.018750000700000002 0.03125 -0.037500001499999998 +0.018750000700000002 0.018750000700000002 -0.037500001499999998 +0.018747 0.037686999899999997 -0.0279399995 +0.018742000700000001 0.037188000999999998 -0.029075000399999999 +0.018733000400000002 0.0129620004 -0.00039100000000000002 +0.0187309999 0.037987999600000003 -0.026743000400000001 +0.018721999600000001 0.038086000799999999 -0.018721999600000001 +0.018721999600000001 0.038086000799999999 -0.025546999599999998 +0.018719999099999999 0.038113001700000003 -0.0312799998 +0.018719999099999999 0.036887001199999998 -0.0312799998 +0.018719999099999999 0.0312799998 -0.036887001199999998 +0.018719999099999999 0.0312799998 -0.038113001700000003 +0.018719999099999999 0.018719999099999999 -0.036887001199999998 +0.018719999099999999 0.018719999099999999 -0.038113001700000003 +0.018719999099999999 0.0131130004 -0.0062799999000000004 +0.0186730009 0.036520998899999997 -0.030092999299999999 +0.018629999800000002 0.038718998400000003 -0.031369999099999997 +0.018629999800000002 0.036281000799999998 -0.031369999099999997 +0.018629999800000002 0.031369999099999997 -0.036281000799999998 +0.018629999800000002 0.031369999099999997 -0.038718998400000003 +0.018629999800000002 0.018629999800000002 -0.036281000799999998 +0.018629999800000002 0.018629999800000002 -0.038718998400000003 +0.018629999800000002 0.013718999900000001 -0.0063700001000000003 +0.018528999800000001 0.039147999099999997 -0.018528999800000001 +0.018492000200000001 0.0357219987 -0.030960999400000001 +0.0184390005 0.0144459996 -0.00039100000000000002 +0.018273999900000001 0.043274000299999997 -0.0398919992 +0.018273999900000001 0.0398919992 -0.031725998999999998 +0.018273999900000001 0.035108 -0.031725998999999998 +0.018273999900000001 0.031725998999999998 -0.035108 +0.018273999900000001 0.031725998999999998 -0.0398919992 +0.018273999900000001 0.018273999900000001 -0.035108 +0.018273999900000001 0.018273999900000001 -0.0398919992 +0.018273999900000001 0.0148919998 -0.0067260000000000002 +0.018155999499999999 0.040160000299999998 -0.018155999499999999 +0.018123999200000001 0.034775000100000002 -0.031706001599999999 +0.0177979991 0.015815999399999999 -0.00039100000000000002 +0.017697000899999999 0.042697001200000001 -0.040972001899999999 +0.017697000899999999 0.040972001899999999 -0.0323030017 +0.017697000899999999 0.034028001099999997 -0.0323030017 +0.017697000899999999 0.0323030017 -0.034028001099999997 +0.017697000899999999 0.0323030017 -0.040972001899999999 +0.017697000899999999 0.017697000899999999 -0.034028001099999997 +0.017697000899999999 0.017697000899999999 -0.040972001899999999 +0.017697000899999999 0.015971999600000002 -0.0073029999999999996 +0.017613999500000001 0.041092999300000002 -0.017613999500000001 +0.017563000299999999 0.033835999700000001 -0.032232001400000002 +0.016919000100000001 0.041919000400000003 -0.016919000100000001 +0.016919000100000001 0.041919000400000003 -0.033080998799999997 +0.016919000100000001 0.041919000400000003 -0.041919000400000003 +0.016919000100000001 0.033080998799999997 -0.033080998799999997 +0.016919000100000001 0.033080998799999997 -0.041919000400000003 +0.016919000100000001 0.016919000100000001 -0.033080998799999997 +0.016919000100000001 0.016919000100000001 -0.041919000400000003 +0.016797000499999999 0.032960999800000002 -0.0170389991 +0.016797000499999999 0.032960999800000002 -0.032575998500000002 +0.016797000499999999 0.0170389991 -0.00039100000000000002 +0.016797000499999999 0.0170389991 -0.0079610002999999999 +-0.031463000900000003 0.050000000699999998 -0.014117999900000001 +0.015971999600000002 0.042697001200000001 -0.017697000899999999 +0.015971999600000002 0.042697001200000001 -0.0323030017 +0.015971999600000002 0.0323030017 -0.0323030017 +0.015971999600000002 0.0323030017 -0.042697001200000001 +0.015971999600000002 0.017697000899999999 -0.0323030017 +0.015971999600000002 0.017697000899999999 -0.042697001200000001 +0.015846999399999999 0.032221999000000001 -0.017777999900000001 +0.015846999399999999 0.017777999900000001 -0.0072220000000000001 +0.0155229997 0.0179699995 -0.00039100000000000002 +0.0148919998 0.043274000299999997 -0.018273999900000001 +0.0148919998 0.043274000299999997 -0.031725998999999998 +0.0148919998 0.031725998999999998 -0.031725998999999998 +0.0148919998 0.031725998999999998 -0.043274000299999997 +0.0148919998 0.018273999900000001 -0.031725998999999998 +0.0148919998 0.018273999900000001 -0.043274000299999997 +0.014773 0.018322000299999999 -0.0066780001000000004 +0.014055999899999999 0.018552999899999999 -0.00039100000000000002 +0.013718999900000001 0.043630000199999998 -0.018629999800000002 +0.013718999900000001 0.043630000199999998 -0.031369999099999997 +0.013718999900000001 0.031369999099999997 -0.031369999099999997 +0.013718999900000001 0.031369999099999997 -0.043630000199999998 +0.013718999900000001 0.018629999800000002 -0.031369999099999997 +0.013718999900000001 0.018629999800000002 -0.043630000199999998 +0.0136139998 0.018650000900000002 -0.0063499999000000001 +0.0131130004 0.043719999500000002 -0.018719999099999999 +0.0131130004 0.043719999500000002 -0.0312799998 +0.0131130004 0.0312799998 -0.0312799998 +0.0131130004 0.0312799998 -0.043719999500000002 +0.0131130004 0.018719999099999999 -0.0312799998 +0.0131130004 0.018719999099999999 -0.043719999500000002 +-0.043274000299999997 0.0398919992 -0.043274000299999997 +0.0130169997 0.018728999400000002 -0.0062709999999999997 +0.0125000002 0.050000000699999998 -0.043749999300000002 +0.0125000002 0.043749999300000002 -0.018750000700000002 +0.0125000002 0.043749999300000002 -0.03125 +0.0125000002 0.03125 -0.03125 +0.0125000002 0.03125 -0.043749999300000002 +0.0125000002 0.018750000700000002 -0.03125 +0.0125000002 0.018750000700000002 -0.043749999300000002 +0.012489999599999999 0.018750000700000002 -0.00039100000000000002 +0.0118869999 0.043719999500000002 -0.018719999099999999 +0.0118869999 0.043719999500000002 -0.0312799998 +0.0118869999 0.0312799998 -0.0312799998 +0.0118869999 0.0312799998 -0.043719999500000002 +0.0118869999 0.018719999099999999 -0.0312799998 +0.0118869999 0.018719999099999999 -0.043719999500000002 +0.011281000500000001 0.043630000199999998 -0.018629999800000002 +0.011281000500000001 0.043630000199999998 -0.031369999099999997 +-0.050000000699999998 0.014117999900000001 -0.043536998299999997 +-0.050000000699999998 0.015625 -0.042913000999999999 +0.010882000399999999 0.050000000699999998 -0.043536998299999997 +0.0101079997 0.043274000299999997 -0.018273999900000001 +0.0101079997 0.043274000299999997 -0.031725998999999998 +-0.050000000699999998 0.03125 -0.037500001499999998 +-0.050000000699999998 0.031463000900000003 -0.039117999399999999 +-0.050000000699999998 0.031463000900000003 -0.035881999900000003 +0.0090279998000000004 0.042697001200000001 -0.017697000899999999 +0.0090279998000000004 0.042697001200000001 -0.0323030017 +-0.050000000699999998 0.032086998200000001 -0.040624998500000002 +-0.050000000699999998 0.032086998200000001 -0.034375000699999998 +-0.050000000699999998 0.033080998799999997 -0.041919000400000003 +0.0080810003000000002 0.041919000400000003 -0.016919000100000001 +0.0080810003000000002 0.041919000400000003 -0.033080998799999997 +-0.050000000699999998 0.035881999900000003 -0.043536998299999997 +-0.050000000699999998 0.035881999900000003 -0.031463000900000003 +-0.050000000699999998 0.037500001499999998 -0.043749999300000002 +-0.050000000699999998 0.037500001499999998 -0.03125 +0.0073029999999999996 0.040972001899999999 -0.017697000899999999 +0.0073029999999999996 0.040972001899999999 -0.0323030017 +-0.038228999800000003 0.028161000499999998 0.031293001000000001 +-0.050000000699999998 0.039117999399999999 -0.043536998299999997 +-0.050000000699999998 0.040624998500000002 -0.042913000999999999 +0.0070870001999999996 0.050000000699999998 -0.0093750003999999998 +0.0067260000000000002 0.0398919992 -0.018273999900000001 +0.0067260000000000002 0.0398919992 -0.031725998999999998 +-0.050000000699999998 0.041919000400000003 -0.041919000400000003 +-0.050000000699999998 0.043536998299999997 -0.035881999900000003 +-0.042697001200000001 0.0323030017 -0.040972001899999999 +-0.042697001200000001 0.034028001099999997 -0.042697001200000001 +0.0064630000999999996 0.050000000699999998 -0.010882000399999999 +-0.042697001200000001 0.040972001899999999 -0.042697001200000001 +0.0063700001000000003 0.038718998400000003 -0.018629999800000002 +0.0063700001000000003 0.038718998400000003 -0.031369999099999997 +-0.050000000699999998 0.043749999300000002 -0.037500001499999998 +-0.050000000699999998 0.050000000699999998 -0.050000000699999998 +-0.050000000699999998 0.050000000699999998 0.050000000699999998 +0.0062799999000000004 0.038113001700000003 -0.018719999099999999 +0.0062799999000000004 0.038113001700000003 -0.0312799998 +0.0062799999000000004 0.036887001199999998 -0.018719999099999999 +0.0062799999000000004 0.036887001199999998 -0.0312799998 +0.0062500000999999999 0.050000000699999998 -0.0125000002 +0.0062500000999999999 0.037500001499999998 -0.018750000700000002 +0.0062500000999999999 0.037500001499999998 -0.03125 +0.0024999999 0.050000000699999998 0.050000000699999998 +0.0024999999 0.050000000699999998 0.037500001499999998 +-0.041919000400000003 0.033080998799999997 -0.041919000400000003 +-0.041919000400000003 0.041919000400000003 -0.041919000400000003 +-0.0062500000999999999 0.050000000699999998 0.037500001499999998 +-0.0062799999000000004 0.038113001700000003 -0.018719999099999999 +-0.037500001499999998 0.03125 0.03125 +-0.043749999300000002 0.037500001499999998 -0.043749999300000002 +-0.037500001499999998 0.050000000699999998 -0.0062500000999999999 +-0.037500001499999998 0.050000000699999998 0.0062500000999999999 +-0.0063700001000000003 0.038718998400000003 -0.018629999800000002 +-0.0063700001000000003 0.038718998400000003 -0.031369999099999997 +-0.037500001499999998 0.050000000699999998 0.043749999300000002 +-0.037059001600000002 0.027484999999999999 0.031266000100000003 +-0.043749999300000002 0.050000000699999998 -0.0125000002 +-0.0064630000999999996 0.050000000699999998 0.039117999399999999 +-0.0064630000999999996 0.050000000699999998 -0.010882000399999999 +-0.0067260000000000002 0.0398919992 -0.018273999900000001 +-0.0067260000000000002 0.0398919992 -0.031725998999999998 +-0.036887001199999998 0.0312799998 0.0312799998 +-0.040624998500000002 0.050000000699999998 0.0070870001999999996 +-0.040624998500000002 0.050000000699999998 0.042913000999999999 +-0.0070870001999999996 0.050000000699999998 0.040624998500000002 +-0.0070870001999999996 0.050000000699999998 -0.0093750003999999998 +-0.0073029999999999996 0.040972001899999999 -0.017697000899999999 +-0.0073029999999999996 0.040972001899999999 -0.0323030017 +-0.043719999500000002 0.0312799998 -0.038113001700000003 +-0.043719999500000002 0.0312799998 -0.036887001199999998 +-0.043719999500000002 0.036887001199999998 -0.043719999500000002 +-0.043719999500000002 0.036887001199999998 -0.0312799998 +-0.043719999500000002 0.038113001700000003 -0.043719999500000002 +-0.0080810003000000002 0.050000000699999998 0.041919000400000003 +-0.0080810003000000002 0.041919000400000003 -0.016919000100000001 +-0.0080810003000000002 0.041919000400000003 -0.033080998799999997 +-0.043719999500000002 0.043719999500000002 -0.036887001199999998 +-0.036281000799999998 0.031369999099999997 0.031369999099999997 +-0.0093750003999999998 0.050000000699999998 0.042913000999999999 +-0.035904001400000003 0.026818999999999999 0.031456999499999999 +-0.010882000399999999 0.050000000699999998 0.043536998299999997 +-0.010882000399999999 0.050000000699999998 -0.043536998299999997 +-0.039117999399999999 0.050000000699999998 -0.0064630000999999996 +-0.039117999399999999 0.050000000699999998 0.0064630000999999996 +-0.035881999900000003 0.050000000699999998 0.043536998299999997 +-0.039117999399999999 0.050000000699999998 0.043536998299999997 +-0.0125000002 0.050000000699999998 0.043749999300000002 +-0.0125000002 0.050000000699999998 -0.043749999300000002 +-0.0125000002 0.03125 -0.018750000700000002 +-0.0125000002 0.03125 -0.03125 +-0.043630000199999998 0.031369999099999997 -0.038718998400000003 +-0.043630000199999998 0.031369999099999997 -0.036281000799999998 +-0.043630000199999998 0.036281000799999998 -0.043630000199999998 +-0.0131130004 0.0312799998 -0.018719999099999999 +-0.0131130004 0.0312799998 -0.0312799998 +-0.043630000199999998 0.038718998400000003 -0.043630000199999998 +-0.013718999900000001 0.031369999099999997 -0.018629999800000002 +-0.013718999900000001 0.031369999099999997 -0.031369999099999997 +-0.014117999900000001 0.050000000699999998 0.043536998299999997 +-0.0148919998 0.031725998999999998 -0.018273999900000001 +-0.0148919998 0.031725998999999998 -0.031725998999999998 +-0.015971999600000002 0.0323030017 -0.017697000899999999 +-0.015971999600000002 0.0323030017 -0.0323030017 +-0.043536998299999997 0.050000000699999998 -0.035881999900000003 +-0.043536998299999997 0.050000000699999998 -0.014117999900000001 +-0.016919000100000001 0.033080998799999997 -0.016919000100000001 +-0.016919000100000001 0.033080998799999997 -0.033080998799999997 +-0.016919000100000001 0.016919000100000001 -0.016919000100000001 +-0.016919000100000001 0.016919000100000001 -0.033080998799999997 +-0.017697000899999999 0.034028001099999997 -0.017697000899999999 +-0.017697000899999999 0.034028001099999997 -0.0323030017 +-0.017697000899999999 0.0323030017 -0.034028001099999997 +-0.017697000899999999 0.017697000899999999 -0.034028001099999997 +-0.017697000899999999 0.015971999600000002 -0.017697000899999999 +-0.017697000899999999 0.015971999600000002 -0.0323030017 +-0.018273999900000001 0.0398919992 -0.031725998999999998 +-0.018273999900000001 0.035108 -0.018273999900000001 +-0.018273999900000001 0.035108 -0.031725998999999998 +-0.018273999900000001 0.031725998999999998 -0.035108 +-0.018273999900000001 0.018273999900000001 -0.035108 +-0.018273999900000001 0.0148919998 -0.018273999900000001 +-0.018273999900000001 0.0148919998 -0.031725998999999998 +-0.018629999800000002 0.038718998400000003 -0.018629999800000002 +-0.018629999800000002 0.038718998400000003 -0.031369999099999997 +-0.018629999800000002 0.036281000799999998 -0.018629999800000002 +-0.018629999800000002 0.036281000799999998 -0.031369999099999997 +-0.018629999800000002 0.031369999099999997 -0.036281000799999998 +-0.018629999800000002 0.018629999800000002 -0.036281000799999998 +-0.018629999800000002 0.013718999900000001 -0.018629999800000002 +-0.018629999800000002 0.013718999900000001 -0.031369999099999997 +-0.018719999099999999 0.038113001700000003 -0.018719999099999999 +-0.018719999099999999 0.038113001700000003 -0.0312799998 +-0.018719999099999999 0.036887001199999998 -0.018719999099999999 +-0.018719999099999999 0.036887001199999998 -0.0312799998 +-0.018719999099999999 0.0312799998 -0.036887001199999998 +-0.018719999099999999 0.018719999099999999 -0.036887001199999998 +-0.018719999099999999 0.0131130004 -0.018719999099999999 +-0.018719999099999999 0.0131130004 -0.0312799998 +-0.018719999099999999 0.0118869999 -0.018719999099999999 +-0.043274000299999997 0.0148919998 -0.043274000299999997 +-0.018750000700000002 0.050000000699999998 -0.0125000002 +-0.018750000700000002 0.037500001499999998 -0.018750000700000002 +-0.018750000700000002 0.03125 -0.037500001499999998 +-0.018750000700000002 0.018750000700000002 -0.037500001499999998 +-0.018750000700000002 0.0125000002 -0.018750000700000002 +-0.018750000700000002 0.0125000002 -0.03125 +-0.0312799998 0.018719999099999999 -0.036887001199999998 +-0.043274000299999997 0.031725998999999998 -0.0398919992 +-0.043274000299999997 0.031725998999999998 -0.035108 +-0.03125 0.050000000699999998 -0.0125000002 +3 25 20 23 +3 26 24 25 +3 33 28 27 +3 40 38 33 +3 34 33 36 +3 34 30 33 +3 35 32 34 +3 37 31 35 +3 39 29 37 +3 74 41 42 +3 71 74 42 +3 43 71 42 +3 43 69 71 +3 43 45 69 +3 45 47 67 +3 47 49 64 +3 53 50 56 +3 150 161 109 +3 375 369 376 +3 367 375 368 +3 359 367 360 +3 351 359 352 +3 345 339 344 +3 339 338 344 +3 50 63 60 +3 160 149 112 +3 149 138 115 +3 119 117 120 +3 116 119 120 +3 148 114 137 +3 159 111 148 +3 107 170 105 +3 107 110 158 +3 110 113 147 +3 281 313 282 +3 313 293 314 +3 106 171 108 +3 61 52 57 +3 67 47 64 +3 69 45 67 +3 80 77 79 +3 81 85 80 +3 82 87 81 +3 316 294 265 +3 315 285 265 +3 273 317 329 +3 273 309 311 +3 273 304 309 +3 274 277 288 +3 295 274 288 +3 264 287 335 +3 334 264 335 +3 380 371 179 +3 44 46 66 +3 229 228 370 +3 230 212 318 +3 312 230 318 +3 101 15 97 +3 10 103 99 +3 96 4 6 +3 4 94 93 +3 1 4 93 +3 343 118 350 +3 358 377 366 +3 100 11 7 +3 100 104 11 +3 98 16 102 +3 98 95 21 +3 349 342 350 +3 365 357 366 +3 373 365 374 +3 297 305 306 +3 290 297 298 +3 283 290 291 +3 278 283 284 +3 157 165 154 +3 143 146 154 +3 135 125 143 +3 379 235 239 +3 235 322 300 +3 90 89 92 +3 86 76 88 +3 88 75 91 +3 72 73 70 +3 83 78 84 +3 68 66 65 +3 59 54 55 +3 55 51 58 +3 58 48 62 +3 320 325 319 +3 327 324 325 +3 330 327 328 +3 332 330 331 +3 336 332 333 +3 340 336 337 +3 347 340 341 +3 355 347 348 +3 363 355 356 +3 372 363 364 +3 372 362 361 +3 353 354 346 +3 196 203 195 +3 259 270 289 +3 296 252 289 +3 188 196 195 +3 223 225 216 +3 209 216 218 +3 200 207 202 +3 192 200 194 +3 185 174 183 +3 162 172 174 +3 151 162 164 +3 140 151 153 +3 132 140 142 +3 121 132 134 +3 131 121 122 +3 139 131 133 +3 150 139 141 +3 173 161 163 +3 161 152 163 +3 191 184 193 +3 199 191 201 +3 206 199 208 +3 215 206 217 +3 222 215 224 +3 187 178 177 +3 219 211 196 +3 9 13 14 +3 14 18 19 +3 2 3 0 +3 0 8 5 +3 12 0 17 +3 0 22 17 +3 286 279 249 +3 286 308 292 +3 286 310 308 +3 268 269 271 +3 267 271 272 +3 261 266 262 +3 253 261 254 +3 247 253 248 +3 241 247 242 +3 236 241 237 +3 231 236 232 +3 227 221 226 +3 213 220 221 +3 204 213 214 +3 197 204 205 +3 189 197 198 +3 180 189 190 +3 168 180 181 +3 166 168 169 +3 144 155 148 +3 127 144 137 +3 182 186 175 +3 171 182 176 +3 160 171 167 +3 149 160 156 +3 138 149 145 +3 130 138 136 +3 120 130 124 +3 123 126 129 +3 126 128 129 +3 263 307 256 +3 246 302 244 +3 233 299 234 +3 260 276 255 +3 260 255 251 +3 250 326 210 +3 280 303 245 +3 243 323 301 +3 240 275 258 +3 257 275 240 +3 238 234 378 +3 234 321 378 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug8.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug8.off new file mode 100644 index 000000000000..24eab3cc0a76 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug8.off @@ -0,0 +1,26 @@ +OFF +16 6 0 + +-27.740373000000002 41.290998000000002 95 +-15.450850000000001 47.552849999999999 96.239999999999995 +-17.099747000000001 46.712691999999997 95 +-19.747043000000001 45.363821000000002 89.853847999999999 +-25.093077000000001 42.639868999999997 89.853847999999999 +-29.359850000000002 40.410400000000003 96.384 +-15.4354 47.505249999999997 96.384 +-29.359850000000002 40.410400000000003 96.480000000000004 +-13.651558 47.837826999999997 70.299999999999997 +-13.651558 47.837826999999997 53.799999999999997 +-25.498366000000001 36.890810999999999 95 +-22.851068000000001 38.239677999999998 89.853847999999999 +-17.505030000000001 40.963622999999998 89.853847999999999 +0 49.899999999999999 96.623999999999995 +-15.41995 47.457700000000003 96.719999999999999 +0 49.899999999999999 96.719999999999999 +3 9 1 8 +3 0 10 4 +3 2 10 0 +3 11 12 3 +3 5 7 6 +3 13 14 15 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug9.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug9.off new file mode 100644 index 000000000000..a3d6d7813ee5 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug9.off @@ -0,0 +1,949 @@ +OFF +485 460 0 + +26.597300000000001 36.607999999999997 97.103999999999999 +26.567900000000002 36.567549999999997 96.959999999999994 +36.567549999999997 26.567900000000002 96.959999999999994 +36.607999999999997 26.597300000000001 97.103999999999999 +29.359850000000002 40.410400000000003 96.480000000000004 +13.983000000000001 43.035299999999999 97.103999999999999 +40.410400000000003 29.359850000000002 96.480000000000004 +15.4354 47.505249999999997 96.480000000000004 +36.405749999999998 26.45035 95.760000000000005 +49.450000000000003 0 97.488 +47.02975 -15.280900000000001 97.488 +49.25 0 97.680000000000007 +42.797550000000001 13.905749999999999 95.760000000000005 +47.02975 15.280900000000001 97.488 +40.005899999999997 29.065999999999999 97.488 +46.839550000000003 15.219099999999999 97.680000000000007 +29.065999999999999 40.005899999999997 97.488 +39.844099999999997 28.948399999999999 97.680000000000007 +15.280900000000001 47.02975 97.488 +0 49.450000000000003 97.488 +0 49.25 97.680000000000007 +-15.280900000000001 47.02975 97.488 +-15.219099999999999 46.839550000000003 97.680000000000007 +-29.065999999999999 40.005899999999997 97.488 +-28.948399999999999 39.844099999999997 97.680000000000007 +-39.844099999999997 28.948399999999999 97.680000000000007 +-40.005899999999997 29.065999999999999 97.488 +-47.02975 15.280900000000001 97.488 +-49.450000000000003 0 97.488 +0 49.950000000000003 96.480000000000004 +-43.130400000000002 14.0139 97.200000000000003 +-45.350000000000001 0 97.200000000000003 +44.557000000000002 14.477449999999999 98.304000000000002 +0 49.149999999999999 97.775999999999996 +-39.763199999999998 28.88965 97.775999999999996 +-36.688899999999997 26.65605 97.200000000000003 +-26.685449999999999 36.729349999999997 97.296000000000006 +-26.65605 36.688899999999997 97.200000000000003 +-49.149999999999999 0 97.775999999999996 +14.601050000000001 44.937399999999997 98.400000000000006 +0 47.25 98.400000000000006 +-38.226050000000001 27.772849999999998 98.400000000000006 +-27.772849999999998 38.226050000000001 98.400000000000006 +-47.25 0 98.400000000000006 +43.891249999999999 14.261150000000001 98.016000000000005 +43.796149999999997 14.23025 97.920000000000002 +46.049999999999997 0 97.920000000000002 +-14.601050000000001 44.937399999999997 98.400000000000006 +37.336150000000004 27.126300000000001 98.016000000000005 +-37.255249999999997 27.067499999999999 97.920000000000002 +-43.891249999999999 14.261150000000001 98.016000000000005 +-43.796149999999997 14.23025 97.920000000000002 +-46.149999999999999 0 98.016000000000005 +27.126300000000001 37.336150000000004 98.016000000000005 +27.067499999999999 37.255249999999997 97.920000000000002 +-14.23025 43.796149999999997 97.920000000000002 +-27.126300000000001 37.336150000000004 98.016000000000005 +-27.067499999999999 37.255249999999997 97.920000000000002 +-37.336150000000004 27.126300000000001 98.016000000000005 +-14.029350000000001 43.177950000000003 97.296000000000006 +-36.810250000000003 26.744250000000001 97.391999999999996 +-44.937399999999997 14.601050000000001 98.400000000000006 +45.399999999999999 0 97.296000000000006 +43.177950000000003 -14.029350000000001 97.296000000000006 +45.5 0 97.391999999999996 +43.273049999999998 -14.06025 97.391999999999996 +0 45.399999999999999 97.296000000000006 +-14.06025 43.273049999999998 97.391999999999996 +43.273049999999998 14.06025 97.391999999999996 +43.177950000000003 14.029350000000001 97.296000000000006 +-44.699649999999998 14.5238 98.352000000000004 +14.029350000000001 43.177950000000003 97.296000000000006 +14.0139 43.130400000000002 97.200000000000003 +26.65605 36.688899999999997 97.200000000000003 +-47 0 98.352000000000004 +0 45.5 97.391999999999996 +-27.625900000000001 38.023800000000001 98.352000000000004 +-38.023800000000001 27.625900000000001 98.352000000000004 +-14.5238 44.699649999999998 98.352000000000004 +36.729349999999997 26.685449999999999 97.296000000000006 +36.688899999999997 26.65605 97.200000000000003 +43.130400000000002 14.0139 97.200000000000003 +0 47 98.352000000000004 +14.5238 44.699649999999998 98.352000000000004 +36.810250000000003 26.744250000000001 97.391999999999996 +26.685449999999999 36.729349999999997 97.296000000000006 +27.625900000000001 38.023800000000001 98.352000000000004 +38.023800000000001 27.625900000000001 98.352000000000004 +46.126249999999999 14.987299999999999 98.207999999999998 +39.237299999999998 28.5076 98.207999999999998 +46.031149999999997 14.9564 98.256 +39.156399999999998 28.448799999999999 98.256 +46.850000000000001 0 98.304000000000002 +47 0 98.352000000000004 +44.699649999999998 14.5238 98.352000000000004 +14.987299999999999 46.126249999999999 98.207999999999998 +28.448799999999999 39.156399999999998 98.256 +0 48.5 98.207999999999998 +44.699649999999998 -14.5238 98.352000000000004 +-14.987299999999999 46.126249999999999 98.207999999999998 +-14.9564 46.031149999999997 98.256 +-28.5076 39.237299999999998 98.207999999999998 +46.649299999999997 -15.157299999999999 97.872 +49.049999999999997 0 97.872 +-39.237299999999998 28.5076 98.207999999999998 +-28.448799999999999 39.156399999999998 98.256 +46.649299999999997 15.157299999999999 97.872 +-46.126249999999999 14.987299999999999 98.207999999999998 +-46.031149999999997 14.9564 98.256 +39.682299999999998 28.830850000000002 97.872 +28.830850000000002 39.682299999999998 97.872 +15.157299999999999 46.649299999999997 97.872 +0 49.049999999999997 97.872 +-15.1882 46.744450000000001 97.775999999999996 +-15.157299999999999 46.649299999999997 97.872 +-28.88965 39.763199999999998 97.775999999999996 +-28.830850000000002 39.682299999999998 97.872 +-39.682299999999998 28.830850000000002 97.872 +-46.649299999999997 15.157299999999999 97.872 +-13.905749999999999 42.797550000000001 95.760000000000005 +46.25 0 98.063999999999993 +43.891249999999999 -14.261150000000001 98.016000000000005 +-13.905749999999999 42.797550000000001 4.7999999999999998 +43.986350000000002 14.29205 98.063999999999993 +46.149999999999999 0 98.016000000000005 +44.794750000000001 -14.5547 98.352000000000004 +0 46.149999999999999 98.016000000000005 +14.261150000000001 43.891249999999999 98.016000000000005 +38.104700000000001 27.684699999999999 98.352000000000004 +44.794750000000001 14.5547 98.352000000000004 +27.684699999999999 38.104700000000001 98.352000000000004 +-14.261150000000001 43.891249999999999 98.016000000000005 +-43.320599999999999 14.075699999999999 97.488 +-26.773599999999998 36.850700000000003 97.488 +-36.850700000000003 26.773599999999998 97.488 +-39.601399999999998 28.772099999999998 97.920000000000002 +-46.554200000000002 15.1264 97.920000000000002 +-49.049999999999997 0 97.872 +-48.950000000000003 0 97.920000000000002 +-45.75 0 97.680000000000007 +-29.306730000000002 33.549370000000003 15.603847999999999 +-33.549370000000003 29.306730000000002 15.603847999999999 +-29.306730000000002 33.549370000000003 9.4961520000000004 +-35.670690999999998 27.185409 53.799999999999997 +-33.549370000000003 29.306730000000002 58.996152000000002 +-35.670690999999998 27.185409 70.299999999999997 +-17.505030000000001 40.963622999999998 17.746151999999999 +-22.851068000000001 38.239677999999998 17.746151999999999 +-48.399999999999999 0 98.256 +48.399999999999999 0 98.256 +46.031149999999997 -14.9564 98.256 +47.505249999999997 -15.4354 96.480000000000004 +49.950000000000003 0 96.480000000000004 +47.505249999999997 15.4354 96.480000000000004 +45 0 95.760000000000005 +47.100000000000001 0 98.352000000000004 +-35.670690999999998 27.185409 20.800000000000001 +-36.405749999999998 26.45035 4.7999999999999998 +-26.45035 36.405749999999998 4.7999999999999998 +-26.45035 36.405749999999998 95.760000000000005 +-27.185409 35.670690999999998 20.800000000000001 +-27.471183 35.384917000000002 5 +-36.405749999999998 26.45035 95.760000000000005 +-33.549370000000003 29.306730000000002 9.4961520000000004 +-33.549370000000003 29.306730000000002 25.996151999999999 +-35.670690999999998 27.185409 37.299999999999997 +-35.384917000000002 27.471183 5 +-33.549370000000003 29.306730000000002 42.496152000000002 +-25.524087000000002 36.877704999999999 12.550000000000001 +-14.832011 42.325595999999997 12.550000000000001 +-17.505030000000001 40.963622999999998 7.3538480000000002 +-17.505030000000001 40.963622999999998 34.246152000000002 +-22.851068000000001 38.239677999999998 34.246152000000002 +-25.524087000000002 36.877704999999999 29.050000000000001 +-14.832011 42.325595999999997 29.050000000000001 +-17.505030000000001 40.963622999999998 23.853847999999999 +-22.851068000000001 38.239677999999998 23.853847999999999 +-17.505030000000001 40.963622999999998 50.746152000000002 +-22.851068000000001 38.239677999999998 50.746152000000002 +-25.524087000000002 36.877704999999999 45.549999999999997 +-14.832011 42.325595999999997 45.549999999999997 +-17.505030000000001 40.963622999999998 40.353847999999999 +-22.851068000000001 38.239677999999998 40.353847999999999 +-17.505030000000001 40.963622999999998 67.246151999999995 +-22.851068000000001 38.239677999999998 67.246151999999995 +-25.524087000000002 36.877704999999999 62.049999999999997 +-14.832011 42.325595999999997 62.049999999999997 +-17.505030000000001 40.963622999999998 56.853847999999999 +-22.851068000000001 38.239677999999998 56.853847999999999 +-17.505030000000001 40.963622999999998 83.746151999999995 +-22.851068000000001 38.239677999999998 83.746151999999995 +-25.524087000000002 36.877704999999999 78.549999999999997 +-14.832011 42.325595999999997 78.549999999999997 +-17.505030000000001 40.963622999999998 73.353847999999999 +-22.851068000000001 38.239677999999998 73.353847999999999 +-25.498366000000001 36.890810999999999 95 +-22.851068000000001 38.239677999999998 89.853847999999999 +-14.857732 42.312489999999997 95 +-17.505030000000001 40.963622999999998 89.853847999999999 +-29.306730000000002 33.549370000000003 25.996151999999999 +-29.306730000000002 33.549370000000003 42.496152000000002 +-27.185409 35.670690999999998 37.299999999999997 +-29.306730000000002 33.549370000000003 32.103847999999999 +-33.549370000000003 29.306730000000002 32.103847999999999 +-29.306730000000002 33.549370000000003 58.996152000000002 +-27.185409 35.670690999999998 53.799999999999997 +-29.306730000000002 33.549370000000003 48.603847999999999 +-33.549370000000003 29.306730000000002 48.603847999999999 +-29.306730000000002 33.549370000000003 75.496151999999995 +-33.549370000000003 29.306730000000002 75.496151999999995 +-27.185409 35.670690999999998 70.299999999999997 +-29.306730000000002 33.549370000000003 65.103847999999999 +-33.549370000000003 29.306730000000002 65.103847999999999 +-29.306730000000002 33.549370000000003 91.996151999999995 +-33.549370000000003 29.306730000000002 91.996151999999995 +-35.670690999999998 27.185409 86.799999999999997 +-27.185409 35.670690999999998 86.799999999999997 +-29.306730000000002 33.549370000000003 81.603847999999999 +-33.549370000000003 29.306730000000002 81.603847999999999 +-36.405749999999998 26.45035 96.239999999999995 +-26.45035 36.405749999999998 96.239999999999995 +-29.330500000000001 40.369950000000003 96.719999999999999 +-40.369950000000003 29.330500000000001 96.719999999999999 +-47.457700000000003 15.41995 96.719999999999999 +-49.899999999999999 0 96.719999999999999 +-45.049999999999997 0 96.384 +-42.845100000000002 13.921200000000001 96.384 +-26.479749999999999 36.446199999999997 96.384 +-36.446199999999997 26.479749999999999 96.384 +47.457700000000003 -15.41995 96.623999999999995 +49.899999999999999 0 96.623999999999995 +45 0 96.239999999999995 +42.797550000000001 -13.905749999999999 96.239999999999995 +47.457700000000003 15.41995 96.623999999999995 +42.797550000000001 13.905749999999999 96.239999999999995 +40.369950000000003 29.330500000000001 96.623999999999995 +29.330500000000001 40.369950000000003 96.623999999999995 +36.405749999999998 26.45035 96.239999999999995 +15.41995 47.457700000000003 96.623999999999995 +26.45035 36.405749999999998 96.239999999999995 +13.905749999999999 42.797550000000001 96.239999999999995 +0 45 96.239999999999995 +-13.905749999999999 42.797550000000001 96.239999999999995 +15.41995 47.457700000000003 96.719999999999999 +15.404500000000001 47.410150000000002 96.864000000000004 +0 49.850000000000001 96.864000000000004 +-15.404500000000001 47.410150000000002 96.864000000000004 +-29.301100000000002 40.329500000000003 96.864000000000004 +-40.329500000000003 29.301100000000002 96.864000000000004 +-47.410150000000002 15.404500000000001 96.864000000000004 +-49.850000000000001 0 96.864000000000004 +-45.049999999999997 0 96.480000000000004 +-42.845100000000002 13.921200000000001 96.480000000000004 +-36.446199999999997 26.479749999999999 96.480000000000004 +-26.479749999999999 36.446199999999997 96.480000000000004 +-13.921200000000001 42.845100000000002 96.384 +-13.921200000000001 42.845100000000002 96.480000000000004 +47.457700000000003 -15.41995 96.719999999999999 +49.899999999999999 0 96.719999999999999 +45.049999999999997 0 96.384 +42.845100000000002 -13.921200000000001 96.384 +47.457700000000003 15.41995 96.719999999999999 +42.845100000000002 13.921200000000001 96.384 +40.369950000000003 29.330500000000001 96.719999999999999 +36.446199999999997 26.479749999999999 96.384 +29.330500000000001 40.369950000000003 96.719999999999999 +26.479749999999999 36.446199999999997 96.384 +13.921200000000001 42.845100000000002 96.384 +0 45.049999999999997 96.384 +29.301100000000002 40.329500000000003 96.864000000000004 +29.271699999999999 40.289050000000003 96.959999999999994 +15.389049999999999 47.3626 96.959999999999994 +0 49.799999999999997 96.959999999999994 +-15.389049999999999 47.3626 96.959999999999994 +-29.271699999999999 40.289050000000003 96.959999999999994 +-40.289050000000003 29.271699999999999 96.959999999999994 +-45.100000000000001 0 96.623999999999995 +-42.892650000000003 13.93665 96.623999999999995 +-26.5091 36.486649999999997 96.623999999999995 +-36.486649999999997 26.5091 96.623999999999995 +0 45.049999999999997 96.480000000000004 +-13.93665 42.892650000000003 96.623999999999995 +47.410150000000002 -15.404500000000001 96.864000000000004 +49.850000000000001 0 96.864000000000004 +42.845100000000002 -13.921200000000001 96.480000000000004 +45.049999999999997 0 96.480000000000004 +42.845100000000002 13.921200000000001 96.480000000000004 +47.410150000000002 15.404500000000001 96.864000000000004 +36.446199999999997 26.479749999999999 96.480000000000004 +0 45.100000000000001 96.719999999999999 +47.3626 -15.389049999999999 96.959999999999994 +45.100000000000001 0 96.623999999999995 +42.892650000000003 -13.93665 96.623999999999995 +49.799999999999997 0 96.959999999999994 +42.892650000000003 13.93665 96.623999999999995 +36.486649999999997 26.5091 96.623999999999995 +47.3626 15.389049999999999 96.959999999999994 +26.5091 36.486649999999997 96.623999999999995 +13.93665 42.892650000000003 96.623999999999995 +47.315049999999999 15.3736 97.103999999999999 +47.219949999999997 15.342700000000001 97.200000000000003 +40.167700000000004 29.18355 97.200000000000003 +29.18355 40.167700000000004 97.200000000000003 +15.342700000000001 47.219949999999997 97.200000000000003 +0 49.649999999999999 97.200000000000003 +-15.342700000000001 47.219949999999997 97.200000000000003 +-29.18355 40.167700000000004 97.200000000000003 +-40.167700000000004 29.18355 97.200000000000003 +-47.219949999999997 15.342700000000001 97.200000000000003 +-49.649999999999999 0 97.200000000000003 +-45.149999999999999 0 96.864000000000004 +-42.940199999999997 13.9521 96.864000000000004 +-26.538499999999999 36.527099999999997 96.864000000000004 +-36.527099999999997 26.538499999999999 96.864000000000004 +-13.9521 42.940199999999997 96.864000000000004 +13.93665 42.892650000000003 96.719999999999999 +0 45.149999999999999 96.864000000000004 +42.892650000000003 -13.93665 96.719999999999999 +45.100000000000001 0 96.719999999999999 +47.315049999999999 -15.3736 97.103999999999999 +42.892650000000003 13.93665 96.719999999999999 +49.75 0 97.103999999999999 +36.486649999999997 26.5091 96.719999999999999 +26.5091 36.486649999999997 96.719999999999999 +49.649999999999999 0 97.200000000000003 +49.600000000000001 0 97.296000000000006 +47.172400000000003 15.327249999999999 97.296000000000006 +40.127249999999997 29.154150000000001 97.296000000000006 +29.154150000000001 40.127249999999997 97.296000000000006 +15.327249999999999 47.172400000000003 97.296000000000006 +0 49.600000000000001 97.296000000000006 +-15.327249999999999 47.172400000000003 97.296000000000006 +-29.154150000000001 40.127249999999997 97.296000000000006 +-40.127249999999997 29.154150000000001 97.296000000000006 +-47.172400000000003 15.327249999999999 97.296000000000006 +-49.600000000000001 0 97.296000000000006 +-45.200000000000003 0 96.959999999999994 +-42.987749999999998 13.967549999999999 96.959999999999994 +-26.567900000000002 36.567549999999997 96.959999999999994 +-36.567549999999997 26.567900000000002 96.959999999999994 +-13.967549999999999 42.987749999999998 96.959999999999994 +13.9521 42.940199999999997 96.864000000000004 +0 45.200000000000003 96.959999999999994 +45.149999999999999 0 96.864000000000004 +42.940199999999997 -13.9521 96.864000000000004 +42.940199999999997 13.9521 96.864000000000004 +47.219949999999997 -15.342700000000001 97.200000000000003 +36.527099999999997 26.538499999999999 96.864000000000004 +26.538499999999999 36.527099999999997 96.864000000000004 +47.172400000000003 -15.327249999999999 97.296000000000006 +47.077300000000001 -15.29635 97.391999999999996 +49.5 0 97.391999999999996 +47.077300000000001 15.29635 97.391999999999996 +40.046349999999997 29.09535 97.391999999999996 +29.09535 40.046349999999997 97.391999999999996 +15.29635 47.077300000000001 97.391999999999996 +0 49.5 97.391999999999996 +-15.29635 47.077300000000001 97.391999999999996 +-29.09535 40.046349999999997 97.391999999999996 +-40.046349999999997 29.09535 97.391999999999996 +-47.077300000000001 15.29635 97.391999999999996 +-49.5 0 97.391999999999996 +-45.25 0 97.103999999999999 +-43.035299999999999 13.983000000000001 97.103999999999999 +-26.597300000000001 36.607999999999997 97.103999999999999 +-36.607999999999997 26.597300000000001 97.103999999999999 +-13.983000000000001 43.035299999999999 97.103999999999999 +13.967549999999999 42.987749999999998 96.959999999999994 +0 45.25 97.103999999999999 +45.200000000000003 0 96.959999999999994 +42.987749999999998 -13.967549999999999 96.959999999999994 +42.987749999999998 13.967549999999999 96.959999999999994 +-14.0139 43.130400000000002 97.200000000000003 +0 45.350000000000001 97.200000000000003 +45.25 0 97.103999999999999 +43.035299999999999 -13.983000000000001 97.103999999999999 +45.350000000000001 0 97.200000000000003 +43.130400000000002 -14.0139 97.200000000000003 +43.035299999999999 13.983000000000001 97.103999999999999 +45.549999999999997 0 97.488 +43.320599999999999 -14.075699999999999 97.488 +43.320599999999999 14.075699999999999 97.488 +-14.075699999999999 43.320599999999999 97.488 +36.850700000000003 26.773599999999998 97.488 +14.06025 43.273049999999998 97.391999999999996 +0 45.549999999999997 97.488 +26.744250000000001 36.810250000000003 97.391999999999996 +26.773599999999998 36.850700000000003 97.488 +46.554200000000002 -15.1264 97.920000000000002 +48.950000000000003 0 97.920000000000002 +46.554200000000002 15.1264 97.920000000000002 +39.601399999999998 28.772099999999998 97.920000000000002 +28.772099999999998 39.601399999999998 97.920000000000002 +15.1264 46.554200000000002 97.920000000000002 +0 48.950000000000003 97.920000000000002 +-15.1264 46.554200000000002 97.920000000000002 +-28.772099999999998 39.601399999999998 97.920000000000002 +-43.510849999999998 14.137549999999999 97.680000000000007 +45.75 0 97.680000000000007 +43.510849999999998 -14.137549999999999 97.680000000000007 +-26.891200000000001 37.012549999999997 97.680000000000007 +-37.012549999999997 26.891200000000001 97.680000000000007 +43.510849999999998 14.137549999999999 97.680000000000007 +37.012549999999997 26.891200000000001 97.680000000000007 +-14.137549999999999 43.510849999999998 97.680000000000007 +26.891200000000001 37.012549999999997 97.680000000000007 +14.075699999999999 43.320599999999999 97.488 +0 45.75 97.680000000000007 +14.137549999999999 43.510849999999998 97.680000000000007 +46.459099999999999 -15.095499999999999 98.016000000000005 +48.850000000000001 0 98.016000000000005 +46.459099999999999 15.095499999999999 98.016000000000005 +39.520499999999998 28.7133 98.016000000000005 +28.7133 39.520499999999998 98.016000000000005 +15.095499999999999 46.459099999999999 98.016000000000005 +0 48.850000000000001 98.016000000000005 +-15.095499999999999 46.459099999999999 98.016000000000005 +-28.7133 39.520499999999998 98.016000000000005 +-39.520499999999998 28.7133 98.016000000000005 +-46.459099999999999 15.095499999999999 98.016000000000005 +-48.850000000000001 0 98.016000000000005 +-45.850000000000001 0 97.775999999999996 +-43.60595 14.16845 97.775999999999996 +45.850000000000001 0 97.775999999999996 +43.60595 -14.16845 97.775999999999996 +43.60595 14.16845 97.775999999999996 +-26.949950000000001 37.093449999999997 97.775999999999996 +-37.093449999999997 26.949950000000001 97.775999999999996 +37.093449999999997 26.949950000000001 97.775999999999996 +26.949950000000001 37.093449999999997 97.775999999999996 +-14.16845 43.60595 97.775999999999996 +14.16845 43.60595 97.775999999999996 +0 45.850000000000001 97.775999999999996 +46.363999999999997 -15.0646 98.063999999999993 +48.75 0 98.063999999999993 +46.363999999999997 15.0646 98.063999999999993 +39.439599999999999 28.65455 98.063999999999993 +28.65455 39.439599999999999 98.063999999999993 +15.0646 46.363999999999997 98.063999999999993 +0 48.75 98.063999999999993 +-15.0646 46.363999999999997 98.063999999999993 +-28.65455 39.439599999999999 98.063999999999993 +-39.439599999999999 28.65455 98.063999999999993 +-46.363999999999997 15.0646 98.063999999999993 +-48.75 0 98.063999999999993 +-45.950000000000003 0 97.872 +45.950000000000003 0 97.872 +43.701050000000002 -14.199350000000001 97.872 +-43.701050000000002 14.199350000000001 97.872 +43.701050000000002 14.199350000000001 97.872 +37.174349999999997 27.008749999999999 97.872 +-27.008749999999999 37.174349999999997 97.872 +-37.174349999999997 27.008749999999999 97.872 +27.008749999999999 37.174349999999997 97.872 +14.199350000000001 43.701050000000002 97.872 +-14.199350000000001 43.701050000000002 97.872 +0 45.950000000000003 97.872 +46.268900000000002 -15.0337 98.159999999999997 +48.649999999999999 0 98.159999999999997 +46.268900000000002 15.0337 98.159999999999997 +39.358699999999999 28.595749999999999 98.159999999999997 +28.595749999999999 39.358699999999999 98.159999999999997 +15.0337 46.268900000000002 98.159999999999997 +0 48.649999999999999 98.159999999999997 +-15.0337 46.268900000000002 98.159999999999997 +-28.595749999999999 39.358699999999999 98.159999999999997 +-39.358699999999999 28.595749999999999 98.159999999999997 +-46.268900000000002 15.0337 98.159999999999997 +-48.649999999999999 0 98.159999999999997 +-46.049999999999997 0 97.920000000000002 +43.796149999999997 -14.23025 97.920000000000002 +14.23025 43.796149999999997 97.920000000000002 +0 46.049999999999997 97.920000000000002 +46.126249999999999 -14.987299999999999 98.207999999999998 +48.5 0 98.207999999999998 +28.5076 39.237299999999998 98.207999999999998 +-48.5 0 98.207999999999998 +14.5547 44.794750000000001 98.352000000000004 +0 47.100000000000001 98.352000000000004 +-14.5547 44.794750000000001 98.352000000000004 +-27.684699999999999 38.104700000000001 98.352000000000004 +-38.104700000000001 27.684699999999999 98.352000000000004 +-44.794750000000001 14.5547 98.352000000000004 +-47.100000000000001 0 98.352000000000004 +42.797550000000001 -13.905749999999999 95.760000000000005 +3 190 189 196 +3 169 119 174 +3 183 186 192 +3 194 191 184 +3 147 146 176 +3 168 173 159 +3 168 147 173 +3 179 178 185 +3 169 170 122 +3 181 182 171 +3 186 119 192 +3 180 181 171 +3 197 119 195 +3 189 119 197 +3 189 197 198 +3 192 193 183 +3 190 196 159 +3 174 175 169 +3 175 176 146 +3 146 169 175 +3 173 172 179 +3 187 188 177 +3 177 180 186 +3 172 171 182 +3 214 159 162 +3 201 202 160 +3 212 145 144 +3 206 207 200 +3 160 159 201 +3 160 140 158 +3 208 210 216 +3 164 199 203 +3 156 165 162 +3 201 205 200 +3 201 159 205 +3 207 143 167 +3 145 215 162 +3 165 143 162 +3 145 209 215 +3 205 206 200 +3 205 159 210 +3 215 214 162 +3 213 216 159 +3 144 204 212 +3 143 145 162 +3 203 165 164 +3 216 217 208 +3 213 159 214 +3 218 215 209 +3 217 218 208 +3 211 212 204 +3 210 211 204 +3 216 210 159 +3 209 208 218 +3 157 163 141 +3 157 158 161 +3 157 166 163 +3 140 142 158 +3 202 203 199 +3 199 160 202 +3 204 205 210 +3 220 227 219 +3 228 226 219 +3 152 229 151 +3 230 229 152 +3 152 153 230 +3 231 154 232 +3 232 154 484 +3 153 233 230 +3 153 6 233 +3 231 234 154 +3 154 234 12 +3 6 235 233 +3 236 235 4 +3 4 235 6 +3 234 237 12 +3 12 237 8 +3 4 7 236 +3 7 238 236 +3 7 29 238 +3 246 221 247 +3 222 248 221 +3 221 248 247 +3 222 223 248 +3 248 223 249 +3 223 224 249 +3 249 224 250 +3 251 225 226 +3 252 251 226 +3 266 267 239 +3 268 241 240 +3 267 268 240 +3 268 255 241 +3 241 255 242 +3 265 243 269 +3 243 244 269 +3 269 244 270 +3 244 271 270 +3 244 245 271 +3 245 272 271 +3 245 246 272 +3 272 246 273 +3 246 247 273 +3 273 247 274 +3 248 275 247 +3 277 276 252 +3 278 279 253 +3 254 278 253 +3 279 277 253 +3 256 278 254 +3 280 281 256 +3 256 255 268 +3 280 256 268 +3 281 278 256 +3 258 282 257 +3 283 282 258 +3 258 261 283 +3 260 284 259 +3 259 284 285 +3 262 259 286 +3 286 259 285 +3 261 287 283 +3 261 263 287 +3 264 262 288 +3 316 289 315 +3 292 317 291 +3 291 317 318 +3 293 319 290 +3 294 291 320 +3 320 291 318 +3 321 319 293 +3 293 296 321 +3 295 294 322 +3 322 294 320 +3 323 297 295 +3 322 323 295 +3 296 299 321 +3 315 298 297 +3 323 315 297 +3 321 299 324 +3 299 300 324 +3 324 300 325 +3 300 326 325 +3 300 301 326 +3 301 327 326 +3 328 327 302 +3 302 327 301 +3 302 303 328 +3 303 329 328 +3 303 304 329 +3 304 330 329 +3 304 305 330 +3 330 305 331 +3 305 306 331 +3 331 306 332 +3 307 333 306 +3 306 333 332 +3 307 308 333 +3 333 308 334 +3 308 309 334 +3 334 309 335 +3 311 336 310 +3 313 337 311 +3 337 336 311 +3 314 338 312 +3 338 339 313 +3 312 338 313 +3 339 337 313 +3 316 340 314 +3 340 338 314 +3 341 315 323 +3 341 316 315 +3 342 340 316 +3 342 316 341 +3 318 317 343 +3 343 317 344 +3 345 320 318 +3 343 345 318 +3 321 346 319 +3 347 322 320 +3 345 347 320 +3 324 346 321 +3 348 323 322 +3 347 348 322 +3 348 341 323 +3 324 349 346 +3 2 1 347 +3 1 367 348 +3 351 10 350 +3 9 10 351 +3 351 352 9 +3 352 13 9 +3 352 353 13 +3 353 14 13 +3 16 14 354 +3 354 14 353 +3 354 355 16 +3 355 18 16 +3 355 356 18 +3 356 19 18 +3 356 357 19 +3 19 357 21 +3 357 358 21 +3 21 358 23 +3 359 26 358 +3 358 26 23 +3 359 360 26 +3 26 360 27 +3 360 361 27 +3 27 361 28 +3 363 31 362 +3 365 30 363 +3 30 31 363 +3 366 37 364 +3 37 35 365 +3 364 37 365 +3 35 30 365 +3 368 372 366 +3 372 37 366 +3 5 367 1 +3 5 368 367 +3 373 372 368 +3 373 368 5 +3 369 370 374 +3 374 370 375 +3 374 375 376 +3 376 375 377 +3 378 371 369 +3 374 378 369 +3 3 2 371 +3 378 3 371 +3 13 15 11 +3 14 17 15 +3 23 26 25 +3 372 36 37 +3 373 59 372 +3 59 36 372 +3 376 377 62 +3 62 377 63 +3 72 5 0 +3 72 373 5 +3 66 59 373 +3 66 373 72 +3 81 378 374 +3 376 81 374 +3 69 81 376 +3 62 69 376 +3 80 3 378 +3 81 80 378 +3 73 0 3 +3 80 73 3 +3 73 72 0 +3 20 22 33 +3 33 22 113 +3 22 24 113 +3 113 24 115 +3 24 25 34 +3 115 24 34 +3 84 79 69 +3 118 38 137 +3 134 132 60 +3 64 65 379 +3 379 65 380 +3 381 68 64 +3 379 381 64 +3 75 382 67 +3 382 133 67 +3 383 84 68 +3 381 383 68 +3 384 71 85 +3 384 75 71 +3 385 382 75 +3 385 75 384 +3 386 85 79 +3 84 386 79 +3 387 386 84 +3 383 387 84 +3 386 384 85 +3 103 388 102 +3 389 388 103 +3 103 106 389 +3 106 390 389 +3 106 109 390 +3 109 391 390 +3 110 392 391 +3 109 110 391 +3 110 111 392 +3 111 393 392 +3 111 112 393 +3 112 394 393 +3 112 114 394 +3 394 114 395 +3 114 116 395 +3 395 116 396 +3 116 117 135 +3 396 116 135 +3 134 397 132 +3 397 139 132 +3 394 395 415 +3 415 395 416 +3 395 396 416 +3 416 396 417 +3 396 135 418 +3 417 396 418 +3 135 136 418 +3 418 136 419 +3 136 138 419 +3 419 138 420 +3 397 421 139 +3 401 422 397 +3 422 421 397 +3 398 399 423 +3 423 399 424 +3 425 402 398 +3 423 425 398 +3 404 426 400 +3 426 427 401 +3 400 426 401 +3 427 422 401 +3 428 403 402 +3 425 428 402 +3 429 405 403 +3 428 429 403 +3 407 430 404 +3 430 426 404 +3 431 408 405 +3 429 431 405 +3 408 407 406 +3 432 430 407 +3 432 407 408 +3 431 432 408 +3 410 433 409 +3 434 433 410 +3 410 411 434 +3 411 435 434 +3 411 412 435 +3 412 436 435 +3 413 437 436 +3 412 413 436 +3 413 414 437 +3 414 438 437 +3 414 415 438 +3 415 439 438 +3 415 416 439 +3 439 416 440 +3 416 417 440 +3 440 417 441 +3 417 418 442 +3 441 417 442 +3 418 419 442 +3 442 419 443 +3 419 420 443 +3 443 420 444 +3 422 445 421 +3 423 424 446 +3 446 424 447 +3 427 448 422 +3 448 445 422 +3 449 425 423 +3 446 449 423 +3 450 428 425 +3 449 450 425 +3 430 451 426 +3 451 452 427 +3 426 451 427 +3 452 448 427 +3 453 429 428 +3 450 453 428 +3 471 454 453 +3 54 471 453 +3 472 456 454 +3 471 472 454 +3 456 55 455 +3 55 57 455 +3 472 55 456 +3 458 473 457 +3 474 473 458 +3 458 459 474 +3 459 88 474 +3 459 460 88 +3 460 89 88 +3 461 475 89 +3 460 461 89 +3 461 462 475 +3 462 95 475 +3 462 463 95 +3 463 97 95 +3 463 464 97 +3 97 464 99 +3 464 465 99 +3 99 465 101 +3 465 466 104 +3 101 465 104 +3 466 467 104 +3 104 467 107 +3 467 468 107 +3 107 468 476 +3 51 52 469 +3 46 470 124 +3 124 470 121 +3 124 44 46 +3 44 48 45 +3 127 471 54 +3 53 127 54 +3 57 56 49 +3 58 50 49 +3 126 472 471 +3 127 126 471 +3 126 131 472 +3 472 131 55 +3 131 56 55 +3 107 476 108 +3 108 476 148 +3 474 150 473 +3 149 150 474 +3 474 88 149 +3 88 90 149 +3 475 96 91 +3 89 475 91 +3 475 95 96 +3 100 101 105 +3 124 121 120 +3 123 44 124 +3 94 32 92 +3 94 87 32 +3 39 477 130 +3 39 40 477 +3 40 478 477 +3 40 47 478 +3 478 47 479 +3 47 42 479 +3 479 42 480 +3 42 41 481 +3 480 42 481 +3 41 61 481 +3 481 61 482 +3 61 43 482 +3 482 43 483 +3 77 76 480 +3 482 483 74 +3 481 77 480 +3 481 482 77 +3 480 76 479 +3 76 78 479 +3 479 78 478 +3 478 82 477 +3 477 83 130 +3 130 86 128 +3 128 87 129 +3 129 94 155 +3 155 93 125 +3 93 98 125 +3 78 82 478 +3 82 83 477 +3 83 86 130 +3 86 87 128 +3 87 94 129 +3 94 93 155 +3 74 70 482 +3 70 77 482 + From 5ce78f2447a81c482e92387672271b2463b8b4a9 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 17 Sep 2026 10:07:10 +0200 Subject: [PATCH 110/151] after the flips there could be 2D CDTs with inverted triangles - avoid `locate`, - be ready for failures of `locate`. --- ...ing_constrained_Delaunay_triangulation_3.h | 69 ++++++++++--------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index f3e4261d6f3d..5858b54a112e 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -2310,12 +2310,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D bool maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(Vertex_handle v, Face_index face_id) { auto& mutable_cdt_2 = non_const_face_cdt_2(face_id); - typename CDT_2::Locate_type lt; - int i; - const auto fh_2d = mutable_cdt_2.locate(v->point(), lt, i); - CGAL_assertion(lt == CDT_2::VERTEX); - - const auto vh_2d = fh_2d->vertex(i); + const auto vh_2d = vertex_of_cdt_2_functor(mutable_cdt_2)(v); CGAL_assertion(vertex_3d(vh_2d) == v); auto face_circ = mutable_cdt_2.incident_faces(vh_2d); const auto done = face_circ; @@ -2377,6 +2372,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } set_facet_constrained(facet_c, face_id, next); set_facet_constrained(facet_d, face_id, current); + CGAL_assertion(dbg().move_Steiner_vertices_level() < 2 || mutable_cdt_2.tds().is_valid(true)); return true; } } while(face_circ != done); @@ -2388,6 +2384,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D remove_Steiner_vertex_from_cdt_2(Vertex_handle v, CDT_3_vertex_type vertex_type, Face_index face_id, + CDT_2_vertex_handle vh_2d, New_CDT_2_face_handles_output_iterator new_face_handles_out, Run_mode run_mode = NORMAL_RUN) { @@ -2401,16 +2398,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::unique_ptr cdt_2_copy_ptr{}; if(run_mode == DRY_RUN) { cdt_2_copy_ptr.reset(new CDT_2_base(original_mutable_cdt_2)); + auto vh_2d_index = std::distance(original_mutable_cdt_2.all_vertices_begin(), vh_2d); + vh_2d = std::next(cdt_2_copy_ptr->all_vertices_begin(), vh_2d_index); } auto& mutable_cdt_2 = (run_mode == DRY_RUN) ? *cdt_2_copy_ptr : original_mutable_cdt_2; const auto & cdt_2 = mutable_cdt_2; - typename CDT_2::Locate_type lt; - int i; - const auto fh_2d = cdt_2.locate(v->point(), lt, i); - CGAL_assertion(lt == CDT_2::VERTEX); - - const auto vh_2d = fh_2d->vertex(i); CGAL_assertion(vertex_3d(vh_2d) == v); if(debug) { @@ -2429,7 +2422,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D dump_face(face_id); std::cerr << " -> remove Steiner vertex " << display_vert(v) << " from CDT_2 of face F#" << face_id << " (nb of incident faces: " << nb_of_incident_faces << ")\n"; - if(!cdt_2.CT_2::is_valid(true, 3)) { + if(!cdt_2.tds().is_valid(true, 3)) { std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << display_vert(v) << " from face F#" << face_id << '\n'; CGAL_error_msg("Aborting due to invalid CDT_2"); @@ -2636,7 +2629,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D dump_face(face_id, std::string("dump_face_") + std::to_string(face_id) + "_after_removing_Steiner_vertex_" + std::to_string(vh_2d->time_stamp()) + ".off"); - if(!cdt_2.CT_2::is_valid(true, 3)) { + if(!cdt_2.tds().is_valid(true, 3)) { std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << with_point_and_info(v) << " from face F#" << face_id << '\n'; CGAL_error_msg("Aborting due to invalid CDT_2"); @@ -2653,9 +2646,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } auto indent_guards = CGAL::IO::make_indenting_guards(dbg().move_Steiner_vertices_level() > 1 ? "| " : "", std::cerr); + CGAL::unordered_flat_map face_to_cdt_2_vertex_for_v; const auto v_type = vertex_type(v); if(v_type == CDT_3_vertex_type::STEINER_IN_FACE) { const auto incident_face_id = v->ccdt_3_data().face_index(); + face_to_cdt_2_vertex_for_v.emplace(incident_face_id, + vertex_of_cdt_2_functor(non_const_face_cdt_2(incident_face_id))(v)); while(maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(v, incident_face_id)) {} } else { CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); @@ -2663,6 +2659,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(const auto& [_, incident_face_id] : CGAL::make_range(this->incident_faces_to_polyline.equal_range(incident_polyline_constraint_id))) { + face_to_cdt_2_vertex_for_v.emplace(incident_face_id, + vertex_of_cdt_2_functor(non_const_face_cdt_2(incident_face_id))(v)); while(maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(v, incident_face_id)) {} } } @@ -2712,7 +2710,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(v_type == CDT_3_vertex_type::STEINER_IN_FACE) { CGAL_assertion(star_components.component_incident_constraint_face_ids.size() == 2); [[maybe_unused]] const auto& [face_id, _] = star_components.face_ids_between_consecutive_components.front(); - remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, dry_run_output_iterator(face_id), DRY_RUN); + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, face_to_cdt_2_vertex_for_v[face_id], + dry_run_output_iterator(face_id), DRY_RUN); } else { CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); for(std::size_t component_index = 0, end = star_components.face_ids_between_consecutive_components.size(); @@ -2720,7 +2719,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D { const auto& [face_id, _] = star_components.face_ids_between_consecutive_components[component_index]; - remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, dry_run_output_iterator(face_id), DRY_RUN); + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, face_to_cdt_2_vertex_for_v[face_id], + dry_run_output_iterator(face_id), DRY_RUN); } } @@ -2819,7 +2819,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(star_components.component_incident_constraint_face_ids.size() == 2); const auto& [face_id, orientation] = star_components.face_ids_between_consecutive_components.front(); auto [new_output_iterator, half_hole_front_facet, second_half_hole_front_facet] = - remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, output_iterator); + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, face_to_cdt_2_vertex_for_v[face_id], output_iterator); output_iterator = new_output_iterator; } else { CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); @@ -2844,7 +2844,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D << (orientation == CGAL::NEGATIVE ? "negative" : "positive") << ")\n"; } auto [new_output_iterator, half_hole_facet_pair, second_half_hole_facet_pair] = - remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, output_iterator); + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, face_to_cdt_2_vertex_for_v[face_id], output_iterator); output_iterator = new_output_iterator; auto facet_pair_to_reconnect = half_hole_facet_pair; @@ -5250,31 +5250,38 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D bool reversed_orientation = false; }; - static auto vertex_of_cdt_2_functor(const CDT_2& cdt_2) { - return [&, hint = CDT_2_face_handle{}](const auto& p) mutable { + auto vertex_of_cdt_2_functor(const CDT_2& cdt_2) const { + return [&, hint = CDT_2_face_handle{}](const auto vh_3d) mutable { + const auto& p = tr().point(vh_3d); int i; typename CDT_2::Locate_type lt; const auto fh = cdt_2.locate(p, lt, i, hint); - if(lt != CDT_2::VERTEX) { - exception_ostream() << cdt_3_format("vertex_of_cdt_2_functor: point {} lt = {}\n", IO::oformat(p), int(lt)); - } - CGAL_assume(lt == CDT_2::VERTEX); hint = fh; - return fh->vertex(i); + if(lt == CDT_2::VERTEX) { + return fh->vertex(i); + } + else { + // the CDT_2 might have been invalidated by flips (valid TDS_2 but invalid CDT_2) + auto it = std::find_if(cdt_2.finite_vertex_handles().begin(), + cdt_2.finite_vertex_handles().end(), + [&](const auto& vh) { return vertex_3d(vh) == vh_3d; }); + CGAL_assertion(it != cdt_2.finite_vertex_handles().end()); + return *it; + } }; } template - static auto facet_is_facet_of_cdt_2(const Tr& tr, typename Tr::Facet f, const CDT_2& cdt_2) + auto facet_is_facet_of_cdt_2(const Tr& tr, typename Tr::Facet f, const CDT_2& cdt_2) const -> std::optional { const auto [v0, v1, v2] = tr.vertices(f); auto v = vertex_of_cdt_2_functor(cdt_2); try { - const auto cdt_2_v0 = v(tr.point(v0)); - const auto cdt_2_v1 = v(tr.point(v1)); - const auto cdt_2_v2 = v(tr.point(v2)); + const auto cdt_2_v0 = v(v0); + const auto cdt_2_v1 = v(v1); + const auto cdt_2_v2 = v(v2); CDT_2_face_handle fh; const bool is_face = cdt_2.is_face(cdt_2_v0, cdt_2_v1, cdt_2_v2, fh); @@ -5365,8 +5372,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D { auto v = vertex_of_cdt_2_functor(cdt_2); - const auto cdt_2_v0 = v(this->point(va)); - const auto cdt_2_v1 = v(this->point(vb)); + const auto cdt_2_v0 = v(va); + const auto cdt_2_v1 = v(vb); CDT_2_face_handle fh; int edge_index; const bool is_edge = cdt_2.is_edge(cdt_2_v0, cdt_2_v1, fh, edge_index); From 9ed8d44702e1a1f75ca755cf8751a8d58ea6c5d5 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 17 Sep 2026 16:42:48 +0200 Subject: [PATCH 111/151] add helper functions for orientation --- .../internal/tetrahedral_remeshing_helpers.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index 79eada1496cf..bef60f5ac439 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -698,6 +698,17 @@ bool is_well_oriented(const Tr& tr, const typename Tr::Cell_handle ch) ch->vertex(3)); } +template +bool are_well_oriented(const Tr& tr, const CellRange& cells) +{ + for(const auto& cell : cells) + { + if(!is_well_oriented(tr, cell)) + return false; + } + return true; +} + template bool is_boundary(const C3T3& c3t3, const typename C3T3::Facet& f, @@ -2231,6 +2242,11 @@ void count_far_points(const C3t3& c3t3) template bool are_cell_orientations_valid(const Tr& tr) { + // skip the test if we know + // that the input triangulation already has inverted cells + if(tr.may_have_badly_oriented_cells()) + return true; + typedef typename Tr::Geom_traits::Point_3 Point_3; typedef typename Tr::Facet Facet; From e3d586bbb4521ffc3816b761fbda08ef8dadcbf8 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 17 Sep 2026 17:37:10 +0200 Subject: [PATCH 112/151] deal with negative cells in collapse --- .../internal/collapse_short_edges.h | 74 +++++++++++-------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index 831e0e94eafe..146137d20e82 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -529,29 +529,34 @@ bool is_valid_collapse(const typename C3t3::Edge& edge, c3t3.triangulation().finite_incident_cells(v0, std::back_inserter(cells_to_check)); - for (const Cell_handle& ch : cells_to_check) + const bool valid_orientation_before = are_well_oriented(c3t3.triangulation(), cells_to_check); + if(valid_orientation_before) { - if (!ch->has_vertex(v1)) + for(const Cell_handle& ch : cells_to_check) { - //check orientation - std::array pts = { ch->vertex(0)->point(), + if(!ch->has_vertex(v1)) + { + // check orientation + std::array pts = { ch->vertex(0)->point(), ch->vertex(1)->point(), ch->vertex(2)->point(), ch->vertex(3)->point()}; - pts[ch->index(v0)] = new_pos; - if (CGAL::orientation(point(pts[0]), point(pts[1]), point(pts[2]), point(pts[3])) - != CGAL::POSITIVE) - { -#ifdef CGAL_DEBUG_TET_REMESHING_IN_PLUGIN - if (in_cx) + pts[ch->index(v0)] = new_pos; + const auto orientation_after = orientation(pts); + + if(orientation_after != CGAL::POSITIVE) { - if (collapse_type == TO_MIDPOINT) - nb_orientation_midpoint++; - else - nb_orientation_v1++; - } +#ifdef CGAL_DEBUG_TET_REMESHING_IN_PLUGIN + if (in_cx) + { + if (collapse_type == TO_MIDPOINT) + nb_orientation_midpoint++; + else + nb_orientation_v1++; + } #endif - return false; + return false; + } } } } @@ -562,29 +567,34 @@ bool is_valid_collapse(const typename C3t3::Edge& edge, c3t3.triangulation().finite_incident_cells(v1, std::back_inserter(cells_to_check)); - for (const Cell_handle& ch : cells_to_check) + const bool valid_orientation_before = are_well_oriented(c3t3.triangulation(), cells_to_check); + if(valid_orientation_before) { - if (!ch->has_vertex(v0)) + for (const Cell_handle& ch : cells_to_check) { - //check orientation - std::array pts = { ch->vertex(0)->point(), + if (!ch->has_vertex(v0)) + { + //check orientation + std::array pts = { ch->vertex(0)->point(), ch->vertex(1)->point(), ch->vertex(2)->point(), ch->vertex(3)->point() }; - pts[ch->index(v1)] = new_pos; - if (CGAL::orientation(point(pts[0]), point(pts[1]), point(pts[2]), point(pts[3])) - != CGAL::POSITIVE) - { -#ifdef CGAL_DEBUG_TET_REMESHING_IN_PLUGIN - if (in_cx) + pts[ch->index(v1)] = new_pos; + + const auto orientation_after = orientation(pts); + if(orientation_after != CGAL::POSITIVE) { - if (collapse_type == TO_MIDPOINT) - nb_orientation_midpoint++; - else - nb_orientation_v0++; - } +#ifdef CGAL_DEBUG_TET_REMESHING_IN_PLUGIN + if (in_cx) + { + if (collapse_type == TO_MIDPOINT) + nb_orientation_midpoint++; + else + nb_orientation_v0++; + } #endif - return false; + return false; + } } } } From 0ddc8912b4b41459df2f6d511f5de0f87e565c8e Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 17 Sep 2026 17:37:53 +0200 Subject: [PATCH 113/151] debug code for negative cells --- .../include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h index ed33ce8a7145..ef1cde8f9cd6 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h @@ -1988,7 +1988,8 @@ void flip_edges(C3T3& c3t3, vertices_subdomain_indices); #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG - if(!debug::are_cell_orientations_valid(c3t3.triangulation())) + if( !tr.may_have_badly_oriented_cells() + && !debug::are_cell_orientations_valid(c3t3.triangulation())) std::cerr << "ERROR in ORIENTATION" << std::endl; #endif From 24981a8ef066f25e33692116488da61bb488f185 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 18 Sep 2026 10:13:33 +0200 Subject: [PATCH 114/151] deal with negative orientation of input in split --- .../internal/split_long_edges.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h index 8a53ed2ea9e4..c9e360bce514 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h @@ -40,10 +40,10 @@ namespace internal { template -bool positive_orientation_after_edge_split(const typename C3t3::Edge& e, +bool valid_orientation_after_edge_split(const typename C3t3::Edge& e, const typename C3t3::Cell_handle circ, const typename C3t3::Triangulation::Geom_traits::Point_3& steiner, - const C3t3&) + const C3t3& c3t3) { using Point = typename C3t3::Triangulation::Geom_traits::Point_3; @@ -54,6 +54,13 @@ bool positive_orientation_after_edge_split(const typename C3t3::Edge& e, point(circ->vertex(1)->point()), point(circ->vertex(2)->point()), point(circ->vertex(3)->point())}; + + // if the orientation is already bad before split, do not bother checking + // orientation after split, and return true straight away + if(c3t3.triangulation().may_have_badly_oriented_cells() + && CGAL::orientation(pts[0], pts[1], pts[2], pts[3]) != CGAL::POSITIVE) + return true; + // 1st half-cell const int i1 = circ->index(v1); const Point p1 = pts[i1]; @@ -103,7 +110,7 @@ construct_steiner_point(const typename C3t3::Edge& e, do { Cell_handle c = circ; - if(!positive_orientation_after_edge_split(e, c, steiner, c3t3)) + if(!valid_orientation_after_edge_split(e, c, steiner, c3t3)) { steiner_successful = false; break; @@ -184,7 +191,7 @@ typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e, } const Cell_handle c = circ; - if(!positive_orientation_after_edge_split(e, c, m, c3t3)) + if(!valid_orientation_after_edge_split(e, c, m, c3t3)) { const std::optional steiner = construct_steiner_point(e, c3t3); if (steiner != std::nullopt) From 9b3f06b9ee03fa0c35215dfbc0a9ea4149707747 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 18 Sep 2026 11:08:20 +0200 Subject: [PATCH 115/151] change the logic, using tr.may_have_badly_oriented_cells() If there are some badly oriented cells locally but it is already known, don't check orientation a posteriori Otherwise, check that no inversion happened, as before --- .../internal/collapse_short_edges.h | 6 ++---- .../internal/smooth_vertices.h | 11 +++++------ .../internal/tetrahedral_remeshing_helpers.h | 19 +++++++++++++++---- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index 146137d20e82..9bdddc7eb57f 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -529,8 +529,7 @@ bool is_valid_collapse(const typename C3t3::Edge& edge, c3t3.triangulation().finite_incident_cells(v0, std::back_inserter(cells_to_check)); - const bool valid_orientation_before = are_well_oriented(c3t3.triangulation(), cells_to_check); - if(valid_orientation_before) + if(need_to_check_orientation_after_change(c3t3.triangulation(), cells_to_check)) { for(const Cell_handle& ch : cells_to_check) { @@ -567,8 +566,7 @@ bool is_valid_collapse(const typename C3t3::Edge& edge, c3t3.triangulation().finite_incident_cells(v1, std::back_inserter(cells_to_check)); - const bool valid_orientation_before = are_well_oriented(c3t3.triangulation(), cells_to_check); - if(valid_orientation_before) + if(need_to_check_orientation_after_change(c3t3.triangulation(), cells_to_check)) { for (const Cell_handle& ch : cells_to_check) { diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h index 47512f50f59e..adf26d26e798 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h @@ -496,8 +496,6 @@ class Tetrahedral_remeshing_smoother const typename Tr::Point backup = v->point(); //backup v's position const typename Tr::Geom_traits::Point_3 pv = point(backup); - bool valid_orientation = false; - bool angles_improved = true; double frac = 1.0; typename Tr::Geom_traits::Vector_3 move(pv, final_pos); @@ -505,7 +503,11 @@ class Tetrahedral_remeshing_smoother ? max_cosine(tr, inc_cells) : Dihedral_angle_cosine(CGAL::ZERO, 0., 1.);//Dummy unused value + const bool check_orientation = need_to_check_orientation_after_change(tr, inc_cells); + bool valid_try = true; + bool valid_orientation = true; + bool angles_improved = true; do { v->set_point(typename Tr::Point(pv + frac * move)); @@ -516,10 +518,7 @@ class Tetrahedral_remeshing_smoother for (const typename Tr::Cell_handle& ci : inc_cells) { - if (CGAL::POSITIVE != CGAL::orientation(point(ci->vertex(0)->point()), - point(ci->vertex(1)->point()), - point(ci->vertex(2)->point()), - point(ci->vertex(3)->point()))) + if(check_orientation && !is_well_oriented(tr, ci)) { frac = 0.5 * frac; valid_try = false; diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index bef60f5ac439..8f19897bb76f 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -699,13 +699,24 @@ bool is_well_oriented(const Tr& tr, const typename Tr::Cell_handle ch) } template -bool are_well_oriented(const Tr& tr, const CellRange& cells) +bool need_to_check_orientation_after_change(const Tr& tr, const CellRange& cells) { - for(const auto& cell : cells) + for (const auto& cell : cells) { - if(!is_well_oriented(tr, cell)) - return false; + if (!is_well_oriented(tr, cell)) + { + // if a cell is badly oriented, but the triangulation is known + // to have badly oriented cells, then no need to re-check orientation + // after change. It will remain bad + if(tr.may_have_badly_oriented_cells()) + return false; + else + CGAL_assertion(false);//this function is called before the change, + //so this point should not be reached + } } + + // all cells are positively oriented return true; } From 3fa70b284b24eb6c5f3445a24f2177c09dff7609 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 18 Sep 2026 11:27:02 +0200 Subject: [PATCH 116/151] add a test for negative input to tet remeshing --- ...t_tetrahedral_remeshing_from_mesh_file.cpp | 86 +++++++++++++++++-- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/Tetrahedral_remeshing/test/Tetrahedral_remeshing/test_tetrahedral_remeshing_from_mesh_file.cpp b/Tetrahedral_remeshing/test/Tetrahedral_remeshing/test_tetrahedral_remeshing_from_mesh_file.cpp index 30c83e1c5334..45fa0c39d21b 100644 --- a/Tetrahedral_remeshing/test/Tetrahedral_remeshing/test_tetrahedral_remeshing_from_mesh_file.cpp +++ b/Tetrahedral_remeshing/test/Tetrahedral_remeshing/test_tetrahedral_remeshing_from_mesh_file.cpp @@ -7,26 +7,96 @@ #include #include +#include +#include + #include #include -#include - typedef CGAL::Exact_predicates_inexact_constructions_kernel K; typedef CGAL::Tetrahedral_remeshing::Remeshing_triangulation_3 Remeshing_triangulation; +typedef Remeshing_triangulation Tr; -int main(int argc, char* argv[]) + +int test(const std::string& filename, const bool perturb_vertices_positions = false) { - const double target_edge_length = (argc > 1) ? atof(argv[1]) : 0.1; + const double target_edge_length = 0.05; - Remeshing_triangulation tr; + std::ifstream in(filename); - std::ifstream in("data/sphere.mesh"); - if (CGAL::SMDS_3::build_triangulation_from_file(in, tr)) + Remeshing_triangulation tr; + if(CGAL::SMDS_3::build_triangulation_from_file(in, tr, + false /*verbose*/, + false /*replace_domain_0*/, + false /*allow_non_manifold*/, + true /*allow_negative_orientation*/)) std::cout << "build triangulation ok" << std::endl; + else + return EXIT_FAILURE; + + if (perturb_vertices_positions) + { + std::cout << "CGAL Random seed = " << CGAL::get_default_random().get_seed() << std::endl; + using P = Remeshing_triangulation::Point; + const int percentage = 10;// 1/percentage = fraction of perturbed vertices + const double max_move = 0.1 * target_edge_length; + + CGAL::Random rng; + int vid = 0; + for (auto v : tr.finite_vertex_handles()) + { + if(++vid % percentage != 0) + continue; + + const P& pv = v->point(); + v->set_point(P{pv.x() + rng.get_double(-1., 1.) * max_move, + pv.y() + rng.get_double(-1., 1.) * max_move, + pv.z() + rng.get_double(-1., 1.) * max_move}); + } + } + + auto count_negative_cells = [&](const Tr& tr) -> unsigned int + { + return std::count_if(tr.finite_cells_begin(), + tr.finite_cells_end(), + [](const auto& c) + { + return CGAL::POSITIVE != CGAL::orientation(c.vertex(0)->point(), + c.vertex(1)->point(), + c.vertex(2)->point(), + c.vertex(3)->point()); + }); + }; + + unsigned int neg = count_negative_cells(tr); + if(neg > 0) + tr.may_have_badly_oriented_cells(true); + std::cout << neg << " finite cells are badly oriented before remeshing" << std::endl; + + CGAL::tetrahedral_isotropic_remeshing(tr, target_edge_length, + CGAL::parameters::number_of_iterations(5) + .remesh_boundaries(false)); + + unsigned int neg2 = count_negative_cells(tr); + if(neg2 > 0) + std::cout << neg2 << " finite cells are badly oriented after remeshing" << std::endl; + + in.close(); + return EXIT_SUCCESS; +} + +int main(int argc, char* argv[]) +{ + std::cout << "Run remeshing from elephant.mesh" << std::endl; + const std::string file1 = CGAL::data_file_path("meshes/elephant.mesh"); + if(test(file1) != EXIT_SUCCESS) + return EXIT_FAILURE; - CGAL::tetrahedral_isotropic_remeshing(tr, target_edge_length); + std::cout << "\n\nRun remeshing from perturbed elephant.mesh" << std::endl; + const std::string file2 = CGAL::data_file_path("meshes/elephant.mesh"); + if(test(file2, true) != EXIT_SUCCESS) + return EXIT_FAILURE; return EXIT_SUCCESS; } From 99cc0c2427e8af587b128cedad303308436d3110 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 18 Sep 2026 15:22:16 +0200 Subject: [PATCH 117/151] check orientation only when it makes sense wrt may_have_badly_oriented_cells() --- .../internal/collapse_short_edges.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index 9bdddc7eb57f..a32b9ac53431 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -477,7 +477,8 @@ template bool collapse_keeps_orientations(const CellRange& star, const typename C3t3::Vertex_handle v_moved, const typename C3t3::Vertex_handle v_other, - const typename C3t3::Triangulation::Geom_traits::Point_3& new_pos) + const typename C3t3::Triangulation::Geom_traits::Point_3& new_pos, + const C3t3& c3t3) { typedef typename C3t3::Triangulation::Geom_traits::Point_3 Point; @@ -490,6 +491,11 @@ bool collapse_keeps_orientations(const CellRange& star, point(ch->vertex(1)->point()), point(ch->vertex(2)->point()), point(ch->vertex(3)->point()) }; + + if(c3t3.triangulation().may_have_badly_oriented_cells() && + CGAL::orientation(pts[0], pts[1], pts[2], pts[3]) != CGAL::POSITIVE) + return true; //in this case we don't care about orientation change, locally + pts[ch->index(v_moved)] = new_pos; if (CGAL::orientation(pts[0], pts[1], pts[2], pts[3]) != CGAL::POSITIVE) return false; @@ -1314,10 +1320,10 @@ typename C3t3::Vertex_handle collapse_edge(typename C3t3::Edge& edge, const auto orientations_ok = [&](const Collapse_type ct, const Point& pos) { if ((ct == TO_V1 || ct == TO_MIDPOINT) - && !collapse_keeps_orientations(star_of_v0(), v0, v1, point(pos))) + && !collapse_keeps_orientations(star_of_v0(), v0, v1, point(pos), c3t3)) return false; if ((ct == TO_V0 || ct == TO_MIDPOINT) - && !collapse_keeps_orientations(star_of_v1(), v1, v0, point(pos))) + && !collapse_keeps_orientations(star_of_v1(), v1, v0, point(pos), c3t3)) return false; return true; }; From aa798757723a48e4f5142472f69f61fcee9c8d5e Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 18 Sep 2026 15:43:14 +0200 Subject: [PATCH 118/151] this function has become useless --- .../internal/collapse_short_edges.h | 104 ------------------ 1 file changed, 104 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index a32b9ac53431..54d950e00722 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -503,110 +503,6 @@ bool collapse_keeps_orientations(const CellRange& star, return true; } -template -bool is_valid_collapse(const typename C3t3::Edge& edge, - const Collapse_type& collapse_type, - const typename C3t3::Triangulation::Point& new_pos, - const C3t3& c3t3) -{ - typedef typename C3t3::Vertex_handle Vertex_handle; - typedef typename C3t3::Cell_handle Cell_handle; - typedef typename C3t3::Triangulation::Point Point; - - const Vertex_handle v0 = edge.first->vertex(edge.second); - const Vertex_handle v1 = edge.first->vertex(edge.third); - -#ifdef CGAL_DEBUG_TET_REMESHING_IN_PLUGIN - const bool in_cx = c3t3.is_in_complex(edge); - if (in_cx) - { - if (collapse_type == TO_MIDPOINT) - nb_test_midpoint++; - else if (collapse_type == TO_V1) - nb_test_v1++; - else - nb_test_v0++; - } -#endif - - if (collapse_type == TO_V1 || collapse_type == TO_MIDPOINT) - { - std::vector cells_to_check; - c3t3.triangulation().finite_incident_cells(v0, - std::back_inserter(cells_to_check)); - - if(need_to_check_orientation_after_change(c3t3.triangulation(), cells_to_check)) - { - for(const Cell_handle& ch : cells_to_check) - { - if(!ch->has_vertex(v1)) - { - // check orientation - std::array pts = { ch->vertex(0)->point(), - ch->vertex(1)->point(), - ch->vertex(2)->point(), - ch->vertex(3)->point()}; - pts[ch->index(v0)] = new_pos; - const auto orientation_after = orientation(pts); - - if(orientation_after != CGAL::POSITIVE) - { -#ifdef CGAL_DEBUG_TET_REMESHING_IN_PLUGIN - if (in_cx) - { - if (collapse_type == TO_MIDPOINT) - nb_orientation_midpoint++; - else - nb_orientation_v1++; - } -#endif - return false; - } - } - } - } - } - if (collapse_type == TO_V0 || collapse_type == TO_MIDPOINT) - { - std::vector cells_to_check; - c3t3.triangulation().finite_incident_cells(v1, - std::back_inserter(cells_to_check)); - - if(need_to_check_orientation_after_change(c3t3.triangulation(), cells_to_check)) - { - for (const Cell_handle& ch : cells_to_check) - { - if (!ch->has_vertex(v0)) - { - //check orientation - std::array pts = { ch->vertex(0)->point(), - ch->vertex(1)->point(), - ch->vertex(2)->point(), - ch->vertex(3)->point() }; - pts[ch->index(v1)] = new_pos; - - const auto orientation_after = orientation(pts); - if(orientation_after != CGAL::POSITIVE) - { -#ifdef CGAL_DEBUG_TET_REMESHING_IN_PLUGIN - if (in_cx) - { - if (collapse_type == TO_MIDPOINT) - nb_orientation_midpoint++; - else - nb_orientation_v0++; - } -#endif - return false; - } - } - } - } - } - - return is_valid_collapse(edge, c3t3); -} - template bool facet_has_edge(const Facet& f, const Vh v0, const Vh v1) { From ac467274165c7eb2a61bdc070ba3315c21f11b35 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 18 Sep 2026 16:00:04 +0200 Subject: [PATCH 119/151] remove assertion, add comments --- .../internal/tetrahedral_remeshing_helpers.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index 8f19897bb76f..edb6622be33e 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -287,8 +287,7 @@ Dihedral_angle_cosine cos_dihedral_angle(const typename Gt::Point_3& i, const typename Gt::Point_3& l, const Gt& gt) { - CGAL_expensive_assertion(CGAL::orientation(i, j, k, l) != CGAL::NEGATIVE); - + //valid however orientation(i,j,k,l) is positive or negative typename Gt::Construct_cross_product_vector_3 cross_product = gt.construct_cross_product_vector_3_object(); typename Gt::Compute_scalar_product_3 scalar_product = @@ -365,7 +364,8 @@ Dihedral_angle_cosine max_cos_dihedral_angle(const Point& p, const Vector_3 ps = vector(p, s); const Vector_3 pr = vector(p, r); - //compute normals pointing outside tetrahedron + // compute normals pointing outside tetrahedron if orientation(p,q,r,s) is POSITIVE + // and inside tetrahedron otherwise const Vector_3 n_pqr = cross(qp, qr); if (CGAL::NULL_VECTOR == n_pqr) return Dihedral_angle_cosine(CGAL::POSITIVE, 1., 1.); From be58f5c77b8cd7e5125bcf40f8276aaac5ef35c2 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 18 Sep 2026 17:01:16 +0200 Subject: [PATCH 120/151] do not restore markers for deleted cells --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 5858b54a112e..b9e97513ba94 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -5214,6 +5214,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto restore_markers = [&](Facet outside_facet) { const auto [outside_cell, outside_face_index] = outside_facet; + if(cells_to_remove.count(outside_cell) > 0) { + return; + } const auto mirror_facet = tr().mirror_facet(outside_facet); if(outside_cell->ccdt_3_data().is_facet_constrained(outside_face_index)) { const auto polygon_id = outside_cell->ccdt_3_data().face_constraint_index(outside_face_index); From 0f6dd299d787e5c75c44b4ec9ea0eaaa18c629b6 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 22 Sep 2026 17:52:37 +0200 Subject: [PATCH 121/151] fix bug12.off --- ...ing_constrained_Delaunay_triangulation_3.h | 10 + .../CMakeLists.txt | 1 + .../data/bug12.off | 1443 +++++++++++++++++ 3 files changed, 1454 insertions(+) create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug12.off diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index b9e97513ba94..893c9f09b6a1 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -3013,6 +3013,16 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D fh_2d->set_constraint(edge_index, false); mirror_fh_2d->set_constraint(mirror_edge_index, false); + std::vector faces_in_conflict; + non_const_cdt_2.get_conflicts(point, std::back_inserter(faces_in_conflict), fh_2d); + for(auto fh : faces_in_conflict) { + if(fh->info().is_outside_the_face != OUTSIDE) { + auto facet_3d = fh->info().facet_3d; + if(facet_3d.first != Cell_handle{}) { + self->set_facet_as_not_constrained(facet_3d); + } + } + } const auto v_Steiner_2d = non_const_cdt_2.insert(point, fh_2d); vertex_3d(v_Steiner_2d) = v_Steiner; non_const_cdt_2.insert_constraint(va_2d, v_Steiner_2d); diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index 3517c9fb9eb9..e30151e36d16 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -121,6 +121,7 @@ CGAL_add_cdt3_from_local_off_test(bug7 OPTIONS --read-mesh-with-operator) CGAL_add_cdt3_from_local_off_test(bug-segfault OPTIONS --read-mesh-with-operator) CGAL_add_cdt3_from_local_off_test(bug8 OPTIONS --read-mesh-with-operator) CGAL_add_cdt3_from_local_off_test(bug9 OPTIONS --read-mesh-with-operator) +CGAL_add_cdt3_from_local_off_test(bug12 OPTIONS --read-mesh-with-operator) CGAL_add_cdt3_from_local_off_test(bug-fixed --move-Steiner-vertices-to-the-volume) CGAL_add_cdt3_from_local_off_test(bug3-fixed --move-Steiner-vertices-to-the-volume) CGAL_add_cdt3_from_local_off_test(bug4-fixed --move-Steiner-vertices-to-the-volume) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug12.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug12.off new file mode 100644 index 000000000000..46cfa956f364 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug12.off @@ -0,0 +1,1443 @@ +OFF +1009 430 0 + +26.567900000000002 36.567549999999997 96.959999999999994 +-48.307125999999997 10.688492 91.996151999999995 +-50 0 96.239999999999995 +-47.552849999999999 15.450850000000001 96.239999999999995 +36.877704999999999 25.524087000000002 86.799999999999997 +43.429470999999999 9.9159480000000002 23.853847999999999 +44.368076000000002 3.9898180000000001 17.746151999999999 +44.368076000000002 3.9898180000000001 23.853847999999999 +-27.740373000000002 41.290998000000002 95 +-29.389250000000001 40.450850000000003 96.239999999999995 +-15.450850000000001 47.552849999999999 96.239999999999995 +-50 0 0 +-49.715021999999998 1.7992950000000001 20.800000000000001 +38.239677999999998 22.851068000000001 81.603847999999999 +29.359850000000002 40.410400000000003 96.384 +29.359850000000002 40.410400000000003 96.480000000000004 +40.410400000000003 29.359850000000002 96.384 +-25.093077000000001 42.639868999999997 40.353847999999999 +-19.747043000000001 45.363821000000002 34.246152000000002 +13.983000000000001 43.035299999999999 97.103999999999999 +-48.307125999999997 10.688492 25.996151999999999 +-47.837826999999997 13.651558 20.800000000000001 +40.410400000000003 29.359850000000002 96.480000000000004 +-29.389250000000001 40.450850000000003 0 +-27.766093999999999 41.277892999999999 12.550000000000001 +-49.245724000000003 4.7623610000000003 15.603847999999999 +45.363821000000002 19.747043000000001 91.996151999999995 +15.4354 47.505249999999997 96.384 +15.4354 47.505249999999997 96.480000000000004 +-15.450850000000001 47.552849999999999 0 +-19.747043000000001 45.363821000000002 7.3538480000000002 +-47.901049 13.252389000000001 5 +-47.552849999999999 15.450850000000001 0 +-25.093077000000001 42.639868999999997 7.3538480000000002 +-49.245724000000003 4.7623610000000003 32.103847999999999 +-49.245724000000003 4.7623610000000003 25.996151999999999 +-49.715021999999998 1.7992950000000001 37.299999999999997 +46.725797 17.074024999999999 86.799999999999997 +42.325595999999997 14.832011 86.799999999999997 +36.877704999999999 25.524087000000002 20.800000000000001 +36.405749999999998 26.45035 95.760000000000005 +36.405749999999998 26.45035 4.7999999999999998 +-48.307125999999997 10.688492 15.603847999999999 +36.877704999999999 25.524087000000002 37.299999999999997 +-19.747043000000001 45.363821000000002 17.746151999999999 +38.239677999999998 22.851068000000001 15.603847999999999 +36.877704999999999 25.524087000000002 70.299999999999997 +49.450000000000003 0 97.488 +40.963622999999998 17.505030000000001 32.103847999999999 +42.325595999999997 14.832011 20.800000000000001 +42.325595999999997 14.832011 37.299999999999997 +49.25 0 97.680000000000007 +42.797550000000001 13.905749999999999 95.760000000000005 +-27.766093999999999 41.277892999999999 78.549999999999997 +-25.093077000000001 42.639868999999997 67.246151999999995 +47.02975 15.280900000000001 97.488 +-47.837826999999997 13.651558 53.799999999999997 +45.363821000000002 19.747043000000001 9.4961520000000004 +40.963622999999998 17.505030000000001 9.4961520000000004 +40.963622999999998 17.505030000000001 15.603847999999999 +-27.766093999999999 41.277892999999999 29.050000000000001 +40.005899999999997 29.065999999999999 97.488 +46.839550000000003 15.219099999999999 97.680000000000007 +-47.837826999999997 13.651558 37.299999999999997 +41.461371 27.405999000000001 5 +38.239677999999998 22.851068000000001 9.4961520000000004 +38.239677999999998 22.851068000000001 25.996151999999999 +38.239677999999998 22.851068000000001 32.103847999999999 +-25.093077000000001 42.639868999999997 23.853847999999999 +29.065999999999999 40.005899999999997 97.488 +39.844099999999997 28.948399999999999 97.680000000000007 +-49.245724000000003 4.7623610000000003 58.996152000000002 +46.542318999999999 17.434121000000001 5 +42.142118000000004 15.192107 5 +36.877704999999999 25.524087000000002 53.799999999999997 +46.75 0 98.304000000000002 +44.319249999999997 -14.4002 98.256 +44.4619 -14.44655 98.304000000000002 +-25.093077000000001 42.639868999999997 73.353847999999999 +-19.747043000000001 45.363821000000002 67.246151999999995 +-48.307125999999997 10.688492 48.603847999999999 +-49.245724000000003 4.7623610000000003 42.496152000000002 +44.4619 14.44655 98.304000000000002 +44.319249999999997 14.4002 98.256 +46.600000000000001 0 98.256 +-49.715021999999998 1.7992950000000001 53.799999999999997 +40.963622999999998 17.505030000000001 25.996151999999999 +15.219099999999999 46.839550000000003 97.680000000000007 +37.061183 25.163990999999999 5 +42.325595999999997 14.832011 53.799999999999997 +40.963622999999998 17.505030000000001 42.496152000000002 +37.821550000000002 27.478950000000001 98.304000000000002 +37.700200000000002 27.390799999999999 98.256 +-49.715021999999998 1.7992950000000001 70.299999999999997 +29.389250000000001 40.450850000000003 96.239999999999995 +15.450850000000001 47.552849999999999 96.239999999999995 +0 49.25 97.680000000000007 +-48.307125999999997 10.688492 42.496152000000002 +33.549370000000003 29.306730000000002 17.746151999999999 +32.798729999999999 37.041370000000001 17.746151999999999 +27.478950000000001 37.821550000000002 98.304000000000002 +27.390799999999999 37.700200000000002 98.256 +-25.093077000000001 42.639868999999997 17.746151999999999 +-15.280900000000001 47.02975 97.488 +29.306730000000002 33.549370000000003 17.746151999999999 +38.239677999999998 22.851068000000001 42.496152000000002 +38.239677999999998 22.851068000000001 48.603847999999999 +-19.747043000000001 45.363821000000002 40.353847999999999 +-15.219099999999999 46.839550000000003 97.680000000000007 +30.677409000000001 39.162691000000002 12.550000000000001 +14.44655 44.4619 98.304000000000002 +14.4002 44.319249999999997 98.256 +-17.074024999999999 46.725797 78.549999999999997 +-17.074024999999999 46.725797 62.049999999999997 +-29.065999999999999 40.005899999999997 97.488 +27.185409 35.670690999999998 12.550000000000001 +-28.948399999999999 39.844099999999997 97.680000000000007 +39.162691000000002 30.677409000000001 12.550000000000001 +35.670690999999998 27.185409 12.550000000000001 +38.239677999999998 22.851068000000001 75.496151999999995 +0 46.75 98.304000000000002 +0 46.600000000000001 98.256 +-39.844099999999997 28.948399999999999 97.680000000000007 +37.041370000000001 32.798729999999999 7.3538480000000002 +-14.44655 44.4619 98.304000000000002 +-40.005899999999997 29.065999999999999 97.488 +-47.02975 15.280900000000001 97.488 +33.549370000000003 29.306730000000002 7.3538480000000002 +40.963622999999998 17.505030000000001 65.103847999999999 +-14.4002 44.319249999999997 98.256 +-46.839550000000003 15.219099999999999 97.680000000000007 +32.798729999999999 37.041370000000001 7.3538480000000002 +-27.478950000000001 37.821550000000002 98.304000000000002 +-27.390799999999999 37.700200000000002 98.256 +-49.450000000000003 0 97.488 +38.239677999999998 22.851068000000001 65.103847999999999 +-49.25 0 97.680000000000007 +-37.700200000000002 27.390799999999999 98.256 +-25.093077000000001 42.639868999999997 83.746151999999995 +33.549370000000003 29.306730000000002 23.853847999999999 +29.306730000000002 33.549370000000003 23.853847999999999 +-30.677409000000001 39.162691000000002 37.299999999999997 +-30.677409000000001 39.162691000000002 20.800000000000001 +-32.798729999999999 37.041370000000001 32.103847999999999 +19.747043000000001 45.363821000000002 25.996151999999999 +22.851068000000001 38.239677999999998 25.996151999999999 +17.505030000000001 40.963622999999998 25.996151999999999 +-47.552849999999999 -15.450850000000001 0 +-40.450850000000003 29.389250000000001 96.239999999999995 +-40.410400000000003 29.359850000000002 96.384 +27.766093999999999 41.277892999999999 20.800000000000001 +25.524087000000002 36.877704999999999 20.800000000000001 +25.093077000000001 42.639868999999997 25.996151999999999 +49.149999999999999 0 97.775999999999996 +-40.450850000000003 29.389250000000001 0 +32.798729999999999 37.041370000000001 34.246152000000002 +29.306730000000002 33.549370000000003 56.853847999999999 +33.549370000000003 29.306730000000002 50.746152000000002 +-40.963622999999998 17.505030000000001 17.746151999999999 +-42.325595999999997 14.832011 12.550000000000001 +25.093077000000001 42.639868999999997 15.603847999999999 +46.744450000000001 15.1882 97.775999999999996 +30.677409000000001 39.162691000000002 29.050000000000001 +35.670690999999998 27.185409 45.549999999999997 +22.851068000000001 38.239677999999998 15.603847999999999 +0 50 0 +27.185409 35.670690999999998 29.050000000000001 +29.306730000000002 33.549370000000003 40.353847999999999 +-40.963622999999998 17.505030000000001 7.3538480000000002 +19.747043000000001 45.363821000000002 15.603847999999999 +15.450850000000001 47.552849999999999 0 +39.162691000000002 30.677409000000001 29.050000000000001 +35.670690999999998 27.185409 29.050000000000001 +-17.099747000000001 46.712691999999997 95 +0 49.950000000000003 96.480000000000004 +27.537749999999999 37.902450000000002 98.304000000000002 +17.505030000000001 40.963622999999998 15.603847999999999 +28.88965 39.763199999999998 97.775999999999996 +29.389250000000001 40.450850000000003 0 +-49.245724000000003 4.7623610000000003 91.996151999999995 +-38.239677999999998 22.851068000000001 17.746151999999999 +-42.639868999999997 25.093077000000001 17.746151999999999 +17.074024999999999 46.725797 37.299999999999997 +19.747043000000001 45.363821000000002 42.496152000000002 +40.450850000000003 29.389250000000001 0 +-43.130400000000002 14.0139 97.200000000000003 +-45.399999999999999 0 97.296000000000006 +-45.350000000000001 0 97.200000000000003 +-49.245724000000003 4.7623610000000003 9.4961520000000004 +37.041370000000001 32.798729999999999 23.853847999999999 +-19.747043000000001 45.363821000000002 89.853847999999999 +-25.093077000000001 42.639868999999997 89.853847999999999 +26.45035 36.405749999999998 95.760000000000005 +13.905749999999999 42.797550000000001 95.760000000000005 +37.902450000000002 27.537749999999999 98.304000000000002 +14.832011 42.325595999999997 37.299999999999997 +47.552849999999999 15.450850000000001 0 +-49.651800999999999 2.1984650000000001 5 +40.963622999999998 17.505030000000001 75.496151999999995 +22.851068000000001 38.239677999999998 9.4961520000000004 +26.45035 36.405749999999998 4.7999999999999998 +-41.277892999999999 27.766093999999999 12.550000000000001 +-36.877704999999999 25.524087000000002 12.550000000000001 +17.505030000000001 40.963622999999998 32.103847999999999 +19.747043000000001 45.363821000000002 32.103847999999999 +15.1882 46.744450000000001 97.775999999999996 +50 0 0 +32.798729999999999 37.041370000000001 23.853847999999999 +13.905749999999999 42.797550000000001 4.7999999999999998 +14.832011 42.325595999999997 20.800000000000001 +44.557000000000002 14.477449999999999 98.304000000000002 +47.552849999999999 -15.450850000000001 0 +-38.239677999999998 22.851068000000001 7.3538480000000002 +25.093077000000001 42.639868999999997 42.496152000000002 +22.851068000000001 38.239677999999998 42.496152000000002 +0 49.149999999999999 97.775999999999996 +37.041370000000001 32.798729999999999 50.746152000000002 +32.798729999999999 37.041370000000001 50.746152000000002 +29.306730000000002 33.549370000000003 50.746152000000002 +30.677409000000001 39.162691000000002 45.549999999999997 +-42.325595999999997 14.832011 29.050000000000001 +-45.363821000000002 19.747043000000001 23.853847999999999 +-46.725797 17.074024999999999 29.050000000000001 +22.851068000000001 38.239677999999998 32.103847999999999 +-39.763199999999998 28.88965 97.775999999999996 +-46.725797 17.074024999999999 78.549999999999997 +-45.363821000000002 19.747043000000001 83.746151999999995 +-36.688899999999997 26.65605 97.200000000000003 +-43.177950000000003 14.029350000000001 97.296000000000006 +-49.715021999999998 -1.7992950000000001 12.550000000000001 +-30.677409000000001 39.162691000000002 53.799999999999997 +-38.239677999999998 22.851068000000001 34.246152000000002 +-40.963622999999998 17.505030000000001 34.246152000000002 +-42.639868999999997 25.093077000000001 34.246152000000002 +17.074024999999999 46.725797 53.799999999999997 +19.747043000000001 45.363821000000002 58.996152000000002 +17.505030000000001 40.963622999999998 58.996152000000002 +-46.744450000000001 15.1882 97.775999999999996 +-49.245724000000003 -4.7623610000000003 7.3538480000000002 +-48.307125999999997 -10.688492 7.3538480000000002 +39.162691000000002 30.677409000000001 45.549999999999997 +37.041370000000001 32.798729999999999 40.353847999999999 +-39.162691000000002 30.677409000000001 20.800000000000001 +32.798729999999999 37.041370000000001 67.246151999999995 +45.555599999999998 14.8019 98.352000000000004 +47.75 0 98.400000000000006 +-26.65605 36.688899999999997 97.200000000000003 +29.306730000000002 33.549370000000003 67.246151999999995 +-39.162691000000002 30.677409000000001 70.299999999999997 +-37.041370000000001 32.798729999999999 58.996152000000002 +27.185409 35.670690999999998 78.549999999999997 +28.066749999999999 38.630549999999999 98.400000000000006 +27.185409 35.670690999999998 45.549999999999997 +0 46.850000000000001 98.304000000000002 +-29.359850000000002 40.410400000000003 96.384 +25.524087000000002 36.877704999999999 53.799999999999997 +22.851068000000001 38.239677999999998 48.603847999999999 +-45.363821000000002 19.747043000000001 34.246152000000002 +14.832011 42.325595999999997 53.799999999999997 +-36.877704999999999 25.524087000000002 29.050000000000001 +19.747043000000001 45.363821000000002 48.603847999999999 +-38.239677999999998 22.851068000000001 23.853847999999999 +25.093077000000001 42.639868999999997 58.996152000000002 +14.601050000000001 44.937399999999997 98.400000000000006 +0 47.25 98.400000000000006 +14.755549999999999 45.412950000000002 98.400000000000006 +-40.963622999999998 17.505030000000001 23.853847999999999 +27.766093999999999 41.277892999999999 53.799999999999997 +-38.226050000000001 27.772849999999998 98.400000000000006 +-27.772849999999998 38.226050000000001 98.400000000000006 +-40.963622999999998 17.505030000000001 50.746152000000002 +-42.325595999999997 14.832011 45.549999999999997 +25.093077000000001 42.639868999999997 48.603847999999999 +-47.25 0 98.400000000000006 +-40.963622999999998 17.505030000000001 40.353847999999999 +-38.239677999999998 22.851068000000001 50.746152000000002 +17.074024999999999 46.725797 70.299999999999997 +19.747043000000001 45.363821000000002 75.496151999999995 +43.891249999999999 14.261150000000001 98.016000000000005 +43.796149999999997 14.23025 97.920000000000002 +46.049999999999997 0 97.920000000000002 +-14.755549999999999 45.412950000000002 98.400000000000006 +-36.877704999999999 25.524087000000002 45.549999999999997 +-14.601050000000001 44.937399999999997 98.400000000000006 +37.336150000000004 27.126300000000001 98.016000000000005 +37.255249999999997 27.067499999999999 97.920000000000002 +0 47.75 98.400000000000006 +-38.239677999999998 22.851068000000001 40.353847999999999 +25.093077000000001 42.639868999999997 75.496151999999995 +-37.255249999999997 27.067499999999999 97.920000000000002 +-43.891249999999999 14.261150000000001 98.016000000000005 +-43.796149999999997 14.23025 97.920000000000002 +46.725797 -17.074024999999999 12.550000000000001 +27.766093999999999 41.277892999999999 70.299999999999997 +-46.149999999999999 0 98.016000000000005 +45.363821000000002 -19.747043000000001 17.746151999999999 +27.126300000000001 37.336150000000004 98.016000000000005 +27.067499999999999 37.255249999999997 97.920000000000002 +47.552849999999999 -15.450850000000001 96.239999999999995 +-40.963622999999998 17.505030000000001 67.246151999999995 +-42.325595999999997 14.832011 62.049999999999997 +25.093077000000001 42.639868999999997 65.103847999999999 +45.363821000000002 -19.747043000000001 23.853847999999999 +45.412950000000002 -14.755549999999999 98.400000000000006 +-40.963622999999998 17.505030000000001 56.853847999999999 +-14.23025 43.796149999999997 97.920000000000002 +-27.126300000000001 37.336150000000004 98.016000000000005 +-27.067499999999999 37.255249999999997 97.920000000000002 +-38.239677999999998 22.851068000000001 67.246151999999995 +17.074024999999999 46.725797 86.799999999999997 +-37.336150000000004 27.126300000000001 98.016000000000005 +-36.877704999999999 25.524087000000002 62.049999999999997 +19.747043000000001 45.363821000000002 81.603847999999999 +-38.239677999999998 22.851068000000001 56.853847999999999 +25.093077000000001 42.639868999999997 91.996151999999995 +27.766093999999999 41.277892999999999 86.799999999999997 +-40.963622999999998 17.505030000000001 83.746151999999995 +-42.325595999999997 14.832011 78.549999999999997 +25.093077000000001 42.639868999999997 81.603847999999999 +-44.937399999999997 14.601050000000001 98.400000000000006 +-40.963622999999998 17.505030000000001 73.353847999999999 +-38.239677999999998 22.851068000000001 83.746151999999995 +25.093077000000001 42.639868999999997 9.4961520000000004 +19.747043000000001 45.363821000000002 9.4961520000000004 +-15.4354 47.505249999999997 96.384 +-15.4354 47.505249999999997 96.480000000000004 +-36.877704999999999 25.524087000000002 78.549999999999997 +17.434121000000001 46.542318999999999 5 +1.055264 44.832863000000003 95 +0 45 95.760000000000005 +3.9898180000000001 44.368076000000002 23.853847999999999 +1.0267520000000001 44.837378999999999 29.050000000000001 +-38.239677999999998 22.851068000000001 73.353847999999999 +27.405999000000001 41.461371 5 +3.9898180000000001 44.368076000000002 50.746152000000002 +9.9159480000000002 43.429470999999999 50.746152000000002 +14.029350000000001 43.177950000000003 97.296000000000006 +26.65605 36.688899999999997 97.200000000000003 +3.9898180000000001 44.368076000000002 7.3538480000000002 +0 45 4.7999999999999998 +-47 0 98.352000000000004 +12.879013 42.960169 12.550000000000001 +9.9159480000000002 43.429470999999999 7.3538480000000002 +-27.625900000000001 38.023800000000001 98.352000000000004 +-38.023800000000001 27.625900000000001 98.352000000000004 +-40.963622999999998 17.505030000000001 89.853847999999999 +-42.312489999999997 14.857732 95 +0 50 96.239999999999995 +1.0267520000000001 44.837378999999999 62.049999999999997 +1.0267520000000001 44.837378999999999 78.549999999999997 +-36.890810999999999 25.498366000000001 95 +3.9898180000000001 44.368076000000002 83.746151999999995 +9.9159480000000002 43.429470999999999 83.746151999999995 +3.9898180000000001 44.368076000000002 89.853847999999999 +-14.5238 44.699649999999998 98.352000000000004 +-38.239677999999998 22.851068000000001 89.853847999999999 +1.7992950000000001 49.715021999999998 12.550000000000001 +-47.505249999999997 15.4354 96.384 +12.879013 42.960169 29.050000000000001 +36.729349999999997 26.685449999999999 97.296000000000006 +36.688899999999997 26.65605 97.200000000000003 +9.9159480000000002 43.429470999999999 67.246151999999995 +12.879013 42.960169 62.049999999999997 +12.879013 42.960169 78.549999999999997 +0 47 98.352000000000004 +13.651558 47.837826999999997 12.550000000000001 +-49.950000000000003 0 96.384 +14.5238 44.699649999999998 98.352000000000004 +-40.410400000000003 29.359850000000002 96.480000000000004 +10.688492 48.307125999999997 7.3538480000000002 +-44.368076000000002 3.9898180000000001 25.996151999999999 +-44.837378999999999 1.0267520000000001 20.800000000000001 +-29.359850000000002 40.410400000000003 96.480000000000004 +26.685449999999999 36.729349999999997 97.296000000000006 +27.625900000000001 38.023800000000001 98.352000000000004 +-47.505249999999997 15.4354 96.480000000000004 +4.7623610000000003 49.245724000000003 7.3538480000000002 +-44.368076000000002 3.9898180000000001 15.603847999999999 +3.9898180000000001 44.368076000000002 67.246151999999995 +3.9898180000000001 44.368076000000002 73.353847999999999 +38.023800000000001 27.625900000000001 98.352000000000004 +12.879013 42.960169 45.549999999999997 +9.9159480000000002 43.429470999999999 34.246152000000002 +-49.950000000000003 0 96.480000000000004 +-43.429470999999999 9.9159480000000002 25.996151999999999 +46.126249999999999 14.987299999999999 98.207999999999998 +39.237299999999998 28.5076 98.207999999999998 +46.031149999999997 14.9564 98.256 +39.156399999999998 28.448799999999999 98.256 +10.688492 48.307125999999997 34.246152000000002 +4.7623610000000003 49.245724000000003 34.246152000000002 +-42.960169 12.879013 20.800000000000001 +3.9898180000000001 44.368076000000002 34.246152000000002 +46.850000000000001 0 98.304000000000002 +44.557000000000002 -14.477449999999999 98.304000000000002 +47 0 98.352000000000004 +-44.368076000000002 3.9898180000000001 91.996151999999995 +-42.797550000000001 13.905749999999999 95.760000000000005 +-45 0 95.760000000000005 +1.7992950000000001 49.715021999999998 29.050000000000001 +-43.429470999999999 9.9159480000000002 15.603847999999999 +1.0267520000000001 44.837378999999999 45.549999999999997 +44.699649999999998 14.5238 98.352000000000004 +-42.797550000000001 13.905749999999999 4.7999999999999998 +14.987299999999999 46.126249999999999 98.207999999999998 +14.9564 46.031149999999997 98.256 +28.448799999999999 39.156399999999998 98.256 +47.901049 -13.252389000000001 5 +-45 0 4.7999999999999998 +0 48.5 98.207999999999998 +49.715021999999998 -1.7992950000000001 20.800000000000001 +49.245724000000003 -4.7623610000000003 15.603847999999999 +44.699649999999998 -14.5238 98.352000000000004 +-44.368076000000002 3.9898180000000001 75.496151999999995 +-43.429470999999999 9.9159480000000002 75.496151999999995 +-44.368076000000002 3.9898180000000001 81.603847999999999 +0 48.399999999999999 98.256 +47.837826999999997 -13.651558 20.800000000000001 +48.307125999999997 -10.688492 15.603847999999999 +-42.960169 12.879013 37.299999999999997 +-43.429470999999999 9.9159480000000002 32.103847999999999 +-44.368076000000002 3.9898180000000001 42.496152000000002 +-44.837378999999999 1.0267520000000001 37.299999999999997 +-14.987299999999999 46.126249999999999 98.207999999999998 +49.245724000000003 -4.7623610000000003 25.996151999999999 +49.245724000000003 -4.7623610000000003 32.103847999999999 +-14.9564 46.031149999999997 98.256 +-28.5076 39.237299999999998 98.207999999999998 +48.307125999999997 -10.688492 25.996151999999999 +49.049999999999997 0 97.872 +-43.429470999999999 9.9159480000000002 42.496152000000002 +-39.237299999999998 28.5076 98.207999999999998 +-39.156399999999998 28.448799999999999 98.256 +-28.448799999999999 39.156399999999998 98.256 +48.307125999999997 -10.688492 32.103847999999999 +9.9159480000000002 43.429470999999999 89.853847999999999 +10.688492 48.307125999999997 50.746152000000002 +4.7623610000000003 49.245724000000003 50.746152000000002 +46.649299999999997 15.157299999999999 97.872 +-46.126249999999999 14.987299999999999 98.207999999999998 +-46.031149999999997 14.9564 98.256 +49.715021999999998 -1.7992950000000001 37.299999999999997 +9.9159480000000002 43.429470999999999 40.353847999999999 +3.9898180000000001 44.368076000000002 40.353847999999999 +1.7992950000000001 49.715021999999998 45.549999999999997 +39.682299999999998 28.830850000000002 97.872 +-46.25 0 98.063999999999993 +47.837826999999997 -13.651558 53.799999999999997 +47.837826999999997 -13.651558 70.299999999999997 +28.830850000000002 39.682299999999998 97.872 +13.651558 47.837826999999997 45.549999999999997 +12.850501 42.964685000000003 95 +-44.837378999999999 1.0267520000000001 53.799999999999997 +10.688492 48.307125999999997 40.353847999999999 +15.157299999999999 46.649299999999997 97.872 +-44.368076000000002 3.9898180000000001 58.996152000000002 +49.245724000000003 -4.7623610000000003 58.996152000000002 +49.715021999999998 -1.7992950000000001 53.799999999999997 +49.715021999999998 -1.7992950000000001 70.299999999999997 +49.245724000000003 -4.7623610000000003 65.103847999999999 +9.9159480000000002 43.429470999999999 73.353847999999999 +-15.157299999999999 46.649299999999997 97.872 +48.307125999999997 -10.688492 75.496151999999995 +48.307125999999997 -10.688492 81.603847999999999 +10.688492 48.307125999999997 67.246151999999995 +4.7623610000000003 49.245724000000003 67.246151999999995 +-28.830850000000002 39.682299999999998 97.872 +-39.682299999999998 28.830850000000002 97.872 +1.7992950000000001 49.715021999999998 62.049999999999997 +49.651800999999999 -2.1984650000000001 5 +13.651558 47.837826999999997 62.049999999999997 +-9.9159480000000002 43.429470999999999 91.996151999999995 +-13.905749999999999 42.797550000000001 95.760000000000005 +-9.9159480000000002 43.429470999999999 75.496151999999995 +-3.9898180000000001 44.368076000000002 75.496151999999995 +-9.9159480000000002 43.429470999999999 81.603847999999999 +10.688492 48.307125999999997 56.853847999999999 +4.7623610000000003 49.245724000000003 56.853847999999999 +-1.0267520000000001 44.837378999999999 37.299999999999997 +-3.9898180000000001 44.368076000000002 32.103847999999999 +-1.0267520000000001 44.837378999999999 20.800000000000001 +46.25 0 98.063999999999993 +43.891249999999999 -14.261150000000001 98.016000000000005 +49.245724000000003 -4.7623610000000003 81.603847999999999 +-9.9159480000000002 43.429470999999999 32.103847999999999 +-9.9159480000000002 43.429470999999999 25.996151999999999 +-13.905749999999999 42.797550000000001 4.7999999999999998 +-9.9159480000000002 43.429470999999999 15.603847999999999 +10.688492 48.307125999999997 83.746151999999995 +4.7623610000000003 49.245724000000003 83.746151999999995 +43.986350000000002 14.29205 98.063999999999993 +46.149999999999999 0 98.016000000000005 +-3.9898180000000001 44.368076000000002 9.4961520000000004 +-1.4259219999999999 44.774157000000002 5 +1.7992950000000001 49.715021999999998 78.549999999999997 +-12.879013 42.960169 86.799999999999997 +13.651558 47.837826999999997 78.549999999999997 +-1.0267520000000001 44.837378999999999 53.799999999999997 +-3.9898180000000001 44.368076000000002 48.603847999999999 +-3.9898180000000001 44.368076000000002 42.496152000000002 +44.794750000000001 -14.5547 98.352000000000004 +0 46.149999999999999 98.016000000000005 +14.261150000000001 43.891249999999999 98.016000000000005 +-12.879013 42.960169 53.799999999999997 +38.104700000000001 27.684699999999999 98.352000000000004 +44.794750000000001 14.5547 98.352000000000004 +4.7623610000000003 49.245724000000003 73.353847999999999 +-9.9159480000000002 43.429470999999999 65.103847999999999 +-12.879013 42.960169 70.299999999999997 +-9.9159480000000002 43.429470999999999 58.996152000000002 +27.684699999999999 38.104700000000001 98.352000000000004 +-14.261150000000001 43.891249999999999 98.016000000000005 +4.7623610000000003 49.245724000000003 89.853847999999999 +1.827807 49.710507 95 +49.245724000000003 -4.7623610000000003 9.4961520000000004 +-9.9159480000000002 43.429470999999999 42.496152000000002 +13.623046 47.842343 95 +47.837826999999997 13.651558 12.550000000000001 +10.688492 48.307125999999997 89.853847999999999 +-43.320599999999999 14.075699999999999 97.488 +47.837826999999997 13.651558 29.050000000000001 +49.715021999999998 1.7992950000000001 12.550000000000001 +49.245724000000003 4.7623610000000003 17.746151999999999 +49.245724000000003 4.7623610000000003 23.853847999999999 +47.837826999999997 13.651558 78.549999999999997 +47.837826999999997 13.651558 62.049999999999997 +49.245724000000003 4.7623610000000003 7.3538480000000002 +48.307125999999997 10.688492 50.746152000000002 +-26.773599999999998 36.850700000000003 97.488 +-36.850700000000003 26.773599999999998 97.488 +42.325595999999997 -14.832011 45.549999999999997 +-1.0267520000000001 44.837378999999999 86.799999999999997 +-3.9898180000000001 44.368076000000002 91.996151999999995 +-39.601399999999998 28.772099999999998 97.920000000000002 +40.963622999999998 -17.505030000000001 50.746152000000002 +46.350000000000001 0 98.159999999999997 +42.639868999999997 25.093077000000001 65.103847999999999 +-3.9898180000000001 44.368076000000002 81.603847999999999 +44.081449999999997 -14.322950000000001 98.159999999999997 +-48.950000000000003 0 97.920000000000002 +44.081449999999997 14.322950000000001 98.159999999999997 +40.450850000000003 29.389250000000001 96.239999999999995 +-45.75 0 97.680000000000007 +-12.479844 43.023390999999997 5 +-29.306730000000002 33.549370000000003 15.603847999999999 +-33.549370000000003 29.306730000000002 15.603847999999999 +-29.306730000000002 33.549370000000003 9.4961520000000004 +45.363821000000002 19.747043000000001 65.103847999999999 +-35.670690999999998 27.185409 53.799999999999997 +-33.549370000000003 29.306730000000002 58.996152000000002 +-35.670690999999998 27.185409 70.299999999999997 +-42.797550000000001 -13.905749999999999 4.7999999999999998 +-17.505030000000001 40.963622999999998 17.746151999999999 +-22.851068000000001 38.239677999999998 17.746151999999999 +47.837826999999997 13.651558 45.549999999999997 +48.307125999999997 10.688492 17.746151999999999 +49.715021999999998 1.7992950000000001 29.050000000000001 +48.307125999999997 10.688492 23.853847999999999 +48.307125999999997 10.688492 40.353847999999999 +48.307125999999997 10.688492 34.246152000000002 +-48.399999999999999 0 98.256 +-45.888500000000001 14.91005 98.304000000000002 +48.307125999999997 10.688492 67.246151999999995 +48.307125999999997 10.688492 73.353847999999999 +49.245724000000003 4.7623610000000003 34.246152000000002 +49.245724000000003 4.7623610000000003 40.353847999999999 +48.399999999999999 0 98.256 +45.888500000000001 -14.91005 98.304000000000002 +46.031149999999997 -14.9564 98.256 +47.505249999999997 -15.4354 96.384 +49.245724000000003 4.7623610000000003 73.353847999999999 +47.842343 13.623046 95 +40.963622999999998 -17.505030000000001 7.3538480000000002 +49.710507 1.827807 95 +42.325595999999997 -14.832011 12.550000000000001 +48.307125999999997 10.688492 89.853847999999999 +49.245724000000003 4.7623610000000003 89.853847999999999 +40.963622999999998 -17.505030000000001 17.746151999999999 +38.239677999999998 -22.851068000000001 17.746151999999999 +38.239677999999998 -22.851068000000001 23.853847999999999 +42.325595999999997 -14.832011 29.050000000000001 +41.277892999999999 27.766093999999999 20.800000000000001 +42.639868999999997 25.093077000000001 15.603847999999999 +40.963622999999998 -17.505030000000001 40.353847999999999 +37.497950000000003 27.243849999999998 98.159999999999997 +-27.243849999999998 37.497950000000003 98.159999999999997 +-37.497950000000003 27.243849999999998 98.159999999999997 +38.954149999999998 28.301850000000002 98.304000000000002 +45.793349999999997 14.879149999999999 98.304000000000002 +28.301850000000002 38.954149999999998 98.304000000000002 +14.879149999999999 45.793349999999997 98.304000000000002 +0 48.149999999999999 98.304000000000002 +-38.954149999999998 28.301850000000002 98.304000000000002 +-28.301850000000002 38.954149999999998 98.304000000000002 +48.149999999999999 0 98.304000000000002 +-48.149999999999999 0 98.304000000000002 +49.950000000000003 0 96.384 +-14.879149999999999 45.793349999999997 98.304000000000002 +32.798729999999999 37.041370000000001 40.353847999999999 +43.429470999999999 -9.9159480000000002 15.603847999999999 +42.960169 -12.879013 20.800000000000001 +44.368076000000002 -3.9898180000000001 15.603847999999999 +44.837378999999999 -1.0267520000000001 20.800000000000001 +44.368076000000002 -3.9898180000000001 25.996151999999999 +30.677409000000001 39.162691000000002 78.549999999999997 +43.429470999999999 -9.9159480000000002 25.996151999999999 +43.429470999999999 -9.9159480000000002 32.103847999999999 +42.960169 -12.879013 37.299999999999997 +32.798729999999999 37.041370000000001 73.353847999999999 +44.368076000000002 -3.9898180000000001 32.103847999999999 +44.837378999999999 -1.0267520000000001 37.299999999999997 +45.793349999999997 -14.879149999999999 98.304000000000002 +-45.793349999999997 14.879149999999999 98.304000000000002 +43.429470999999999 -9.9159480000000002 42.496152000000002 +43.429470999999999 -9.9159480000000002 48.603847999999999 +42.960169 -12.879013 53.799999999999997 +-46.5 0 98.207999999999998 +44.368076000000002 -3.9898180000000001 48.603847999999999 +44.837378999999999 -1.0267520000000001 53.799999999999997 +32.798729999999999 37.041370000000001 83.746151999999995 +37.041370000000001 32.798729999999999 83.746151999999995 +44.368076000000002 -3.9898180000000001 58.996152000000002 +43.429470999999999 -9.9159480000000002 58.996152000000002 +43.429470999999999 -9.9159480000000002 65.103847999999999 +37.041370000000001 32.798729999999999 89.853847999999999 +32.798729999999999 37.041370000000001 89.853847999999999 +44.368076000000002 -3.9898180000000001 65.103847999999999 +44.837378999999999 -1.0267520000000001 70.299999999999997 +47.505249999999997 -15.4354 96.480000000000004 +49.950000000000003 0 96.480000000000004 +44.368076000000002 -3.9898180000000001 75.496151999999995 +42.797550000000001 -13.905749999999999 4.7999999999999998 +36.405749999999998 -26.45035 4.7999999999999998 +46.5 0 98.207999999999998 +43.429470999999999 -9.9159480000000002 75.496151999999995 +44.224150000000002 -14.369300000000001 98.207999999999998 +44.224150000000002 14.369300000000001 98.207999999999998 +42.797550000000001 -13.905749999999999 95.760000000000005 +37.619300000000003 27.332000000000001 98.207999999999998 +17.074024999999999 46.725797 20.800000000000001 +25.093077000000001 42.639868999999997 32.103847999999999 +44.368076000000002 -3.9898180000000001 81.603847999999999 +27.332000000000001 37.619300000000003 98.207999999999998 +44.837378999999999 -1.0267520000000001 86.799999999999997 +14.369300000000001 44.224150000000002 98.207999999999998 +0 46.5 98.207999999999998 +44.368076000000002 -3.9898180000000001 91.996151999999995 +-44.224150000000002 14.369300000000001 98.207999999999998 +43.429470999999999 -9.9159480000000002 91.996151999999995 +-14.369300000000001 44.224150000000002 98.207999999999998 +44.774157000000002 -1.4259219999999999 5 +-27.332000000000001 37.619300000000003 98.207999999999998 +27.766093999999999 41.277892999999999 37.299999999999997 +43.023390999999997 -12.479844 5 +43.429470999999999 -9.9159480000000002 9.4961520000000004 +-37.619300000000003 27.332000000000001 98.207999999999998 +42.325595999999997 -14.832011 62.049999999999997 +40.963622999999998 -17.505030000000001 56.853847999999999 +47.505249999999997 15.4354 96.384 +43.429470999999999 9.9159480000000002 17.746151999999999 +42.960169 12.879013 12.550000000000001 +44.837378999999999 1.0267520000000001 12.550000000000001 +44.368076000000002 3.9898180000000001 7.3538480000000002 +43.429470999999999 9.9159480000000002 34.246152000000002 +42.960169 12.879013 29.050000000000001 +44.368076000000002 3.9898180000000001 34.246152000000002 +48 0 98.352000000000004 +44.837378999999999 1.0267520000000001 29.050000000000001 +45.650700000000001 14.832800000000001 98.352000000000004 +38.832799999999999 28.213699999999999 98.352000000000004 +28.213699999999999 38.832799999999999 98.352000000000004 +47.505249999999997 15.4354 96.480000000000004 +14.832800000000001 45.650700000000001 98.352000000000004 +45 0 4.7999999999999998 +43.429470999999999 9.9159480000000002 50.746152000000002 +0 48 98.352000000000004 +42.960169 12.879013 45.549999999999997 +44.368076000000002 3.9898180000000001 50.746152000000002 +-14.832800000000001 45.650700000000001 98.352000000000004 +45 0 95.760000000000005 +-38.832799999999999 28.213699999999999 98.352000000000004 +44.368076000000002 3.9898180000000001 40.353847999999999 +-45.650700000000001 14.832800000000001 98.352000000000004 +43.429470999999999 9.9159480000000002 40.353847999999999 +-48 0 98.352000000000004 +-46.600000000000001 0 98.256 +43.429470999999999 9.9159480000000002 67.246151999999995 +43.429470999999999 9.9159480000000002 56.853847999999999 +42.960169 12.879013 78.549999999999997 +44.837378999999999 1.0267520000000001 78.549999999999997 +44.368076000000002 3.9898180000000001 73.353847999999999 +43.429470999999999 9.9159480000000002 73.353847999999999 +10.688492 48.307125999999997 73.353847999999999 +44.832863000000003 1.055264 95 +42.964685000000003 12.850501 95 +43.429470999999999 9.9159480000000002 89.853847999999999 +44.368076000000002 3.9898180000000001 89.853847999999999 +-13.651558 47.837826999999997 70.299999999999997 +-13.651558 47.837826999999997 86.799999999999997 +-1.7992950000000001 49.715021999999998 20.800000000000001 +-4.7623610000000003 49.245724000000003 15.603847999999999 +-13.651558 47.837826999999997 53.799999999999997 +-14.8019 45.555599999999998 98.352000000000004 +0 47.899999999999999 98.352000000000004 +14.8019 45.555599999999998 98.352000000000004 +-10.688492 48.307125999999997 42.496152000000002 +-10.688492 48.307125999999997 48.603847999999999 +47.899999999999999 0 98.352000000000004 +45.555599999999998 -14.8019 98.352000000000004 +33.549370000000003 29.306730000000002 40.353847999999999 +-32.798729999999999 37.041370000000001 15.603847999999999 +-30.963183000000001 38.876916999999999 5 +-32.798729999999999 37.041370000000001 9.4961520000000004 +27.205822000000001 35.650278 95 +-37.041370000000001 32.798729999999999 65.103847999999999 +29.306730000000002 33.549370000000003 83.746151999999995 +33.549370000000003 29.306730000000002 83.746151999999995 +29.306730000000002 33.549370000000003 89.853847999999999 +29.306730000000002 33.549370000000003 73.353847999999999 +-32.798729999999999 37.041370000000001 65.103847999999999 +-32.798729999999999 37.041370000000001 58.996152000000002 +-37.041370000000001 32.798729999999999 81.603847999999999 +-39.162691000000002 30.677409000000001 86.799999999999997 +-30.677409000000001 39.162691000000002 86.799999999999997 +-32.798729999999999 37.041370000000001 81.603847999999999 +33.549370000000003 29.306730000000002 89.853847999999999 +-37.041370000000001 32.798729999999999 9.4961520000000004 +-38.876916999999999 30.963183000000001 5 +-3.9898180000000001 44.368076000000002 15.603847999999999 +-3.9898180000000001 44.368076000000002 25.996151999999999 +47.100000000000001 0 98.352000000000004 +-36.405749999999998 26.45035 4.7999999999999998 +-26.45035 36.405749999999998 4.7999999999999998 +-26.45035 36.405749999999998 95.760000000000005 +-27.185409 35.670690999999998 20.800000000000001 +-27.471183 35.384917000000002 5 +-36.405749999999998 26.45035 95.760000000000005 +-33.549370000000003 29.306730000000002 9.4961520000000004 +-33.549370000000003 29.306730000000002 25.996151999999999 +-35.670690999999998 27.185409 37.299999999999997 +-35.384917000000002 27.471183 5 +-9.9159480000000002 43.429470999999999 48.603847999999999 +-3.9898180000000001 44.368076000000002 58.996152000000002 +-1.0267520000000001 44.837378999999999 70.299999999999997 +-3.9898180000000001 44.368076000000002 65.103847999999999 +-9.9159480000000002 43.429470999999999 9.4961520000000004 +-25.524087000000002 36.877704999999999 12.550000000000001 +-14.832011 42.325595999999997 12.550000000000001 +-17.505030000000001 40.963622999999998 7.3538480000000002 +-22.851068000000001 38.239677999999998 7.3538480000000002 +-17.505030000000001 40.963622999999998 34.246152000000002 +-22.851068000000001 38.239677999999998 34.246152000000002 +-25.524087000000002 36.877704999999999 29.050000000000001 +-14.832011 42.325595999999997 29.050000000000001 +-17.505030000000001 40.963622999999998 23.853847999999999 +-22.851068000000001 38.239677999999998 23.853847999999999 +-17.505030000000001 40.963622999999998 50.746152000000002 +-22.851068000000001 38.239677999999998 50.746152000000002 +-25.524087000000002 36.877704999999999 45.549999999999997 +-14.832011 42.325595999999997 45.549999999999997 +-17.505030000000001 40.963622999999998 40.353847999999999 +-22.851068000000001 38.239677999999998 40.353847999999999 +-17.505030000000001 40.963622999999998 67.246151999999995 +-22.851068000000001 38.239677999999998 67.246151999999995 +-25.524087000000002 36.877704999999999 62.049999999999997 +-14.832011 42.325595999999997 62.049999999999997 +-17.505030000000001 40.963622999999998 56.853847999999999 +-22.851068000000001 38.239677999999998 56.853847999999999 +-17.505030000000001 40.963622999999998 83.746151999999995 +-22.851068000000001 38.239677999999998 83.746151999999995 +-25.524087000000002 36.877704999999999 78.549999999999997 +-14.832011 42.325595999999997 78.549999999999997 +-17.505030000000001 40.963622999999998 73.353847999999999 +-22.851068000000001 38.239677999999998 73.353847999999999 +-25.498366000000001 36.890810999999999 95 +-22.851068000000001 38.239677999999998 89.853847999999999 +-14.857732 42.312489999999997 95 +-17.505030000000001 40.963622999999998 89.853847999999999 +-29.306730000000002 33.549370000000003 25.996151999999999 +-29.306730000000002 33.549370000000003 32.103847999999999 +-33.549370000000003 29.306730000000002 32.103847999999999 +-29.306730000000002 33.549370000000003 58.996152000000002 +-27.185409 35.670690999999998 53.799999999999997 +-29.306730000000002 33.549370000000003 75.496151999999995 +-33.549370000000003 29.306730000000002 75.496151999999995 +-27.185409 35.670690999999998 70.299999999999997 +-29.306730000000002 33.549370000000003 65.103847999999999 +-33.549370000000003 29.306730000000002 65.103847999999999 +-29.306730000000002 33.549370000000003 91.996151999999995 +-33.549370000000003 29.306730000000002 91.996151999999995 +-35.670690999999998 27.185409 86.799999999999997 +-27.185409 35.670690999999998 86.799999999999997 +-29.306730000000002 33.549370000000003 81.603847999999999 +-33.549370000000003 29.306730000000002 81.603847999999999 +-15.41995 47.457700000000003 96.623999999999995 +-29.330500000000001 40.369950000000003 96.623999999999995 +-40.369950000000003 29.330500000000001 96.623999999999995 +-47.457700000000003 15.41995 96.623999999999995 +-49.899999999999999 0 96.623999999999995 +-26.45035 -36.405749999999998 4.7999999999999998 +-36.405749999999998 -26.45035 4.7999999999999998 +-13.905749999999999 -42.797550000000001 4.7999999999999998 +0 -45 4.7999999999999998 +13.905749999999999 -42.797550000000001 4.7999999999999998 +26.45035 -36.405749999999998 4.7999999999999998 +-42.797550000000001 13.905749999999999 96.239999999999995 +-45 0 96.239999999999995 +-36.405749999999998 26.45035 96.239999999999995 +0 49.899999999999999 96.623999999999995 +-26.45035 36.405749999999998 96.239999999999995 +-15.41995 47.457700000000003 96.719999999999999 +0 49.899999999999999 96.719999999999999 +-29.330500000000001 40.369950000000003 96.719999999999999 +-40.369950000000003 29.330500000000001 96.719999999999999 +-47.457700000000003 15.41995 96.719999999999999 +-49.899999999999999 0 96.719999999999999 +-45.049999999999997 0 96.384 +-42.845100000000002 13.921200000000001 96.384 +-26.479749999999999 36.446199999999997 96.384 +-36.446199999999997 26.479749999999999 96.384 +47.457700000000003 -15.41995 96.623999999999995 +49.899999999999999 0 96.623999999999995 +45 0 96.239999999999995 +42.797550000000001 -13.905749999999999 96.239999999999995 +47.457700000000003 15.41995 96.623999999999995 +42.797550000000001 13.905749999999999 96.239999999999995 +40.369950000000003 29.330500000000001 96.623999999999995 +29.330500000000001 40.369950000000003 96.623999999999995 +36.405749999999998 26.45035 96.239999999999995 +15.41995 47.457700000000003 96.623999999999995 +26.45035 36.405749999999998 96.239999999999995 +13.905749999999999 42.797550000000001 96.239999999999995 +0 45 96.239999999999995 +-13.905749999999999 42.797550000000001 96.239999999999995 +15.41995 47.457700000000003 96.719999999999999 +15.404500000000001 47.410150000000002 96.864000000000004 +0 49.850000000000001 96.864000000000004 +-15.404500000000001 47.410150000000002 96.864000000000004 +-29.301100000000002 40.329500000000003 96.864000000000004 +-40.329500000000003 29.301100000000002 96.864000000000004 +-47.410150000000002 15.404500000000001 96.864000000000004 +-49.850000000000001 0 96.864000000000004 +-45.049999999999997 0 96.480000000000004 +-42.845100000000002 13.921200000000001 96.480000000000004 +-36.446199999999997 26.479749999999999 96.480000000000004 +-26.479749999999999 36.446199999999997 96.480000000000004 +-13.921200000000001 42.845100000000002 96.384 +-13.921200000000001 42.845100000000002 96.480000000000004 +29.2423 40.248600000000003 97.103999999999999 +40.248600000000003 29.2423 97.103999999999999 +15.3736 47.315049999999999 97.103999999999999 +0 49.75 97.103999999999999 +-15.3736 47.315049999999999 97.103999999999999 +-29.2423 40.248600000000003 97.103999999999999 +-40.248600000000003 29.2423 97.103999999999999 +-47.315049999999999 15.3736 97.103999999999999 +-49.75 0 97.103999999999999 +-45.100000000000001 0 96.719999999999999 +-42.892650000000003 13.93665 96.719999999999999 +-36.486649999999997 26.5091 96.719999999999999 +-26.5091 36.486649999999997 96.719999999999999 +-13.93665 42.892650000000003 96.719999999999999 +0 45.100000000000001 96.719999999999999 +47.315049999999999 15.3736 97.103999999999999 +47.219949999999997 15.342700000000001 97.200000000000003 +40.167700000000004 29.18355 97.200000000000003 +29.18355 40.167700000000004 97.200000000000003 +15.342700000000001 47.219949999999997 97.200000000000003 +0 49.649999999999999 97.200000000000003 +-15.342700000000001 47.219949999999997 97.200000000000003 +-29.18355 40.167700000000004 97.200000000000003 +-40.167700000000004 29.18355 97.200000000000003 +-47.219949999999997 15.342700000000001 97.200000000000003 +-49.649999999999999 0 97.200000000000003 +-45.149999999999999 0 96.864000000000004 +-42.940199999999997 13.9521 96.864000000000004 +-26.538499999999999 36.527099999999997 96.864000000000004 +-36.527099999999997 26.538499999999999 96.864000000000004 +-13.9521 42.940199999999997 96.864000000000004 +47.077300000000001 15.29635 97.391999999999996 +40.046349999999997 29.09535 97.391999999999996 +29.09535 40.046349999999997 97.391999999999996 +-15.29635 47.077300000000001 97.391999999999996 +-29.09535 40.046349999999997 97.391999999999996 +-40.046349999999997 29.09535 97.391999999999996 +-47.077300000000001 15.29635 97.391999999999996 +-49.5 0 97.391999999999996 +-45.25 0 97.103999999999999 +-43.035299999999999 13.983000000000001 97.103999999999999 +-26.597300000000001 36.607999999999997 97.103999999999999 +-36.607999999999997 26.597300000000001 97.103999999999999 +-13.983000000000001 43.035299999999999 97.103999999999999 +13.967549999999999 42.987749999999998 96.959999999999994 +0 45.25 97.103999999999999 +45.200000000000003 0 96.959999999999994 +42.987749999999998 -13.967549999999999 96.959999999999994 +42.987749999999998 13.967549999999999 96.959999999999994 +-14.0139 43.130400000000002 97.200000000000003 +0 45.350000000000001 97.200000000000003 +45.25 0 97.103999999999999 +43.035299999999999 -13.983000000000001 97.103999999999999 +45.350000000000001 0 97.200000000000003 +43.130400000000002 -14.0139 97.200000000000003 +43.035299999999999 13.983000000000001 97.103999999999999 +45.549999999999997 0 97.488 +43.320599999999999 -14.075699999999999 97.488 +43.320599999999999 14.075699999999999 97.488 +-14.075699999999999 43.320599999999999 97.488 +36.850700000000003 26.773599999999998 97.488 +14.06025 43.273049999999998 97.391999999999996 +0 45.549999999999997 97.488 +26.744250000000001 36.810250000000003 97.391999999999996 +26.773599999999998 36.850700000000003 97.488 +46.554200000000002 -15.1264 97.920000000000002 +48.950000000000003 0 97.920000000000002 +46.554200000000002 15.1264 97.920000000000002 +39.601399999999998 28.772099999999998 97.920000000000002 +28.772099999999998 39.601399999999998 97.920000000000002 +15.1264 46.554200000000002 97.920000000000002 +0 48.950000000000003 97.920000000000002 +-15.1264 46.554200000000002 97.920000000000002 +-28.772099999999998 39.601399999999998 97.920000000000002 +-43.510849999999998 14.137549999999999 97.680000000000007 +45.75 0 97.680000000000007 +43.510849999999998 -14.137549999999999 97.680000000000007 +-26.891200000000001 37.012549999999997 97.680000000000007 +-37.012549999999997 26.891200000000001 97.680000000000007 +43.510849999999998 14.137549999999999 97.680000000000007 +37.012549999999997 26.891200000000001 97.680000000000007 +-14.137549999999999 43.510849999999998 97.680000000000007 +26.891200000000001 37.012549999999997 97.680000000000007 +14.075699999999999 43.320599999999999 97.488 +0 45.75 97.680000000000007 +14.137549999999999 43.510849999999998 97.680000000000007 +46.459099999999999 -15.095499999999999 98.016000000000005 +48.850000000000001 0 98.016000000000005 +46.459099999999999 15.095499999999999 98.016000000000005 +39.520499999999998 28.7133 98.016000000000005 +28.7133 39.520499999999998 98.016000000000005 +15.095499999999999 46.459099999999999 98.016000000000005 +0 48.850000000000001 98.016000000000005 +-15.095499999999999 46.459099999999999 98.016000000000005 +-28.7133 39.520499999999998 98.016000000000005 +-39.520499999999998 28.7133 98.016000000000005 +-46.459099999999999 15.095499999999999 98.016000000000005 +-48.850000000000001 0 98.016000000000005 +-45.850000000000001 0 97.775999999999996 +-43.60595 14.16845 97.775999999999996 +45.850000000000001 0 97.775999999999996 +43.60595 -14.16845 97.775999999999996 +43.60595 14.16845 97.775999999999996 +-26.949950000000001 37.093449999999997 97.775999999999996 +-37.093449999999997 26.949950000000001 97.775999999999996 +37.093449999999997 26.949950000000001 97.775999999999996 +26.949950000000001 37.093449999999997 97.775999999999996 +-14.16845 43.60595 97.775999999999996 +46.363999999999997 -15.0646 98.063999999999993 +48.75 0 98.063999999999993 +46.363999999999997 15.0646 98.063999999999993 +39.439599999999999 28.65455 98.063999999999993 +28.65455 39.439599999999999 98.063999999999993 +15.0646 46.363999999999997 98.063999999999993 +0 48.75 98.063999999999993 +-15.0646 46.363999999999997 98.063999999999993 +-28.65455 39.439599999999999 98.063999999999993 +-39.439599999999999 28.65455 98.063999999999993 +-46.363999999999997 15.0646 98.063999999999993 +-48.75 0 98.063999999999993 +-45.950000000000003 0 97.872 +45.950000000000003 0 97.872 +43.701050000000002 -14.199350000000001 97.872 +-43.701050000000002 14.199350000000001 97.872 +43.701050000000002 14.199350000000001 97.872 +37.174349999999997 27.008749999999999 97.872 +-37.174349999999997 27.008749999999999 97.872 +27.008749999999999 37.174349999999997 97.872 +14.199350000000001 43.701050000000002 97.872 +-14.199350000000001 43.701050000000002 97.872 +0 45.950000000000003 97.872 +46.268900000000002 -15.0337 98.159999999999997 +48.649999999999999 0 98.159999999999997 +46.268900000000002 15.0337 98.159999999999997 +39.358699999999999 28.595749999999999 98.159999999999997 +28.595749999999999 39.358699999999999 98.159999999999997 +15.0337 46.268900000000002 98.159999999999997 +0 48.649999999999999 98.159999999999997 +-15.0337 46.268900000000002 98.159999999999997 +-28.595749999999999 39.358699999999999 98.159999999999997 +-39.358699999999999 28.595749999999999 98.159999999999997 +-46.268900000000002 15.0337 98.159999999999997 +-48.649999999999999 0 98.159999999999997 +-46.049999999999997 0 97.920000000000002 +43.796149999999997 -14.23025 97.920000000000002 +14.23025 43.796149999999997 97.920000000000002 +0 46.049999999999997 97.920000000000002 +46.126249999999999 -14.987299999999999 98.207999999999998 +48.5 0 98.207999999999998 +28.5076 39.237299999999998 98.207999999999998 +-48.5 0 98.207999999999998 +14.5547 44.794750000000001 98.352000000000004 +0 47.100000000000001 98.352000000000004 +-14.5547 44.794750000000001 98.352000000000004 +-27.684699999999999 38.104700000000001 98.352000000000004 +-38.104700000000001 27.684699999999999 98.352000000000004 +-44.794750000000001 14.5547 98.352000000000004 +-47.100000000000001 0 98.352000000000004 +-50 0 96.239999999999995 +-45 0 95.760000000000005 +42.797550000000001 -13.905749999999999 95.760000000000005 +3 34 35 36 +3 37 38 26 +3 39 40 41 +3 33 30 29 +3 31 42 32 +3 72 73 57 +3 74 40 43 +3 75 76 77 +3 61 69 70 +3 91 100 92 +3 107 18 17 +3 96 103 108 +3 97 80 63 +3 109 99 104 +3 46 40 74 +3 110 111 101 +3 79 112 113 +3 63 56 3 +3 115 109 104 +3 116 114 122 +3 117 123 118 +3 52 89 50 +3 85 93 71 +3 127 123 131 +3 106 105 90 +3 179 2 1 +3 147 184 196 +3 197 25 188 +3 172 189 139 +3 198 13 119 +3 138 8 191 +3 164 199 200 +3 87 205 177 +3 139 189 207 +3 173 10 190 +3 91 82 210 +3 181 201 180 +3 213 214 183 +3 219 217 218 +3 78 54 53 +3 209 146 195 +3 220 221 222 +3 203 223 204 +3 122 130 224 +3 225 3 226 +3 227 228 185 +3 11 229 1006 +3 216 163 157 +3 228 186 185 +3 238 147 239 +3 240 241 163 +3 49 48 86 +3 154 148 242 +3 124 120 253 +3 152 151 145 +3 51 62 153 +3 255 256 214 +3 233 232 257 +3 258 234 236 +3 130 136 237 +3 342 369 376 +3 359 373 360 +3 380 374 194 +3 149 375 368 +3 371 25 12 +3 374 367 175 +3 366 383 357 +3 338 376 356 +3 373 336 337 +3 295 292 302 +3 20 21 384 +3 353 328 329 +3 393 394 395 +3 396 397 398 +3 399 390 392 +3 400 391 21 +3 410 411 206 +3 341 342 208 +3 395 394 412 +3 413 414 415 +3 389 358 382 +3 25 377 42 +3 409 416 405 +3 417 211 418 +3 331 330 399 +3 153 161 429 +3 97 421 81 +3 433 427 432 +3 434 428 424 +3 97 63 430 +3 420 419 63 +3 290 446 294 +3 447 448 298 +3 362 193 363 +3 420 370 384 +3 401 444 334 +3 205 454 449 +3 455 452 71 +3 456 457 458 +3 352 451 435 +3 422 452 1007 +3 381 453 442 +3 205 215 454 +3 425 441 610 +3 594 611 567 +3 598 155 219 +3 612 592 561 +3 162 94 219 +3 620 619 624 +3 626 459 627 +3 628 569 596 +3 608 243 604 +3 630 462 634 +3 574 631 572 +3 633 538 635 +3 171 117 189 +3 633 636 535 +3 314 95 94 +3 638 584 540 +3 463 483 641 +3 654 407 653 +3 585 651 586 +3 272 183 260 +3 650 469 514 +3 660 517 659 +3 530 534 657 +3 318 288 312 +3 637 530 656 +3 276 309 277 +3 555 6 659 +3 182 234 183 +3 301 262 235 +3 522 661 6 +3 521 526 661 +3 530 580 583 +3 315 288 318 +3 267 301 293 +3 559 665 663 +3 556 667 564 +3 588 668 666 +3 327 169 323 +3 652 150 640 +3 7 523 557 +3 578 579 577 +3 587 589 669 +3 150 178 160 +3 590 672 670 +3 333 170 327 +3 664 5 520 +3 590 591 672 +3 160 322 169 +3 626 618 621 +3 675 597 678 +3 527 677 674 +3 597 593 678 +3 617 613 614 +3 170 365 95 +3 681 565 558 +3 606 605 607 +3 680 612 682 +3 477 437 468 +3 637 600 631 +3 607 615 613 +3 682 595 684 +3 468 494 465 +3 676 683 554 +3 609 603 605 +3 647 685 616 +3 525 562 686 +3 689 570 690 +3 630 643 627 +3 476 450 436 +3 690 570 563 +3 692 464 506 +3 688 691 524 +3 83 92 636 +3 470 496 95 +3 573 693 571 +3 646 648 1008 +3 101 642 638 +3 513 95 516 +3 518 489 512 +3 694 695 571 +3 111 121 644 +3 488 489 518 +3 696 576 575 +3 599 654 653 +3 602 673 601 +3 645 129 649 +3 94 16 541 +3 626 622 623 +3 671 658 22 +3 697 10 698 +3 45 582 581 +3 662 673 661 +3 651 133 655 +3 699 165 700 +3 128 547 536 +3 677 687 674 +3 706 705 701 +3 135 128 536 +3 4 119 13 +3 24 60 102 +3 88 64 65 +3 84 76 75 +3 102 68 44 +3 133 132 137 +3 45 58 59 +3 67 66 86 +3 167 709 598 +3 713 40 192 +3 707 303 708 +3 140 166 104 +3 714 249 248 +3 252 167 219 +3 218 157 156 +3 707 244 245 +3 142 23 710 +3 715 716 717 +3 704 265 251 +3 142 141 9 +3 718 250 247 +3 703 702 286 +3 719 230 720 +3 104 98 140 +3 286 702 281 +3 717 725 625 +3 95 27 14 +3 95 347 27 +3 639 144 146 +3 176 169 639 +3 780 779 143 +3 792 723 724 +3 722 790 721 +3 712 737 726 +3 727 740 711 +3 254 372 324 +3 324 372 325 +3 403 168 159 +3 274 271 232 +3 202 212 731 +3 313 304 270 +3 300 299 317 +3 316 321 345 +3 282 287 231 +3 332 320 299 +3 275 282 311 +3 350 736 346 +3 321 350 355 +3 321 326 736 +3 332 299 308 +3 259 261 202 +3 261 266 158 +3 794 372 795 +3 372 796 795 +3 797 383 798 +3 403 408 551 +3 800 403 551 +3 801 403 799 +3 803 403 802 +3 632 403 804 +3 398 397 806 +3 397 736 805 +3 348 378 349 +3 460 379 361 +3 351 353 329 +3 335 381 362 +3 392 382 443 +3 498 741 515 +3 478 497 499 +3 508 473 495 +3 537 475 474 +3 532 329 471 +3 744 507 509 +3 743 744 742 +3 474 743 531 +3 741 503 515 +3 486 339 493 +3 486 543 745 +3 479 484 485 +3 729 480 479 +3 728 487 492 +3 757 756 767 +3 732 748 749 +3 746 553 752 +3 761 758 751 +3 764 763 770 +3 765 766 756 +3 772 773 762 +3 759 760 750 +3 776 472 774 +3 768 776 777 +3 771 772 762 +3 769 775 733 +3 753 754 747 +3 754 755 552 +3 782 733 785 +3 790 789 736 +3 788 791 733 +3 549 781 787 +3 548 550 736 +3 780 739 738 +3 785 786 781 +3 784 783 793 +3 731 737 545 +3 731 732 735 +3 544 546 732 +3 778 734 779 +3 819 817 807 +3 821 820 629 +3 822 679 823 +3 22 826 824 +3 825 828 52 +3 15 28 827 +3 192 830 831 +3 174 808 829 +3 329 193 832 +3 833 818 809 +3 834 811 835 +3 836 810 837 +3 812 839 838 +3 813 814 839 +3 840 815 841 +3 842 816 817 +3 844 843 819 +3 847 845 846 +3 863 849 864 +3 849 865 864 +3 848 850 866 +3 850 867 866 +3 851 852 868 +3 868 852 869 +3 870 853 871 +3 854 855 871 +3 872 856 873 +3 858 874 857 +3 877 875 859 +3 861 876 860 +3 862 878 861 +3 879 55 47 +3 881 61 880 +3 882 883 103 +3 884 125 883 +3 885 886 126 +3 126 886 134 +3 888 187 887 +3 890 185 888 +3 185 187 888 +3 891 246 889 +3 19 892 0 +3 898 897 893 +3 899 895 900 +3 901 900 902 +3 903 896 894 +3 429 438 914 +3 445 916 915 +3 919 461 920 +3 466 467 533 +3 921 466 533 +3 922 542 519 +3 904 905 923 +3 528 925 529 +3 928 908 906 +3 910 929 907 +3 931 909 911 +3 932 929 910 +3 930 933 912 +3 914 934 913 +3 917 918 938 +3 944 539 945 +3 926 947 922 +3 947 946 922 +3 948 924 949 +3 950 953 927 +3 954 930 928 +3 955 951 929 +3 935 956 934 +3 936 958 957 +3 937 959 958 +3 939 961 960 +3 940 941 962 +3 962 941 963 +3 942 943 965 +3 964 942 965 +3 952 971 947 +3 971 968 947 +3 966 967 989 +3 989 967 990 +3 971 991 968 +3 969 970 280 +3 974 291 971 +3 291 991 971 +3 285 973 972 +3 993 976 975 +3 305 307 977 +3 994 305 978 +3 980 995 979 +3 981 982 385 +3 983 984 997 +3 984 985 404 +3 986 987 423 +3 987 988 431 +3 291 294 991 +3 280 992 491 +3 278 284 279 +3 296 502 297 +3 310 290 289 +3 501 994 993 +3 511 306 305 +3 439 998 440 +3 440 998 560 +3 566 568 996 +3 385 387 566 +3 386 997 388 +3 997 404 406 +3 426 427 433 +3 491 482 481 +3 490 278 491 +3 263 264 999 +3 283 269 1001 +3 269 268 1003 +3 319 273 1004 +3 1004 1005 340 +3 1003 1004 344 +3 1002 343 1001 +3 1001 354 1000 +3 1000 364 999 +3 510 374 504 +3 505 402 730 +3 730 395 500 + From 1f0a20f2e0ebe03c12d788b24845282255a77bad Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 22 Sep 2026 17:55:54 +0200 Subject: [PATCH 122/151] Fix Triangulation_segment_traverser_3_impl --- .../Triangulation_segment_traverser_3_impl.h | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h b/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h index b7138101256b..9d36635c39fe 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h +++ b/Triangulation_3/include/CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h @@ -22,6 +22,8 @@ #include #include +#include + #include #include #include @@ -274,6 +276,7 @@ walk_to_next() { else { #if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 + CGAL_assertion_code(std::size_t counter = 0); std::cerr << "Starting walk_to_next_3 loop with backup: " << debug_simplex(_cur) << '\n'; std::cerr << "source: " << with_point(_s_vertex) << '\n'; if(_s_vertex == Vertex_handle()) @@ -281,12 +284,18 @@ walk_to_next() { std::cerr << "target: " << with_point(_t_vertex) << '\n'; if(_t_vertex == Vertex_handle()) std::cerr << " _target is " << _target << '\n'; - std::size_t counter = 0; #endif const Simplex backup = _cur; + boost::container::small_vector visited_cells; + if(cell() != Cell_handle{}) + { + visited_cells.push_back(cell()); + cell()->tds_data().mark_processed(); + } + auto _ = make_scope_exit([&](){ for(auto c : visited_cells) c->tds_data().clear(); }); do { #if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 - ++counter; + CGAL_assertion_code(++counter); std::cerr << debug_iterator() << '\n'; if(counter >= _tr->number_of_cells()) { @@ -303,6 +312,11 @@ walk_to_next() { std::pair p = walk_to_next_3(_prev, _cur); _prev = p.first; _cur = p.second; + if(cell() != Cell_handle{}) + { + cell()->tds_data().mark_processed(); + visited_cells.push_back(cell()); + } } while (cell() != Cell_handle()//end && !cell()->has_vertex(_tr->infinite_vertex(), inf) @@ -552,6 +566,8 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre // For the remembering stochastic walk, we start trying with a random facet. CGAL_assertion_code(bool incell = true;) + int pass_number = 0; + const int number_of_pass = cur.lt == Tr::VERTEX ? 2 : 1; int start = 0; int end = 4; if(cur.lt == Tr::VERTEX) { @@ -561,7 +577,7 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre end = cur.li + 4; } - for(int li_ = start; li_ < end; ++li_) + for(int li_ = start; li_ < end || ++pass_number < number_of_pass; ++li_) { const int li = li_ % 4; // Skip the previous cell. @@ -574,7 +590,13 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre #if CGAL_DEBUG_TRIANGULATION_SEGMENT_TRAVERSER_3 std::cerr << "Test facet " << li << ":\n " <vertices(Facet{cur_cell, li}), With_point_tag{}) << '\n'; + if(next->tds_data().processed()) { + std::cerr << " Next cell already processed.\n"; + } #endif + if(next->tds_data().processed()) { + continue; + } const Point* const backup_vert_li = std::exchange(vert[li], &_target); auto exit_guard = CGAL::make_scope_exit([&](){ vert[li] = backup_vert_li; }); @@ -593,7 +615,8 @@ Triangulation_segment_cell_iterator_3::walk_to_next_3(const Simplex& pre if(op[li] == POSITIVE) pos += li; if(op[li] != NEGATIVE) { - continue; + if(pass_number < 1) + continue; } CGAL_assertion_code(incell = false;) From ba00315d48e12dfe0aff57e6f1c5e4b27a431beb Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 1 Oct 2026 16:09:26 +0200 Subject: [PATCH 123/151] small updates for Lab * allow to display cells not in complex on a c3t3 read from a .mesh * allow to read .mesh with negative orientation cells * count inverted cells in statistics --- Lab/demo/Lab/Plugins/Mesh_3/C3t3_io_plugin.cpp | 3 ++- Lab/demo/Lab/Scene_triangulation_3_item.cpp | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Lab/demo/Lab/Plugins/Mesh_3/C3t3_io_plugin.cpp b/Lab/demo/Lab/Plugins/Mesh_3/C3t3_io_plugin.cpp index 4e226a73472e..bbf59d575c61 100644 --- a/Lab/demo/Lab/Plugins/Mesh_3/C3t3_io_plugin.cpp +++ b/Lab/demo/Lab/Plugins/Mesh_3/C3t3_io_plugin.cpp @@ -135,11 +135,11 @@ CGAL_Lab_c3t3_binary_io_plugin::load( CGAL_assertion(!(!in)); item->setName(fileinfo.completeBaseName()); - item->set_valid(false); if(CGAL::IO::read_MEDIT(in, item->c3t3(), CGAL::parameters::verbose(false) .allow_non_manifold(true) + .allow_negative_orientation(true) .rebind_labels(false))) { item->resetCutPlane(); @@ -150,6 +150,7 @@ CGAL_Lab_c3t3_binary_io_plugin::load( } else if(item->c3t3().triangulation().number_of_finite_cells() == 0) { + item->set_valid(false); QMessageBox::warning(CGAL::Three::Three::mainWindow(), tr("C3t3_io_plugin"), tr("No finite cell provided.\n" "Nothing to display."), diff --git a/Lab/demo/Lab/Scene_triangulation_3_item.cpp b/Lab/demo/Lab/Scene_triangulation_3_item.cpp index 629b5be9115b..ec6a0fddd336 100644 --- a/Lab/demo/Lab/Scene_triangulation_3_item.cpp +++ b/Lab/demo/Lab/Scene_triangulation_3_item.cpp @@ -438,7 +438,8 @@ struct Scene_triangulation_3_item_priv { SMALLEST_RAD_RAD, SMALLEST_EDGE_RAD, BIGGEST_VL3_CUBE, - NB_SUBDOMAINS + NB_SUBDOMAINS, + NB_BADLY_ORIENTED_CELLS }; Scene_triangulation_3_item* item; T3 triangulation; @@ -520,6 +521,7 @@ struct Scene_triangulation_3_item_priv { mutable std::size_t nb_subdomains = 0; mutable std::size_t nb_vertices = 0; mutable std::size_t nb_tets = 0; + mutable std::size_t nb_badly_oriented_cells = 0; mutable float smallest_radius_radius = 0.f; mutable float smallest_edge_radius = 0.f; mutable float biggest_v_sma_cube = 0.f; @@ -1837,7 +1839,12 @@ QString Scene_triangulation_3_item::computeStats(int type) const Tr::Bare_point& p1 = wp2p(cit->vertex(1)->point()); const Tr::Bare_point& p2 = wp2p(cit->vertex(2)->point()); const Tr::Bare_point& p3 = wp2p(cit->vertex(3)->point()); - double v = std::abs(CGAL::volume(p0, p1, p2, p3)); + double v = CGAL::volume(p0, p1, p2, p3); + + if(v < 0) + ++d->nb_badly_oriented_cells; + v = std::abs(v); + double circumradius = std::sqrt(CGAL::squared_radius(p0, p1, p2, p3)); //find smallest edge double edges[6]; @@ -1943,6 +1950,8 @@ QString Scene_triangulation_3_item::computeStats(int type) return QString::number(d->biggest_v_sma_cube); case Scene_triangulation_3_item_priv::NB_SUBDOMAINS: return QString::number(d->nb_subdomains); + case Scene_triangulation_3_item_priv::NB_BADLY_ORIENTED_CELLS: + return QString::number(d->nb_badly_oriented_cells); default: return QString(); @@ -1969,6 +1978,7 @@ CGAL::Three::Scene_item::Header_data Scene_triangulation_3_item::header() const data.titles.append(QString("Smallest Edge-Radius Ratio")); data.titles.append(QString("Biggest Vl^3")); data.titles.append(QString("#Subdomains")); + data.titles.append(QString("#Badly Oriented Cells")); return data; } From 7d93922effb78c27a0dcdaa8eb44f0f6ba04f41e Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 1 Oct 2026 16:23:39 +0200 Subject: [PATCH 124/151] use may_have_badly_oriented_cells() --- .../CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h | 8 ++++++-- .../internal/tetrahedral_remeshing_helpers.h | 4 ---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h index 20eda9f88f53..f7d716b89912 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h @@ -1090,7 +1090,9 @@ std::size_t smooth_internal_vertices(C3t3& c3t3, #endif ); } - CGAL_expensive_assertion(CGAL::Tetrahedral_remeshing::debug::are_cell_orientations_valid(tr)); + CGAL_expensive_assertion( + !tr.may_have_badly_oriented_cells() || + CGAL::Tetrahedral_remeshing::debug::are_cell_orientations_valid(tr)); //// end if(!protect_boundaries) ////////////// INTERNAL VERTICES /////////////////////// @@ -1104,7 +1106,9 @@ std::size_t smooth_internal_vertices(C3t3& c3t3, #endif ); - CGAL_expensive_assertion(CGAL::Tetrahedral_remeshing::debug::are_cell_orientations_valid(tr)); + CGAL_expensive_assertion( + !tr.may_have_badly_oriented_cells() || + CGAL::Tetrahedral_remeshing::debug::are_cell_orientations_valid(tr)); #ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE timer.stop(); diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index e30baafdbe59..e209cb71bbb8 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -2234,9 +2234,6 @@ void count_far_points(const C3t3& c3t3) template bool are_cell_orientations_valid([[maybe_unused]] const Tr& tr) { -#ifdef CGAL_T3_ALLOW_NEGATIVE_VOLUME - return true; -#else typedef typename Tr::Geom_traits::Point_3 Point_3; typedef typename Tr::Facet Facet; @@ -2264,7 +2261,6 @@ bool are_cell_orientations_valid([[maybe_unused]] const Tr& tr) dump_facets(facets, "cells_with_negative_volume.polylines.txt"); } return facets.empty(); -#endif } template From 4c36a8bf5e823bfabc981e0ff0055376c89c3cd2 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 1 Oct 2026 16:25:26 +0200 Subject: [PATCH 125/151] deal with the case where no valid subdomain_index was set (may be the case with CDT_3) --- .../internal/tetrahedral_adaptive_remeshing_impl.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h index b07f3862ac27..5af89ecfe739 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h @@ -374,7 +374,9 @@ class Adaptive_remesher const Subdomain_index new_index = (Subdomain_index() != index) ? index : default_subdomain; - m_c3t3.add_to_complex(cit, new_index); + + if(Subdomain_index() != new_index) + m_c3t3.add_to_complex(cit, new_index); #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG ++nbc; @@ -581,6 +583,9 @@ class Adaptive_remesher if (cid > max_index && cid != Subdomain_index()) max_index = cid; } + if(max_index == (std::numeric_limits::min)()) + max_index = Subdomain_index(); + return max_index; } From 26c3dcb8c58bb6fbbf4ffedb321d71ccfc2e7f99 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 1 Oct 2026 16:27:13 +0200 Subject: [PATCH 126/151] add compatibility layer for Surface_patch_index --- ...ained_Delaunay_triangulation_cell_base_3.h | 12 ++++++++- SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h | 27 ++++++++++--------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h index f44460b7b4b6..001407824dae 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h @@ -71,7 +71,9 @@ class Conforming_constrained_Delaunay_triangulation_cell_base_3 ///////// for C3T3 : dummy implementation for compilation purposes using Subdomain_index = int; - Subdomain_index subdomain_index() const { return 1; } // dummy value, not used in the implementation of CCDT + Subdomain_index subdomain_{}; + Subdomain_index subdomain_index() const { return subdomain_; } + // dummy value, not used in the implementation of CCDT // todo : remove this function and the corresponding typedef, as they are not used in the implementation of CCDT, and // are only here to satisfy the requirements of the concept `SimplicialMeshCellBase_3` // for tetrahedral remeshing @@ -81,6 +83,14 @@ class Conforming_constrained_Delaunay_triangulation_cell_base_3 bool is_cache_valid() const { return false;} void set_sliver_value(double) {} double sliver_value() const { return 0.; } + + ///////// valid implementation + template + void set_surface_patch_index(const int i, const Surface_patch_index& patch) + { + this->ccdt_3_data().set_face_constraint_index(i, patch); + } + ///////// end of for C3T3 static std::string io_signature() { diff --git a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h index 8889f63991b2..530bfb864017 100644 --- a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h +++ b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h @@ -164,6 +164,7 @@ bool build_finite_cells(Tr& tr, { typedef typename Tr::Vertex_handle Vertex_handle; typedef typename Tr::Cell_handle Cell_handle; + typedef typename FacetPatchMap::value_type Surface_patch_index; bool success = true; @@ -242,21 +243,21 @@ bool build_finite_cells(Tr& tr, } while(facet[0] != n0); -// typename FacetPatchMap::const_iterator it = border_facets.find(facet); -// if(it != border_facets.end()) -// { -// c->set_surface_patch_index(j, it->second); -// } -// else -// { -// std::swap(facet[1], facet[2]); // facet[0] is still the smallest, no need to rotate again -// -// it = border_facets.find(facet); -// if(it != border_facets.end()) -// c->set_surface_patch_index(j, it->second); + typename FacetPatchMap::const_iterator it = border_facets.find(facet); + if(it != border_facets.end()) + { + c->set_surface_patch_index(j, it->second); + } + else + { + std::swap(facet[1], facet[2]); // facet[0] is still the smallest, no need to rotate again + + it = border_facets.find(facet); + if(it != border_facets.end()) + c->set_surface_patch_index(j, it->second); // else // c->set_surface_patch_index(j, Surface_patch_index()); -// } + } } } } From 19ae7e8eac13704a9b40e95253486d325770c205 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 2 Oct 2026 10:26:26 +0200 Subject: [PATCH 127/151] use c3t3 function --- .../internal/smooth_Steiner_vertices.h | 569 ++++++++++++++++++ .../internal/collapse_short_edges.h | 2 +- 2 files changed, 570 insertions(+), 1 deletion(-) create mode 100644 Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h diff --git a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h new file mode 100644 index 000000000000..2ba81663cbd8 --- /dev/null +++ b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h @@ -0,0 +1,569 @@ +// Copyright (c) 2019-2026 GeometryFactory Sarl (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Jane Tournois + +#ifndef CGAL_INTERNAL_CDT_3_SMOOTH_STEINER_VERTICES_H +#define CGAL_INTERNAL_CDT_3_SMOOTH_STEINER_VERTICES_H + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace CGAL +{ +namespace internal +{ + template + struct Cells_pmap + { + std::shared_ptr> + cells_{ std::make_shared>() }; + + using category = boost::read_write_property_map_tag; + using reference = bool; + using value_type = bool; + using key_type = Cell_handle; + + friend reference get(const Cells_pmap& m, key_type c) { return m.cells_->find(c) != m.cells_->end(); } + friend void put(Cells_pmap& m, key_type c, value_type value) + { + if(value) + m.cells_->insert(c); + else if(m.cells_->find(c) != m.cells_->end()) + m.cells_->erase(c); + } + }; + + template + struct Is_input_vertex_map + { + using category = boost::readable_property_map_tag; + using reference = bool; + using value_type = bool; + using key_type = Vertex_handle; + friend reference get(const Is_input_vertex_map&, key_type v) + { + auto type = v->ccdt_3_data().vertex_type(); + return type == CDT_3_vertex_type::INPUT_VERTEX + || type == CDT_3_vertex_type::BBOX; + } + }; + + template + struct Is_finite_cell_pmap + { + using category = boost::read_write_property_map_tag; + using reference = bool; + using value_type = bool; + using key_type = typename CDT::Cell_handle; + + const typename CDT::Vertex_handle infinite_vertex_; + + friend reference get(const Is_finite_cell_pmap& m, key_type cell) + { + for (int i = 0; i < 4; ++i) + { + if(cell->vertex(i) == m.infinite_vertex_) + return false; + } + return true; + } + friend void put(Is_finite_cell_pmap&, const key_type, const value_type){} + }; + + template + struct Is_not_volume_Steiner_point_map + { + using category = boost::readable_property_map_tag; + using reference = bool; + using value_type = bool; + using key_type = Vertex_handle; + + friend reference get(const Is_not_volume_Steiner_point_map&, key_type v) + { + auto type = v->ccdt_3_data().vertex_type(); + return type != CDT_3_vertex_type::STEINER_IN_VOLUME; + } + }; +} // end namespace internal + +template +std::size_t refine_around_Steiner_points_in_volume(CDT_3& cdt) +{ + std::vector steiner_in_volume; + + for(auto v : cdt.triangulation().finite_vertex_handles()) + if(v->ccdt_3_data().vertex_type() == CDT_3_vertex_type::STEINER_IN_VOLUME) + steiner_in_volume.push_back(v); + + std::size_t nb_insertions = 0; + for(auto v : steiner_in_volume) + { + std::vector cells; + cdt.triangulation().incident_cells(v, std::back_inserter(cells)); + bool do_refine = false; + for(auto c : cells) + { + if(cdt.triangulation().is_infinite(c)) + continue; + if(CGAL::volume(c->vertex(0)->point(), + c->vertex(1)->point(), + c->vertex(2)->point(), + c->vertex(3)->point()) < 0.) + { + do_refine = true; + break; + } + } + + if(do_refine) + { + for(auto c : cells) + { + if(cdt.triangulation().is_infinite(c)) + continue; + const auto p = CGAL::centroid(c->vertex(0)->point(), + c->vertex(1)->point(), + c->vertex(2)->point(), + c->vertex(3)->point()); + auto new_v = cdt.triangulation().insert(p); + new_v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); + ++nb_insertions; + } + } + } + return nb_insertions; +} + +template +std::size_t refine_cells(CDT_3& cdt) +{ + std::size_t nb_insertions = 0; + + struct Cell_and_point + { + typename CDT_3::Cell_handle c; + typename CDT_3::Triangulation::Geom_traits::Point_3 p; + }; + + std::vector cells; + for(auto c : cdt.triangulation().finite_cell_handles()) + { + const auto p = CGAL::centroid(c->vertex(0)->point(), + c->vertex(1)->point(), + c->vertex(2)->point(), + c->vertex(3)->point()); + cells.push_back({c, p}); + } + + struct Facet_from_outside + { + bool in_complex; + typename CDT_3::Facet facet; + typename CDT_3::Surface_patch_index patch = {}; + }; + + for(const auto& [c, p] : cells) + { + //save facets seen from outside c + std::array facets_from_outside; + for (int i = 0; i < 4; ++i) + { + auto fi = cdt.triangulation().mirror_facet({c, i}); + if(cdt.is_in_complex(fi)) + facets_from_outside[i] = {true, fi, cdt.surface_patch_index(fi)}; + else + facets_from_outside[i] = {false, fi, typename CDT_3::Surface_patch_index{}}; + } + + //clear indices from c + for(int i = 0; i < 4; ++i) + if(cdt.is_in_complex(c, i)) + cdt.remove_from_complex(c, i); + + //insert steiner point + auto new_v = cdt.triangulation().insert(p); + new_v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); + ++nb_insertions; + + //restore patches for facets seen from outside c + std::vector new_cells; + cdt.triangulation().finite_incident_cells(new_v, std::back_inserter(new_cells)); + for(auto nc : new_cells) + { + for(int i = 0; i < 4; ++i) + { + auto nfi = cdt.triangulation().mirror_facet({nc, i}); + // restore index + if(facets_from_outside[nfi.second].in_complex) + cdt.add_to_complex(nfi, facets_from_outside[nfi.second].patch); + } + } + } + return nb_insertions; +} + +template +std::size_t count_negative_tetrahedra(const CDT_3& cdt) +{ + const auto& tr = cdt.triangulation(); + return std::count_if( + tr.finite_cells_begin(), + tr.finite_cells_end(), + [&](const auto& c) + { + return CGAL::POSITIVE != CGAL::orientation(tr.point(c.vertex(0)), + tr.point(c.vertex(1)), + tr.point(c.vertex(2)), + tr.point(c.vertex(3))); + }); +} + +template +void remove_from_complex_and_reset_index(const typename CDT_3::Facet& f, CDT_3& cdt) +{ + if(cdt.is_in_complex(f)) + cdt.remove_from_complex(f); + cdt.set_surface_patch_index(f, typename CDT_3::Surface_patch_index{}); +} + +template +auto split_internal_edge(const typename CDT_3::Edge& e, CDT_3& cdt) +{ + using Vertex_handle = typename CDT_3::Vertex_handle; + using Cell_handle = typename CDT_3::Cell_handle; + using Facet = typename CDT_3::Facet; + using Edge = typename CDT_3::Edge; + using Patch_index = typename CDT_3::Surface_patch_index; + + const Vertex_handle v1 = e.first->vertex(e.second); + const Vertex_handle v2 = e.first->vertex(e.third); + Vertex_handle new_v = cdt.triangulation().tds().insert_in_edge(e); + new_v->set_point(CGAL::midpoint(v1->point(), v2->point())); + new_v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); + + // restore facets seen from outside the cells incident to the edge + std::vector new_cells; + cdt.triangulation().finite_incident_cells(new_v, std::back_inserter(new_cells)); + for(Cell_handle new_c : new_cells) + { + const int index_new_v = new_c->index(new_v); + for(int i = 0; i < 4; ++i) + { + const Facet fi(new_c, i); + const Facet mfi = cdt.triangulation().mirror_facet(fi); + + const Patch_index patch = cdt.surface_patch_index(mfi); // backup patch index + + remove_from_complex_and_reset_index(fi, cdt); + remove_from_complex_and_reset_index(mfi, cdt); + + // outer hull facets + if(i == index_new_v && patch != Patch_index{}) + cdt.add_to_complex(mfi, patch); + } + } + return new_v; +} + +template +std::size_t refine_internal_edges(CDT_3& cdt) +{ + using Edge = typename CDT_3::Edge; + + std::size_t nb_insertions = 0; + struct Edge_vv + { + typename CDT_3::Vertex_handle v1, v2; + }; + auto make_edge_vv + = [](typename CDT_3::Vertex_handle v1, + typename CDT_3::Vertex_handle v2) + { + if(v1 < v2) + return Edge_vv{v1, v2}; + else + return Edge_vv{v2, v1}; + }; + + std::vector edges; + for(auto e : cdt.triangulation().finite_edges()) + { + if(cdt.is_in_complex(e)) + continue; + + auto v1 = e.first->vertex(e.second); + auto v2 = e.first->vertex(e.third); + if(v1->ccdt_3_data().vertex_type() == CDT_3_vertex_type::BBOX || + v2->ccdt_3_data().vertex_type() == CDT_3_vertex_type::BBOX) + continue; + + edges.push_back(make_edge_vv(v1, v2)); + } + + std::ofstream ofs("edges_to_refine.polylines.txt"); + for(const auto& [v1, v2] : edges) + ofs << "2 " << v1->point() << " " << v2->point() << std::endl; + ofs.close(); + + std::ofstream ofs2("edges_to_refine_steiner_points.xyz"); + ofs2.precision(17); + CGAL::Static_boolean_property_map select; + for(const auto& [v1, v2] : edges) + { + typename CDT_3::Cell_handle c; + int i, j; + if (cdt.triangulation().tds().is_edge(v1, v2, c, i, j)) + { + //auto new_v = CGAL::Tetrahedral_remeshing::internal::split_edge({c, i, j}, select, cdt); + auto new_v = split_internal_edge(Edge(c, i, j), cdt); + //if(new_v != typename CDT_3::Vertex_handle()) + { + //new_v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); + ++nb_insertions; + ofs2 << new_v->point() << std::endl; + } + } + + std::ofstream ofs00("out_after_edge_split.mesh"); + ofs00.precision(17); + CGAL::IO::write_MEDIT(ofs00, cdt); + ofs00.close(); + std::cout << "Refined edge " << v1->point() << " " << v2->point() << std::endl; + std::cout << "File " << "out_after_edge_split.mesh" << " written." << std::endl; + std::cout << std::endl; + } + ofs2.close(); + + return nb_insertions; +} + +template +std::size_t perturb_slivers(CDT_3& cdt, const double bbox_max_span) +{ + const double min_volume = bbox_max_span * bbox_max_span * bbox_max_span * 1e-8; + std::cout << "Perturb slivers with volume < " << min_volume << std::endl; + + using GT = typename CDT_3::Triangulation::Geom_traits; + using Vector_3 = typename GT::Vector_3; + + CGAL::Random rng; + std::size_t nb_perturbations = 0; + for(auto v : cdt.triangulation().finite_vertex_handles()) + { + if(v->ccdt_3_data().vertex_type() != CDT_3_vertex_type::STEINER_IN_VOLUME) + continue; + std::vector cells; + cdt.triangulation().finite_incident_cells(v, std::back_inserter(cells)); + bool do_perturb = false; + for(auto c : cells) + { + if(CGAL::abs(CGAL::volume(c->vertex(0)->point(), + c->vertex(1)->point(), + c->vertex(2)->point(), + c->vertex(3)->point())) < min_volume) + { + do_perturb = true; + break; + } + } + if(do_perturb) + { + const auto& p = v->point(); + const Vector_3 perturbation(rng.get_double(-1e-2, 1e-2) * p.x(), + rng.get_double(-1e-2, 1e-2) * p.y(), + rng.get_double(-1e-2, 1e-2) * p.z()); + const auto p_perturbed = p + perturbation; + v->set_point(p_perturbed); + ++nb_perturbations; + } + } + return nb_perturbations; +} + +template +void dump_negative_tetrahedra(const char* filename, const CDT_3& cdt) +{ + std::ofstream ofs(filename); + ofs.precision(17); + for(auto c : cdt.finite_cell_handles()) { + if(CGAL::volume(c->vertex(0)->point(), + c->vertex(1)->point(), + c->vertex(2)->point(), + c->vertex(3)->point()) < 0.) + { + ofs << "2 " << c->vertex(0)->point() << " " << c->vertex(1)->point() << std::endl; + ofs << "2 " << c->vertex(0)->point() << " " << c->vertex(2)->point() << std::endl; + ofs << "2 " << c->vertex(0)->point() << " " << c->vertex(3)->point() << std::endl; + ofs << "2 " << c->vertex(1)->point() << " " << c->vertex(2)->point() << std::endl; + ofs << "2 " << c->vertex(1)->point() << " " << c->vertex(3)->point() << std::endl; + ofs << "2 " << c->vertex(2)->point() << " " << c->vertex(3)->point() << std::endl; + } + } + + ofs.close(); +} + + template + void smooth_Steiner_vertices_in_volume(CDT_3& cdt, + const double bbox_max_span, + const bool remesh, + const bool refine_and_smooth) + { + using Cell_handle = typename CDT_3::Cell_handle; + using Vertex_handle = typename CDT_3::Vertex_handle; + + std::cout << "#Negative tetrahedra before Steiner smoothing: " + << count_negative_tetrahedra(cdt) << std::endl; + + std::ofstream ofs00("out_before_steiner_smoothing.mesh"); + ofs00.precision(17); + CGAL::IO::write_MEDIT(ofs00, cdt, CGAL::parameters::all_cells(true)); + ofs00.close(); + + if (count_negative_tetrahedra(cdt) == 0) + { + std::cout << "Good :-) No negative tetrahedra left!" << std::endl; + return; + } + + cdt.triangulation().may_have_badly_oriented_cells(true); + + if(remesh) + { + internal::Is_input_vertex_map is_input_vertex_pmap; + internal::Is_finite_cell_pmap is_finite_cell{cdt.triangulation().infinite_vertex()}; + + // collapse and smooth only + CGAL::tetrahedral_isotropic_remeshing(cdt, bbox_max_span, + CGAL::parameters::number_of_iterations(5) + .nb_smoothing_iterations(3) + .nb_flip_smooth_iterations(0) + .do_split(false) + .do_collapse(true) + .do_flip(true) + .remesh_boundaries(false) + .cell_is_selected_map(is_finite_cell) + .vertex_is_constrained_map(is_input_vertex_pmap)); + + std::cout << std::endl; + std::cout << "AFTER COLLAPSE" << std::endl; + std::cout << cdt.statistics() << std::endl; + + std::ofstream ofs("out_after_collapse.mesh"); + ofs.precision(17); + CGAL::IO::write_MEDIT(ofs, cdt); + ofs.close(); + if(count_negative_tetrahedra(cdt) == 0) + { + std::cout << "Good :-) No negative tetrahedra left after collapse!" << std::endl; + return; + } + + // split long internal edges + const double target_edge_length = 0.0005 * bbox_max_span; + CGAL::tetrahedral_isotropic_remeshing(cdt, target_edge_length, + CGAL::parameters::number_of_iterations(5) + .nb_smoothing_iterations(5) + .nb_flip_smooth_iterations(5) + .do_split(true) + .do_collapse(true) + .do_flip(true) + .remesh_boundaries(false) + .vertex_is_constrained_map(is_input_vertex_pmap)); + + std::cout << std::endl; + std::cout << "AFTER SPLIT AND BEFORE SMOOTH" << std::endl; + std::cout << cdt.statistics() << std::endl; + + if(count_negative_tetrahedra(cdt) == 0) + { + std::cout << "Good :-) No negative tetrahedra left after remeshing!" << std::endl; + return; + } + } + + //if(refine_and_smooth) + for(int i = 0; i < 2; ++i) + { + std::cout << "Refining around Steiner points in volume..."; + std::cout.flush(); + std::size_t nb_insertions = refine_cells(cdt); + + std::ofstream ofs0("out_after_cells_refinement.mesh"); + ofs0.precision(17); + CGAL::IO::write_MEDIT(ofs0, cdt); + ofs0.close(); + dump_negative_tetrahedra("out_negative_tetrahedra_after_cells_refinement.polylines.txt", cdt); + +// nb_insertions += refine_internal_edges(cdt); + //refine_around_Steiner_points_in_volume(cdt); + std::cout << "done (" << nb_insertions << " points inserted)" << std::endl; + std::cout << cdt.statistics() << std::endl; + + std::ofstream ofs("out_after_refinement.mesh"); + ofs.precision(17); + CGAL::IO::write_MEDIT(ofs, cdt); + ofs.close(); + dump_negative_tetrahedra("out_negative_tetrahedra_after_refinement.polylines.txt", cdt); + +// std::cout << std::endl; +// std::cout << "Perturb slivers..."; +// std::cout.flush(); +// std::size_t nb_perturbations = perturb_slivers(cdt, bbox_max_span); +// std::cout << "done (" << nb_perturbations << " perturbations)" << std::endl; +// std::cout << cdt.statistics() << std::endl; +// +// std::ofstream ofs1("out_after_perturb.mesh"); +// ofs1.precision(17); +// CGAL::IO::write_MEDIT(ofs1, cdt); +// ofs1.close(); +// dump_negative_tetrahedra +// ("out_negative_tetrahedra_after_perturb.polylines.txt", cdt); + + std::cout << std::endl; + std::cout << "Mesh smoothing around Steiner points in volume..."; + std::cout.flush(); + internal::Is_not_volume_Steiner_point_map is_not_volume_Steiner_point_pmap; + CGAL::boundary_aware_mesh_smoothing( + cdt, + CGAL::Mesh_smoothing_3::C3t3_no_projection(), + CGAL::parameters::vertex_is_constrained_map(is_not_volume_Steiner_point_pmap) + .number_of_iterations(1500)); + std::cout << "done." << std::endl; + std::cout << cdt.statistics() << std::endl; + + std::ofstream ofs2("out_after_refinement_and_smoothing.mesh"); + ofs2.precision(17); + CGAL::IO::write_MEDIT(ofs2, cdt); + ofs2.close(); + dump_negative_tetrahedra("out_negative_tetrahedra_after_refinement_and_smoothing.polylines.txt", cdt); + + if(count_negative_tetrahedra(cdt) == 0) + { + std::cout << "Good :-) No negative tetrahedra left after smoothing!" << std::endl; + return; + } + } + + std::cout << cdt.statistics() << std::endl; + } +} // namespace CGAL + +#endif // CGAL_INTERNAL_CDT_3_SMOOTH_STEINER_VERTICES_H diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index 14854d0b037f..4de3f8439e3b 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -1443,7 +1443,7 @@ auto can_be_collapsed(const typename C3T3::Edge& e, const auto v0 = e.first->vertex(e.second); const auto v1 = e.first->vertex(e.third); - if(v0->in_dimension() != 3 && v1->in_dimension() != 3) + if(c3t3.in_dimension(v0) != 3 && c3t3.in_dimension(v1) != 3) { const auto patch_v0 = patch_cache ? cached_surface_patch_index(v0, c3t3, *patch_cache) From 50b1ab56d1d84d1bd037b54dc6cc9698146d5e85 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 2 Oct 2026 10:27:09 +0200 Subject: [PATCH 128/151] use subdomain_index() in is_in_complex(c) --- .../Conforming_constrained_Delaunay_triangulation_3.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 07a3fca48a7e..50993d2db08f 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -676,13 +676,8 @@ class Conforming_constrained_Delaunay_triangulation_3 bool is_in_complex(const Cell_handle& c) const { - return !triangulation().is_infinite(c); - for(const auto v : triangulation().vertices(c)) - { - if(impl().vertex_type(v) == CDT_3_vertex_type::BBOX) - return false; - } - return true; + Subdomain_index default_index{}; + return c->subdomain_index() != default_index; } bool is_in_complex(const Facet& f) const { From 68abec9b6e616222365b567378ebefd80ed326ed Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 2 Oct 2026 14:18:03 +0200 Subject: [PATCH 129/151] wip to fix inverted cells --- .../internal/smooth_Steiner_vertices.h | 588 +++--------------- .../cdt_3_from_off.cpp | 15 +- 2 files changed, 106 insertions(+), 497 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h index 2ba81663cbd8..eadc112992de 100644 --- a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h +++ b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h @@ -14,7 +14,6 @@ #include -#include #include #include @@ -29,372 +28,57 @@ namespace CGAL { namespace internal { - template - struct Cells_pmap - { - std::shared_ptr> - cells_{ std::make_shared>() }; - - using category = boost::read_write_property_map_tag; - using reference = bool; - using value_type = bool; - using key_type = Cell_handle; - - friend reference get(const Cells_pmap& m, key_type c) { return m.cells_->find(c) != m.cells_->end(); } - friend void put(Cells_pmap& m, key_type c, value_type value) - { - if(value) - m.cells_->insert(c); - else if(m.cells_->find(c) != m.cells_->end()) - m.cells_->erase(c); - } - }; - - template - struct Is_input_vertex_map - { - using category = boost::readable_property_map_tag; - using reference = bool; - using value_type = bool; - using key_type = Vertex_handle; - friend reference get(const Is_input_vertex_map&, key_type v) - { - auto type = v->ccdt_3_data().vertex_type(); - return type == CDT_3_vertex_type::INPUT_VERTEX - || type == CDT_3_vertex_type::BBOX; - } - }; - - template - struct Is_finite_cell_pmap - { - using category = boost::read_write_property_map_tag; - using reference = bool; - using value_type = bool; - using key_type = typename CDT::Cell_handle; - - const typename CDT::Vertex_handle infinite_vertex_; - - friend reference get(const Is_finite_cell_pmap& m, key_type cell) - { - for (int i = 0; i < 4; ++i) - { - if(cell->vertex(i) == m.infinite_vertex_) - return false; - } - return true; - } - friend void put(Is_finite_cell_pmap&, const key_type, const value_type){} - }; - - template - struct Is_not_volume_Steiner_point_map - { - using category = boost::readable_property_map_tag; - using reference = bool; - using value_type = bool; - using key_type = Vertex_handle; - friend reference get(const Is_not_volume_Steiner_point_map&, key_type v) - { - auto type = v->ccdt_3_data().vertex_type(); - return type != CDT_3_vertex_type::STEINER_IN_VOLUME; - } - }; -} // end namespace internal - -template -std::size_t refine_around_Steiner_points_in_volume(CDT_3& cdt) +template +struct Is_input_vertex_map { - std::vector steiner_in_volume; - - for(auto v : cdt.triangulation().finite_vertex_handles()) - if(v->ccdt_3_data().vertex_type() == CDT_3_vertex_type::STEINER_IN_VOLUME) - steiner_in_volume.push_back(v); - - std::size_t nb_insertions = 0; - for(auto v : steiner_in_volume) + using category = boost::readable_property_map_tag; + using reference = bool; + using value_type = bool; + using key_type = Vertex_handle; + friend reference get(const Is_input_vertex_map&, key_type v) { - std::vector cells; - cdt.triangulation().incident_cells(v, std::back_inserter(cells)); - bool do_refine = false; - for(auto c : cells) - { - if(cdt.triangulation().is_infinite(c)) - continue; - if(CGAL::volume(c->vertex(0)->point(), - c->vertex(1)->point(), - c->vertex(2)->point(), - c->vertex(3)->point()) < 0.) - { - do_refine = true; - break; - } - } - - if(do_refine) - { - for(auto c : cells) - { - if(cdt.triangulation().is_infinite(c)) - continue; - const auto p = CGAL::centroid(c->vertex(0)->point(), - c->vertex(1)->point(), - c->vertex(2)->point(), - c->vertex(3)->point()); - auto new_v = cdt.triangulation().insert(p); - new_v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); - ++nb_insertions; - } - } + auto type = v->ccdt_3_data().vertex_type(); + return type == CDT_3_vertex_type::INPUT_VERTEX || type == CDT_3_vertex_type::BBOX; } - return nb_insertions; -} +}; -template -std::size_t refine_cells(CDT_3& cdt) +template +struct Is_finite_cell_pmap { - std::size_t nb_insertions = 0; + using category = boost::read_write_property_map_tag; + using reference = bool; + using value_type = bool; + using key_type = typename CDT::Cell_handle; - struct Cell_and_point - { - typename CDT_3::Cell_handle c; - typename CDT_3::Triangulation::Geom_traits::Point_3 p; - }; + const typename CDT::Vertex_handle infinite_vertex_; - std::vector cells; - for(auto c : cdt.triangulation().finite_cell_handles()) + friend reference get(const Is_finite_cell_pmap& m, key_type cell) { - const auto p = CGAL::centroid(c->vertex(0)->point(), - c->vertex(1)->point(), - c->vertex(2)->point(), - c->vertex(3)->point()); - cells.push_back({c, p}); - } - - struct Facet_from_outside - { - bool in_complex; - typename CDT_3::Facet facet; - typename CDT_3::Surface_patch_index patch = {}; - }; - - for(const auto& [c, p] : cells) - { - //save facets seen from outside c - std::array facets_from_outside; - for (int i = 0; i < 4; ++i) - { - auto fi = cdt.triangulation().mirror_facet({c, i}); - if(cdt.is_in_complex(fi)) - facets_from_outside[i] = {true, fi, cdt.surface_patch_index(fi)}; - else - facets_from_outside[i] = {false, fi, typename CDT_3::Surface_patch_index{}}; - } - - //clear indices from c for(int i = 0; i < 4; ++i) - if(cdt.is_in_complex(c, i)) - cdt.remove_from_complex(c, i); - - //insert steiner point - auto new_v = cdt.triangulation().insert(p); - new_v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); - ++nb_insertions; - - //restore patches for facets seen from outside c - std::vector new_cells; - cdt.triangulation().finite_incident_cells(new_v, std::back_inserter(new_cells)); - for(auto nc : new_cells) { - for(int i = 0; i < 4; ++i) - { - auto nfi = cdt.triangulation().mirror_facet({nc, i}); - // restore index - if(facets_from_outside[nfi.second].in_complex) - cdt.add_to_complex(nfi, facets_from_outside[nfi.second].patch); - } + if(cell->vertex(i) == m.infinite_vertex_) + return false; } + return true; } - return nb_insertions; -} + friend void put(Is_finite_cell_pmap&, const key_type, const value_type) {} +}; -template +template std::size_t count_negative_tetrahedra(const CDT_3& cdt) { const auto& tr = cdt.triangulation(); return std::count_if( - tr.finite_cells_begin(), - tr.finite_cells_end(), - [&](const auto& c) - { - return CGAL::POSITIVE != CGAL::orientation(tr.point(c.vertex(0)), - tr.point(c.vertex(1)), - tr.point(c.vertex(2)), - tr.point(c.vertex(3))); - }); -} - -template -void remove_from_complex_and_reset_index(const typename CDT_3::Facet& f, CDT_3& cdt) -{ - if(cdt.is_in_complex(f)) - cdt.remove_from_complex(f); - cdt.set_surface_patch_index(f, typename CDT_3::Surface_patch_index{}); -} - -template -auto split_internal_edge(const typename CDT_3::Edge& e, CDT_3& cdt) -{ - using Vertex_handle = typename CDT_3::Vertex_handle; - using Cell_handle = typename CDT_3::Cell_handle; - using Facet = typename CDT_3::Facet; - using Edge = typename CDT_3::Edge; - using Patch_index = typename CDT_3::Surface_patch_index; - - const Vertex_handle v1 = e.first->vertex(e.second); - const Vertex_handle v2 = e.first->vertex(e.third); - Vertex_handle new_v = cdt.triangulation().tds().insert_in_edge(e); - new_v->set_point(CGAL::midpoint(v1->point(), v2->point())); - new_v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); - - // restore facets seen from outside the cells incident to the edge - std::vector new_cells; - cdt.triangulation().finite_incident_cells(new_v, std::back_inserter(new_cells)); - for(Cell_handle new_c : new_cells) - { - const int index_new_v = new_c->index(new_v); - for(int i = 0; i < 4; ++i) - { - const Facet fi(new_c, i); - const Facet mfi = cdt.triangulation().mirror_facet(fi); - - const Patch_index patch = cdt.surface_patch_index(mfi); // backup patch index - - remove_from_complex_and_reset_index(fi, cdt); - remove_from_complex_and_reset_index(mfi, cdt); - - // outer hull facets - if(i == index_new_v && patch != Patch_index{}) - cdt.add_to_complex(mfi, patch); - } - } - return new_v; -} - -template -std::size_t refine_internal_edges(CDT_3& cdt) -{ - using Edge = typename CDT_3::Edge; - - std::size_t nb_insertions = 0; - struct Edge_vv - { - typename CDT_3::Vertex_handle v1, v2; - }; - auto make_edge_vv - = [](typename CDT_3::Vertex_handle v1, - typename CDT_3::Vertex_handle v2) - { - if(v1 < v2) - return Edge_vv{v1, v2}; - else - return Edge_vv{v2, v1}; - }; - - std::vector edges; - for(auto e : cdt.triangulation().finite_edges()) - { - if(cdt.is_in_complex(e)) - continue; - - auto v1 = e.first->vertex(e.second); - auto v2 = e.first->vertex(e.third); - if(v1->ccdt_3_data().vertex_type() == CDT_3_vertex_type::BBOX || - v2->ccdt_3_data().vertex_type() == CDT_3_vertex_type::BBOX) - continue; - - edges.push_back(make_edge_vv(v1, v2)); - } - - std::ofstream ofs("edges_to_refine.polylines.txt"); - for(const auto& [v1, v2] : edges) - ofs << "2 " << v1->point() << " " << v2->point() << std::endl; - ofs.close(); - - std::ofstream ofs2("edges_to_refine_steiner_points.xyz"); - ofs2.precision(17); - CGAL::Static_boolean_property_map select; - for(const auto& [v1, v2] : edges) - { - typename CDT_3::Cell_handle c; - int i, j; - if (cdt.triangulation().tds().is_edge(v1, v2, c, i, j)) - { - //auto new_v = CGAL::Tetrahedral_remeshing::internal::split_edge({c, i, j}, select, cdt); - auto new_v = split_internal_edge(Edge(c, i, j), cdt); - //if(new_v != typename CDT_3::Vertex_handle()) - { - //new_v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); - ++nb_insertions; - ofs2 << new_v->point() << std::endl; - } - } - - std::ofstream ofs00("out_after_edge_split.mesh"); - ofs00.precision(17); - CGAL::IO::write_MEDIT(ofs00, cdt); - ofs00.close(); - std::cout << "Refined edge " << v1->point() << " " << v2->point() << std::endl; - std::cout << "File " << "out_after_edge_split.mesh" << " written." << std::endl; - std::cout << std::endl; - } - ofs2.close(); - - return nb_insertions; -} - -template -std::size_t perturb_slivers(CDT_3& cdt, const double bbox_max_span) -{ - const double min_volume = bbox_max_span * bbox_max_span * bbox_max_span * 1e-8; - std::cout << "Perturb slivers with volume < " << min_volume << std::endl; - - using GT = typename CDT_3::Triangulation::Geom_traits; - using Vector_3 = typename GT::Vector_3; - - CGAL::Random rng; - std::size_t nb_perturbations = 0; - for(auto v : cdt.triangulation().finite_vertex_handles()) - { - if(v->ccdt_3_data().vertex_type() != CDT_3_vertex_type::STEINER_IN_VOLUME) - continue; - std::vector cells; - cdt.triangulation().finite_incident_cells(v, std::back_inserter(cells)); - bool do_perturb = false; - for(auto c : cells) - { - if(CGAL::abs(CGAL::volume(c->vertex(0)->point(), - c->vertex(1)->point(), - c->vertex(2)->point(), - c->vertex(3)->point())) < min_volume) - { - do_perturb = true; - break; - } - } - if(do_perturb) - { - const auto& p = v->point(); - const Vector_3 perturbation(rng.get_double(-1e-2, 1e-2) * p.x(), - rng.get_double(-1e-2, 1e-2) * p.y(), - rng.get_double(-1e-2, 1e-2) * p.z()); - const auto p_perturbed = p + perturbation; - v->set_point(p_perturbed); - ++nb_perturbations; - } - } - return nb_perturbations; + tr.finite_cells_begin(), + tr.finite_cells_end(), + [&](const auto& c) + { + return CGAL::POSITIVE != CGAL::orientation(tr.point(c.vertex(0)), + tr.point(c.vertex(1)), + tr.point(c.vertex(2)), + tr.point(c.vertex(3))); + }); } template @@ -402,11 +86,10 @@ void dump_negative_tetrahedra(const char* filename, const CDT_3& cdt) { std::ofstream ofs(filename); ofs.precision(17); - for(auto c : cdt.finite_cell_handles()) { - if(CGAL::volume(c->vertex(0)->point(), - c->vertex(1)->point(), - c->vertex(2)->point(), - c->vertex(3)->point()) < 0.) + for(auto c : cdt.finite_cell_handles()) + { + if(CGAL::volume(c->vertex(0)->point(), c->vertex(1)->point(), + c->vertex(2)->point(), c->vertex(3)->point()) < 0.) { ofs << "2 " << c->vertex(0)->point() << " " << c->vertex(1)->point() << std::endl; ofs << "2 " << c->vertex(0)->point() << " " << c->vertex(2)->point() << std::endl; @@ -416,154 +99,87 @@ void dump_negative_tetrahedra(const char* filename, const CDT_3& cdt) ofs << "2 " << c->vertex(2)->point() << " " << c->vertex(3)->point() << std::endl; } } - ofs.close(); } - template - void smooth_Steiner_vertices_in_volume(CDT_3& cdt, - const double bbox_max_span, - const bool remesh, - const bool refine_and_smooth) - { - using Cell_handle = typename CDT_3::Cell_handle; - using Vertex_handle = typename CDT_3::Vertex_handle; - - std::cout << "#Negative tetrahedra before Steiner smoothing: " - << count_negative_tetrahedra(cdt) << std::endl; - - std::ofstream ofs00("out_before_steiner_smoothing.mesh"); - ofs00.precision(17); - CGAL::IO::write_MEDIT(ofs00, cdt, CGAL::parameters::all_cells(true)); - ofs00.close(); - - if (count_negative_tetrahedra(cdt) == 0) - { - std::cout << "Good :-) No negative tetrahedra left!" << std::endl; - return; - } - - cdt.triangulation().may_have_badly_oriented_cells(true); +template +void smooth_Steiner_vertices_in_volume(CDT_3& cdt, + const double bbox_max_span, + const bool remesh) +{ + using Cell_handle = typename CDT_3::Cell_handle; + using Vertex_handle = typename CDT_3::Vertex_handle; - if(remesh) - { - internal::Is_input_vertex_map is_input_vertex_pmap; - internal::Is_finite_cell_pmap is_finite_cell{cdt.triangulation().infinite_vertex()}; + std::cout << "#Negative tetrahedra before Steiner smoothing: " << count_negative_tetrahedra(cdt) << std::endl; - // collapse and smooth only - CGAL::tetrahedral_isotropic_remeshing(cdt, bbox_max_span, - CGAL::parameters::number_of_iterations(5) - .nb_smoothing_iterations(3) - .nb_flip_smooth_iterations(0) - .do_split(false) - .do_collapse(true) - .do_flip(true) - .remesh_boundaries(false) - .cell_is_selected_map(is_finite_cell) - .vertex_is_constrained_map(is_input_vertex_pmap)); + std::ofstream ofs00("out_before_steiner_smoothing.mesh"); + ofs00.precision(17); + CGAL::IO::write_MEDIT(ofs00, cdt, CGAL::parameters::all_cells(true)); + ofs00.close(); - std::cout << std::endl; - std::cout << "AFTER COLLAPSE" << std::endl; - std::cout << cdt.statistics() << std::endl; + if(count_negative_tetrahedra(cdt) == 0) { + std::cout << "Good :-) No negative tetrahedra left!" << std::endl; + return; + } - std::ofstream ofs("out_after_collapse.mesh"); - ofs.precision(17); - CGAL::IO::write_MEDIT(ofs, cdt); - ofs.close(); - if(count_negative_tetrahedra(cdt) == 0) - { - std::cout << "Good :-) No negative tetrahedra left after collapse!" << std::endl; - return; - } + cdt.triangulation().may_have_badly_oriented_cells(true); - // split long internal edges - const double target_edge_length = 0.0005 * bbox_max_span; - CGAL::tetrahedral_isotropic_remeshing(cdt, target_edge_length, - CGAL::parameters::number_of_iterations(5) - .nb_smoothing_iterations(5) - .nb_flip_smooth_iterations(5) - .do_split(true) - .do_collapse(true) - .do_flip(true) - .remesh_boundaries(false) - .vertex_is_constrained_map(is_input_vertex_pmap)); + if(remesh) + { + internal::Is_input_vertex_map is_input_vertex_pmap; + internal::Is_finite_cell_pmap is_finite_cell{cdt.triangulation().infinite_vertex()}; + + // collapse and smooth only + CGAL::tetrahedral_isotropic_remeshing(cdt, bbox_max_span, + CGAL::parameters::number_of_iterations(5) + .nb_smoothing_iterations(3) + .nb_flip_smooth_iterations(0) + .do_split(false) + .do_collapse(true) + .do_flip(true) + .remesh_boundaries(false) + .cell_is_selected_map(is_finite_cell) + .vertex_is_constrained_map(is_input_vertex_pmap)); - std::cout << std::endl; - std::cout << "AFTER SPLIT AND BEFORE SMOOTH" << std::endl; - std::cout << cdt.statistics() << std::endl; + std::cout << std::endl; + std::cout << "AFTER COLLAPSE" << std::endl; + std::cout << cdt.statistics() << std::endl; - if(count_negative_tetrahedra(cdt) == 0) - { - std::cout << "Good :-) No negative tetrahedra left after remeshing!" << std::endl; - return; - } + std::ofstream ofs("out_after_collapse.mesh"); + ofs.precision(17); + CGAL::IO::write_MEDIT(ofs, cdt); + ofs.close(); + if(count_negative_tetrahedra(cdt) == 0) { + std::cout << "Good :-) No negative tetrahedra left after collapse!" << std::endl; + return; } - //if(refine_and_smooth) - for(int i = 0; i < 2; ++i) - { - std::cout << "Refining around Steiner points in volume..."; - std::cout.flush(); - std::size_t nb_insertions = refine_cells(cdt); - - std::ofstream ofs0("out_after_cells_refinement.mesh"); - ofs0.precision(17); - CGAL::IO::write_MEDIT(ofs0, cdt); - ofs0.close(); - dump_negative_tetrahedra("out_negative_tetrahedra_after_cells_refinement.polylines.txt", cdt); - -// nb_insertions += refine_internal_edges(cdt); - //refine_around_Steiner_points_in_volume(cdt); - std::cout << "done (" << nb_insertions << " points inserted)" << std::endl; - std::cout << cdt.statistics() << std::endl; - - std::ofstream ofs("out_after_refinement.mesh"); - ofs.precision(17); - CGAL::IO::write_MEDIT(ofs, cdt); - ofs.close(); - dump_negative_tetrahedra("out_negative_tetrahedra_after_refinement.polylines.txt", cdt); - -// std::cout << std::endl; -// std::cout << "Perturb slivers..."; -// std::cout.flush(); -// std::size_t nb_perturbations = perturb_slivers(cdt, bbox_max_span); -// std::cout << "done (" << nb_perturbations << " perturbations)" << std::endl; -// std::cout << cdt.statistics() << std::endl; -// -// std::ofstream ofs1("out_after_perturb.mesh"); -// ofs1.precision(17); -// CGAL::IO::write_MEDIT(ofs1, cdt); -// ofs1.close(); -// dump_negative_tetrahedra -// ("out_negative_tetrahedra_after_perturb.polylines.txt", cdt); - - std::cout << std::endl; - std::cout << "Mesh smoothing around Steiner points in volume..."; - std::cout.flush(); - internal::Is_not_volume_Steiner_point_map is_not_volume_Steiner_point_pmap; - CGAL::boundary_aware_mesh_smoothing( - cdt, - CGAL::Mesh_smoothing_3::C3t3_no_projection(), - CGAL::parameters::vertex_is_constrained_map(is_not_volume_Steiner_point_pmap) - .number_of_iterations(1500)); - std::cout << "done." << std::endl; - std::cout << cdt.statistics() << std::endl; + // split long internal edges + const double target_edge_length = 0.0005 * bbox_max_span; + CGAL::tetrahedral_isotropic_remeshing(cdt, target_edge_length, + CGAL::parameters::number_of_iterations(5) + .nb_smoothing_iterations(5) + .nb_flip_smooth_iterations(10) + .do_split(true) + .do_collapse(true) + .do_flip(true) + .remesh_boundaries(false) + .vertex_is_constrained_map(is_input_vertex_pmap)); - std::ofstream ofs2("out_after_refinement_and_smoothing.mesh"); - ofs2.precision(17); - CGAL::IO::write_MEDIT(ofs2, cdt); - ofs2.close(); - dump_negative_tetrahedra("out_negative_tetrahedra_after_refinement_and_smoothing.polylines.txt", cdt); + std::cout << std::endl; + std::cout << "AFTER SPLIT AND BEFORE SMOOTH" << std::endl; + std::cout << cdt.statistics() << std::endl; - if(count_negative_tetrahedra(cdt) == 0) - { - std::cout << "Good :-) No negative tetrahedra left after smoothing!" << std::endl; - return; - } + if(count_negative_tetrahedra(cdt) == 0) + { + std::cout << "Good :-) No negative tetrahedra left after remeshing!" << std::endl; + return; } - - std::cout << cdt.statistics() << std::endl; } + std::cout << cdt.statistics() << std::endl; +} + +} // namespace internal } // namespace CGAL #endif // CGAL_INTERNAL_CDT_3_SMOOTH_STEINER_VERTICES_H diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 20102dd18182..b9ea8a0ac797 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -1,7 +1,6 @@ #include -#define CGAL_T3_ALLOW_NEGATIVE_VOLUME 1 -#define CGAL_TETRAHEDRAL_REMESHING_VERBOSE 1 +//#define CGAL_TETRAHEDRAL_REMESHING_VERBOSE 1 // #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1 #include #include @@ -23,7 +22,7 @@ #include #include #include -//#include +#include #include #include @@ -135,7 +134,7 @@ struct CDT_options false; #endif bool move_Steiner_vertices_to_the_volume = true; - bool allow_moving_Steiner_vertices_to_create_negative_tets = false; + bool allow_moving_Steiner_vertices_to_create_negative_tets = true; bool use_epeck_for_normals = false; bool use_epeck_for_Steiner_points = false; bool call_is_valid = true; @@ -815,13 +814,7 @@ int go(Mesh mesh, CDT_options options) { << move_vertices_guard.time_ms() << " ms\n"; std::cout << cdt.statistics() << "\n"; } - // CGAL::tetrahedral_isotropic_remeshing( - // cdt, - // 3 * bbox_max_span, - // CGAL::parameters::number_of_iterations(30).nb_flip_smooth_iterations(20u) - // .remesh_boundaries(false)); - // CGAL_assertion(cdt.tr().tds().is_valid(true)); - // std::cout << cdt.statistics() << "\n"; + CGAL::internal::smooth_Steiner_vertices_in_volume(cdt, bbox_max_span, true); } return EXIT_SUCCESS; From 8757d8f40356260f020dffedf9716c1908a18549 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 5 Oct 2026 09:34:32 +0200 Subject: [PATCH 130/151] fix includes --- .../include/CGAL/Adaptive_remeshing_sizing_field.h | 1 - .../internal/tetrahedral_remeshing_helpers.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Adaptive_remeshing_sizing_field.h b/Tetrahedral_remeshing/include/CGAL/Adaptive_remeshing_sizing_field.h index b65a4f2d849c..ef3df8263780 100644 --- a/Tetrahedral_remeshing/include/CGAL/Adaptive_remeshing_sizing_field.h +++ b/Tetrahedral_remeshing/include/CGAL/Adaptive_remeshing_sizing_field.h @@ -33,7 +33,6 @@ #include #include -#include #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG #include diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index e209cb71bbb8..de53b7586edb 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -25,6 +25,7 @@ #include #include #include +#include #include From d094740b01bf9087c2fd5b4c91c625595abdb502 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 5 Oct 2026 09:36:45 +0200 Subject: [PATCH 131/151] add the licence header include --- .../internal/smooth_Steiner_vertices.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h index eadc112992de..a465693c800b 100644 --- a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h +++ b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h @@ -12,6 +12,8 @@ #ifndef CGAL_INTERNAL_CDT_3_SMOOTH_STEINER_VERTICES_H #define CGAL_INTERNAL_CDT_3_SMOOTH_STEINER_VERTICES_H +#include + #include #include From d5e1d98e43957dac09b5840d44d7fa5356da104b Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 8 Oct 2026 16:03:35 +0200 Subject: [PATCH 132/151] add plc_face_id to dump --- .../internal/smooth_Steiner_vertices.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h index a465693c800b..c9e9eb845ac6 100644 --- a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h +++ b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h @@ -116,7 +116,7 @@ void smooth_Steiner_vertices_in_volume(CDT_3& cdt, std::ofstream ofs00("out_before_steiner_smoothing.mesh"); ofs00.precision(17); - CGAL::IO::write_MEDIT(ofs00, cdt, CGAL::parameters::all_cells(true)); + CGAL::IO::write_MEDIT(ofs00, cdt, CGAL::parameters::all_cells(true).with_plc_face_id(true)); ofs00.close(); if(count_negative_tetrahedra(cdt) == 0) { From 22089709109140e03fafdb7301985d5db2e5e8a3 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 8 Oct 2026 16:05:15 +0200 Subject: [PATCH 133/151] sync surface patch indices between each facet and its mirror --- SMDS_3/include/CGAL/IO/File_medit.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/SMDS_3/include/CGAL/IO/File_medit.h b/SMDS_3/include/CGAL/IO/File_medit.h index b7741330f130..64ad90078aa5 100644 --- a/SMDS_3/include/CGAL/IO/File_medit.h +++ b/SMDS_3/include/CGAL/IO/File_medit.h @@ -1228,6 +1228,28 @@ bool read_MEDIT(std::istream& in, using Facet = typename C3t3::Triangulation::Facet; using Vertex_handle = typename C3t3::Triangulation::Vertex_handle; using Subdomain_index = typename C3t3::Subdomain_index; + using Surface_patch_index = typename C3t3::Surface_patch_index; + + auto sync_surface_patch_index_with_mirror = [&](Facet f) -> bool + { + if(f.first->surface_patch_index(f.second) != Surface_patch_index()) + { + Facet mf = c3t3.triangulation().mirror_facet(f); + mf.first->set_surface_patch_index(mf.second, f.first->surface_patch_index(f.second)); + return true; + } + return false; + }; + + for(Facet f : c3t3.triangulation().finite_facets()) + { + if(sync_surface_patch_index_with_mirror(f) || + sync_surface_patch_index_with_mirror(c3t3.triangulation().mirror_facet(f))) + { + if(!c3t3.is_in_complex(f)) + c3t3.add_to_complex(f, f.first->surface_patch_index(f.second)); + } + } c3t3.rescan_after_load_of_triangulation(); // fix counters for facets and cells for(Cell_handle cit : c3t3.triangulation().finite_cell_handles()) From 612eda6018f0434a7d0e6540c2d843a5228e4317 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 8 Oct 2026 16:58:10 +0200 Subject: [PATCH 134/151] add remove_bbox_points() --- .../Conforming_Delaunay_triangulation_3.h | 1 - ...ing_constrained_Delaunay_triangulation_3.h | 61 ++++++++++++++++++- .../cdt_3_from_off.cpp | 5 +- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 256b60cbfe1c..965fbce6c271 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -830,7 +830,6 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { /// @{ /// remove functions cannot be called - void remove(Vertex_handle) = delete; void remove_cluster() = delete; /// @} diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 50993d2db08f..42df42d01039 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -1416,6 +1416,10 @@ class Conforming_constrained_Delaunay_triangulation_3 cdt_impl.move_Steiner_vertices_to_the_volume(); } + void remove_bbox_points() { + cdt_impl.remove_bbox_points(); + } + // Lightweight accessors used by tests/tools size_type number_of_vertices() const { return static_cast(cdt_impl.number_of_vertices()); } size_type number_of_cells() const { return triangulation().number_of_cells(); } @@ -6064,6 +6068,62 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } + void remove_bbox_points() + { + std::vector bbox_vertices; + this->adjacent_vertices(this->infinite_vertex(), std::back_inserter(bbox_vertices)); + if(bbox_vertices.size() != 8) + return; + + auto remove_bbox_vertex = [this](Vertex_handle v) + { + // collect facets that will be on the outer hull after removing v + // seen from the other side, so that they remain valid + std::vector cells_incident_to_v; + this->finite_incident_cells(v, std::back_inserter(cells_incident_to_v)); + + std::unordered_set> cavity_outer_hull; + for(Cell_handle c : cells_incident_to_v) + cavity_outer_hull.insert(this->mirror_facet({c, c->index(v)})); + + // remove v + this->remove(v); + + // mark the facets that are on the outer hull after removal + for(const auto& f : cavity_outer_hull) // f is seen from the "inside" + { + const auto& outer_facet = f; + const auto& outer_data = outer_facet.first->ccdt_3_data(); + const auto& cavity_facet = this->mirror_facet(outer_facet); + + const Cell_handle outer_cell = outer_facet.first; + const int outer_index = outer_facet.second; + if(outer_cell->ccdt_3_data().is_facet_constrained(outer_index)) + { + const CDT_3_signed_index face_id = face_constraint_index(outer_facet); + const auto& cdt_2d = this->face_cdt_2(face_id); + auto f_2d = outer_data.face_2(cdt_2d, outer_index);//CDT_2_face_handle + Facet oriented_facet = same_triple(outer_facet, f_2d) + ? outer_facet + : cavity_facet; + set_facet_constrained(oriented_facet, face_id, f_2d); + } + else + set_facet_as_not_constrained(cavity_facet); + } + }; + + for(auto v : bbox_vertices) + { + if (v->ccdt_3_data().vertex_type() != CDT_3_vertex_type::BBOX) + { + std::cout << "Bbox invalid. Cannot be removed" << std::endl; + return; + } + remove_bbox_vertex(v); + } + } + static void write_region_to_OFF(std::ostream& out, const CDT_2& cdt_2) { out.precision(17); auto color_fn = [](CDT_2_face_handle fh_2d) -> CGAL::IO::Color { @@ -6635,7 +6695,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D /// @{ /// remove functions cannot be called - void remove(Vertex_handle) = delete; void remove_cluster() = delete; /// @} diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index b9ea8a0ac797..cb6985919a14 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -806,7 +806,10 @@ int go(Mesh mesh, CDT_options options) { } // Move Steiner vertices to the volume if requested - if(options.move_Steiner_vertices_to_the_volume) { + if(options.move_Steiner_vertices_to_the_volume) + { +// cdt.remove_bbox_points(); + auto move_vertices_guard = CGAL::CDT_3_MOVE_STEINER_VERTICES_TASK_guard(); cdt.move_Steiner_vertices_to_the_volume(); if(!options.quiet) { From 744c4bf26689479f85ee6ee0733d980df3e2cf77 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 9 Oct 2026 15:37:55 +0200 Subject: [PATCH 135/151] fix compilation of debug code --- .../include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h index ef1cde8f9cd6..d79ef82abaf0 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h @@ -1988,7 +1988,7 @@ void flip_edges(C3T3& c3t3, vertices_subdomain_indices); #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG - if( !tr.may_have_badly_oriented_cells() + if(!c3t3.triangulation().may_have_badly_oriented_cells() && !debug::are_cell_orientations_valid(c3t3.triangulation())) std::cerr << "ERROR in ORIENTATION" << std::endl; #endif From 3f69de228fc1ef26911faeac557c632aa90a2931 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 12 Oct 2026 17:05:15 +0200 Subject: [PATCH 136/151] fix compilation error on Linux ``` [build] .../include/CGAL/tetrahedral_remeshing.h:576:7: error: non-constant-expression cannot be narrowed from type 'int' to 'std::size_t' (aka 'unsigned long') in initializer list [-Wc++11-narrowing] [build] 576 | choose_parameter(get_parameter(np, internal_np::nb_smoothing_iterations), std::size_t{1}), [build] | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` --- Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h index 2105185eef05..e32d43d602a2 100644 --- a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h +++ b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h @@ -573,8 +573,10 @@ void tetrahedral_isotropic_remeshing(CDT_3& cdt3, choose_parameter(get_parameter(np, internal_np::do_split), true), choose_parameter(get_parameter(np, internal_np::do_collapse), true), choose_parameter(get_parameter(np, internal_np::do_flip), true), - choose_parameter(get_parameter(np, internal_np::nb_smoothing_iterations), std::size_t{1}), - choose_parameter(get_parameter(np, internal_np::nb_flip_smooth_iterations), std::size_t{3})}; + static_cast( + choose_parameter(get_parameter(np, internal_np::nb_smoothing_iterations), std::size_t{1})), + static_cast( + choose_parameter(get_parameter(np, internal_np::nb_flip_smooth_iterations), std::size_t{3}))}; #ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE std::cout << "Tetrahedral remeshing (" From d016dd7aa37181a7f1eaeb4ca1c0e612183f2a10 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 12 Oct 2026 17:05:28 +0200 Subject: [PATCH 137/151] fix a warning --- .../internal/smooth_Steiner_vertices.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h index c9e9eb845ac6..db84878bbc01 100644 --- a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h +++ b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/smooth_Steiner_vertices.h @@ -109,7 +109,6 @@ void smooth_Steiner_vertices_in_volume(CDT_3& cdt, const double bbox_max_span, const bool remesh) { - using Cell_handle = typename CDT_3::Cell_handle; using Vertex_handle = typename CDT_3::Vertex_handle; std::cout << "#Negative tetrahedra before Steiner smoothing: " << count_negative_tetrahedra(cdt) << std::endl; From 3896c4c7dd132b9deba2596bf2cf88acca7fffff Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 12 Oct 2026 18:06:18 +0200 Subject: [PATCH 138/151] allow to compiler tet remeshing with Epeck --- .../Tetrahedral_remeshing/internal/collapse_short_edges.h | 4 ++-- .../CGAL/Tetrahedral_remeshing/internal/flip_edges.h | 5 +++++ .../internal/tetrahedral_remeshing_helpers.h | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index a253a454ff66..e53b4d450ab4 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -618,10 +618,10 @@ bool collapse_preserves_surface_star(const typename C3t3::Edge& edge, if(do_opp) n_after_collapse = opp(n_after_collapse); - const double dotref = product(reference_normal, n_after_collapse); + const auto dotref = product(reference_normal, n_after_collapse); if(dotref < 0) return false; - const double dot = product(n_before_collapse, n_after_collapse); + const auto dot = product(n_before_collapse, n_after_collapse); if(dot < 0) return false; diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h index 4ece5af2142d..5e0639f04ba7 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h @@ -205,6 +205,11 @@ Sliver_removal_result flip_3_to_2(typename C3t3::Edge& edge, } else if (criterion == AVERAGE_ANGLE_BASED) { + auto min_dihedral_angle = [](auto&& ...args) { + auto angle = CGAL::Tetrahedral_remeshing::min_dihedral_angle(std::forward(args)...); + return to_double(exact(angle)); + }; + //Current worst dihedral angle double average_min_dh = min_dihedral_angle(tr, ch0); average_min_dh += min_dihedral_angle(tr, ch1); diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index cfde9c9cbe71..4f8bf6ac79ac 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -529,9 +529,9 @@ bool edges_form_a_sharp_angle(const EdgesVector& incident_edges, CGAL_assertion(shared_vertex != v2); CGAL_assertion(v1 != v2); - const double angle = CGAL::approximate_angle(point(v1->point()), - point(shared_vertex->point()), - point(v2->point())); + const auto angle = CGAL::approximate_angle(point(v1->point()), + point(shared_vertex->point()), + point(v2->point())); return angle < angle_bound; } From f14c1bbcc107a64ec1fc340b488251ef5432d61c Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 13 Oct 2026 11:17:52 +0200 Subject: [PATCH 139/151] fix link condition before collapse # Conflicts: # Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h --- .../internal/collapse_short_edges.h | 79 +++++++++++++------ 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index e53b4d450ab4..51410ce26cc9 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -434,39 +434,74 @@ Collapse_type get_collapse_type(const typename C3t3::Edge& edge, // return NO_COLLAPSE; //} -template -bool is_valid_collapse(const typename C3t3::Edge& edge, - const C3t3& c3t3) + +template +bool is_link_condition_valid(const typename Triangulation::Edge& edge, + const Triangulation& tr) { - typedef typename C3t3::Vertex_handle Vertex_handle; - typedef typename C3t3::Cell_handle Cell_handle; - typedef typename C3t3::Triangulation::Cell_circulator Cell_circulator; + using Vertex_handle = typename Triangulation::Vertex_handle; + using Cell_handle = typename Triangulation::Cell_handle; + using Cell_circulator = typename Triangulation::Cell_circulator; const Vertex_handle v0 = edge.first->vertex(edge.second); const Vertex_handle v1 = edge.first->vertex(edge.third); - Cell_circulator circ = c3t3.triangulation().incident_cells(edge); + if(tr.is_infinite(v0) || tr.is_infinite(v1)) + return false; + + // collect vertices of link(edge) - the "circle" around edge + std::set edge_link_vertices; + Cell_circulator circ = tr.incident_cells(edge); Cell_circulator done = circ; do { - int v0_id = circ->index(v0); - int v1_id = circ->index(v1); - - Cell_handle n0_ch = circ->neighbor(v0_id); - Cell_handle n1_ch = circ->neighbor(v1_id); + for(int k = 0; k < 4; ++k) + { + Vertex_handle vk = circ->vertex(k); + if(vk != v0 && vk != v1) + edge_link_vertices.insert(vk); + } + } while(++circ != done); + + // collect the intersection of neighbors of v0 and neighbors of v1 + std::set neighbors_v0, neighbors_v1; + tr.adjacent_vertices(v0, std::inserter(neighbors_v0, neighbors_v0.end())); + tr.adjacent_vertices(v1, std::inserter(neighbors_v1, neighbors_v1.end())); + + std::set intersection_neighbors_v0v1; + std::set_intersection(neighbors_v0.begin(), neighbors_v0.end(), + neighbors_v1.begin(), neighbors_v1.end(), + std::inserter(intersection_neighbors_v0v1, intersection_neighbors_v0v1.end())); + + if(intersection_neighbors_v0v1 != edge_link_vertices) + return false; // there are vertices shared by v0 and v1 outside edge_link_vertices + + // Check the link condition + // for each pair of vertices in the link, if they are connected by an edge of the triangulation, + // that edge must belong to one of the cells incident to `edge` + // (checking that there are no "chords" in the link that would violate the link condition) + for(Vertex_handle link_vertex : edge_link_vertices) + { + std::vector neighbors; + tr.adjacent_vertices(link_vertex, std::back_inserter(neighbors)); - if (n0_ch->has_vertex(v0) - || n1_ch->has_vertex(v1) - || n0_ch->has_neighbor(n1_ch)) + for(Vertex_handle neighbor : neighbors) { -#ifdef CGAL_DEBUG_TET_REMESHING_IN_PLUGIN - if (c3t3.is_in_complex(edge)) - ++nb_invalid_collapse_short; -#endif - return false; + if(neighbor < link_vertex) // avoid checking the same edge twice + continue; + if(edge_link_vertices.find(neighbor) != edge_link_vertices.end()) + { + // the edge [link_vertex, neighbor] exists + // if facet [v0, link_vertex, neighbor] is not in the triangulation + // then the edge [link_vertex, neighbor] is not part of the link of [v0, v1] + if(!tr.is_facet(v0, link_vertex, neighbor)) + { + CGAL_assertion(!tr.is_facet(v1, link_vertex, neighbor)); + return false; + } + } } } - while (++circ != done); return true; } @@ -1185,7 +1220,7 @@ typename C3t3::Vertex_handle collapse_edge(typename C3t3::Edge& edge, // so it settles all three attempts below at once - and it is the cheap one: // one cell circulation, against a star walk plus an orientation predicate // per cell of the star. - if (!is_valid_collapse(edge, c3t3)) + if(!is_link_condition_valid(edge, c3t3.triangulation())) return Vertex_handle(); // Each attempt below walks the star of v0 and/or of v1, and so do the angle From 3c15f941ead57373bfb2085eee008243794414f6 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 13 Oct 2026 14:04:06 +0200 Subject: [PATCH 140/151] fix CDT+tet remeshing --- ...ained_Delaunay_triangulation_cell_base_3.h | 13 ++++++++++- .../test_ccdt_remeshing.cpp | 22 ++++++++++++------- .../internal/smooth_vertices.h | 1 + 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h index 001407824dae..d9e42f9e0b79 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h @@ -88,7 +88,18 @@ class Conforming_constrained_Delaunay_triangulation_cell_base_3 template void set_surface_patch_index(const int i, const Surface_patch_index& patch) { - this->ccdt_3_data().set_face_constraint_index(i, patch); + if(patch > 0) + this->ccdt_3_data().set_face_constraint_index(i, patch - 1); + else + this->ccdt_3_data().set_face_constraint_index(i, patch); + } + + auto surface_patch_index(const int i) const { + return this->ccdt_3_data().face_constraint_index(i) + 1; + } + + bool is_facet_on_surface(const int i) const { + return this->ccdt_3_data().face_constraint_index(i) >= 0; } ///////// end of for C3T3 diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/test_ccdt_remeshing.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/test_ccdt_remeshing.cpp index 3310fa00a646..45ffaa9c8d22 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/test_ccdt_remeshing.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/test_ccdt_remeshing.cpp @@ -11,6 +11,7 @@ #include #include +#include using K = CGAL::Exact_predicates_inexact_constructions_kernel; @@ -49,20 +50,25 @@ int main(int argc, char* argv[]) namespace Tet_remesh = CGAL::Tetrahedral_remeshing; Tr tr = Tet_remesh::get_remeshing_triangulation(std::move(cdt)); + auto number_of_constrained_facets = [](const Tr& tr) { + auto n = std::count_if(tr.finite_facets().begin(), tr.finite_facets().end(), + [](const auto& f) { + auto [c, i] = f; + return c->is_facet_on_surface(i); + }); + return static_cast(n); + }; + + auto nb = number_of_constrained_facets(tr); + assert(nb == nb_cstr_facets); + CGAL::tetrahedral_isotropic_remeshing(tr, 2., CGAL::parameters::number_of_iterations(3) .remesh_boundaries(false)); std::cout << "Number of vertices in tr: " << tr.number_of_vertices() << std::endl; - auto nb = 0u; - for(auto f : tr.finite_facets()) - { - const Tr::Cell_handle c = f.first; - const int i = f.second; - if(c->is_facet_on_surface(i)) - ++nb; - } + nb = number_of_constrained_facets(tr); assert(nb == nb_cstr_facets); return EXIT_SUCCESS; diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h index c6f74f90fd15..fec369e4e0d5 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h @@ -207,6 +207,7 @@ class Tetrahedral_remeshing_smoother m_free_vertices[idi] = !m_protect_boundaries && m_smooth_constrained_edges; break; case 0: + case -1: m_free_vertices[idi] = false; break; default: From cc69f5e617c3539d3222f95f3d591bf3f18d9ea1 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 13 Oct 2026 14:04:18 +0200 Subject: [PATCH 141/151] was committed by error --- .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index cb6985919a14..c82d98fa1a2f 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -133,7 +133,7 @@ struct CDT_options #else false; #endif - bool move_Steiner_vertices_to_the_volume = true; + bool move_Steiner_vertices_to_the_volume = false; bool allow_moving_Steiner_vertices_to_create_negative_tets = true; bool use_epeck_for_normals = false; bool use_epeck_for_Steiner_points = false; From f539e24975997dfc343788b4f01268725eacef34 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 13 Oct 2026 16:48:15 +0200 Subject: [PATCH 142/151] CDT_2: Intersection_of_constraints_exception details in `what()` --- .../CGAL/Constrained_triangulation_2.h | 93 ++++++++++++++----- .../CGAL/Constrained_triangulation_plus_2.h | 24 ++--- .../include/CGAL/Triangulation_2.h | 16 ++++ 3 files changed, 99 insertions(+), 34 deletions(-) diff --git a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h index eb71d1223d6a..5dcc2f901d17 100644 --- a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h +++ b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h @@ -18,10 +18,14 @@ #include -#include -#include +#include +#include +#include +#include #include +#include +#include #include #include #include @@ -43,6 +47,54 @@ struct No_constraint_intersection_requiring_constructions_tag{}; struct Exact_intersections_tag{}; // to be used with an exact number type struct Exact_predicates_tag{}; // to be used with filtered exact number +class Intersection_of_constraints_exception : public std::runtime_error +{ + static auto construct_what() + { + return std::string("Unauthorized intersections of constraints"); + } + + template + static auto with_point(Vertex_handle v) { + return IO::oformat([v](std::ostream& os) -> auto& { + return os << IO::oformat(v, With_point_tag{}); + }, IO_manip_tag{}); + } + + template + static auto construct_what(Vertex_handle vaa, + Vertex_handle vbb, + Vertex_handle v1, + Vertex_handle v2) + { + std::stringstream ss; + ss.copyfmt(std::cerr); + ss << "Unauthorized intersections of constraints: ( " + << with_point(vaa) << " " << with_point(vbb) << " )" << " intersects with (" + << with_point(v1) << ", " << with_point(v2) << ")"; + return ss.str(); + } + + template + static auto construct_what(Point p, + Vertex_handle v1, + Vertex_handle v2) + { + std::stringstream ss; + ss.copyfmt(std::cerr); + ss << "Unauthorized intersections of constraints: point " + << p << " intersects with (" << with_point(v1) << ", " << with_point(v2) << ")"; + return ss.str(); + } + +public: + template + Intersection_of_constraints_exception(Args&&... args) + : std::runtime_error(construct_what(std::forward(args)...)) + {} +}; + // This was deprecated and replaced by ` No_constraint_intersection_tag` and `No_constraint_intersection_requiring_constructions_tag` // due to an inconsistency between the code and the documentation. struct CGAL_DEPRECATED No_intersection_tag : @@ -127,14 +179,7 @@ class Constrained_triangulation_2 } }; - - class Intersection_of_constraints_exception : public std::exception - { - const char* what() const throw () - { - return "Unauthorized intersections of constraints"; - } - }; + using Intersection_of_constraints_exception = CGAL::Intersection_of_constraints_exception; typedef boost::filter_iterator @@ -346,7 +391,7 @@ insert_constraint(Vertex_handle vaa, Vertex_handle vbb, OutputIterator out) // then this is an intersection if constexpr (std::is_same_v) { if(fr->is_constrained(dimension() == 1 ? 2 : i)) { - throw Intersection_of_constraints_exception(); + throw Intersection_of_constraints_exception(vaa, vbb, vaa, vi); } } @@ -722,12 +767,12 @@ insert(const Point& a, Locate_type lt, Face_handle loc, int li) } if ( lt == Triangulation::EDGE && loc->is_constrained(li) ) { + v1=loc->vertex(ccw(li)); //endpoint of the constraint + v2=loc->vertex(cw(li)); // endpoint of the constraint if constexpr (std::is_same_v) - throw Intersection_of_constraints_exception(); + throw Intersection_of_constraints_exception(a, v1, v2); insert_in_constrained_edge = true; - v1=loc->vertex(ccw(li)); //endpoint of the constraint - v2=loc->vertex(cw(li)); // endpoint of the constraint } va = Triangulation::insert(a,lt,loc,li); @@ -837,7 +882,7 @@ insert_constraint(Vertex_handle vaa, Vertex_handle vbb) // then this is an intersection if constexpr (std::is_same_v) { if(fr->is_constrained(dimension() == 1 ? 2 : i)) { - throw Intersection_of_constraints_exception(); + throw Intersection_of_constraints_exception(vaa, vbb, vaa, vi); } } @@ -1028,24 +1073,26 @@ intersect(Face_handle f, int i, template typename Constrained_triangulation_2::Vertex_handle Constrained_triangulation_2:: -intersect(Face_handle , int , - Vertex_handle , - Vertex_handle , +intersect(Face_handle f, int i, + Vertex_handle vaa, + Vertex_handle vbb, No_constraint_intersection_tag) { - throw Intersection_of_constraints_exception(); + auto [v1, v2] = this->vertices(f, i); + throw Intersection_of_constraints_exception(vaa, vbb, v1, v2); return Vertex_handle() ; } template typename Constrained_triangulation_2::Vertex_handle Constrained_triangulation_2:: -intersect(Face_handle , int , - Vertex_handle , - Vertex_handle , +intersect(Face_handle f, int i, + Vertex_handle vaa, + Vertex_handle vbb, No_constraint_intersection_requiring_constructions_tag) { - throw Intersection_of_constraints_exception(); + auto [v1, v2] = this->vertices(f, i); + throw Intersection_of_constraints_exception(vaa, vbb, v1, v2); return Vertex_handle() ; } diff --git a/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h b/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h index 47f1ab299123..7111696b19f1 100644 --- a/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h +++ b/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h @@ -1089,12 +1089,12 @@ insert(const Point& a, Locate_type lt, Face_handle loc, int li) if ( lt == Triangulation::EDGE && loc->is_constrained(li) ) { + v1=loc->vertex(ccw(li)); //endpoint of the constraint + v2=loc->vertex(cw(li)); // endpoint of the constraint if(std::is_same::value) - throw typename Tr::Intersection_of_constraints_exception(); + throw typename Tr::Intersection_of_constraints_exception(a, v1, v2); insert_in_constrained_edge = true; - v1=loc->vertex(ccw(li)); //endpoint of the constraint - v2=loc->vertex(cw(li)); // endpoint of the constraint } Vertex_handle va = Triangulation::insert(a,lt,loc,li); @@ -1126,24 +1126,26 @@ intersect(Face_handle f, int i, template typename Constrained_triangulation_plus_2:: Vertex_handle Constrained_triangulation_plus_2:: -intersect(Face_handle, int, - Vertex_handle, - Vertex_handle, +intersect(Face_handle f, int i, + Vertex_handle vaa, + Vertex_handle vbb, No_constraint_intersection_tag) { - throw typename Tr::Intersection_of_constraints_exception(); + auto [v1, v2] = this->vertices(f, i); + throw typename Tr::Intersection_of_constraints_exception(vaa, vbb, v1, v2); return Vertex_handle(); } template typename Constrained_triangulation_plus_2:: Vertex_handle Constrained_triangulation_plus_2:: -intersect(Face_handle, int, - Vertex_handle, - Vertex_handle, +intersect(Face_handle f, int i, + Vertex_handle vaa, + Vertex_handle vbb, No_constraint_intersection_requiring_constructions_tag) { - throw typename Tr::Intersection_of_constraints_exception(); + auto [v1, v2] = this->vertices(f, i); + throw typename Tr::Intersection_of_constraints_exception(vaa, vbb, v1, v2); return Vertex_handle(); } diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index fea1aba15eec..1fb240adf625 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -310,6 +310,22 @@ class Triangulation_2 construct_point(const Point& p) const { return geom_traits().construct_point_2_object()(p); } + std::array vertices(Face_handle f) const { + return {f->vertex(0), f->vertex(1), f->vertex(2)}; + } + + std::array vertices(Face_handle f, int i) const { + return {f->vertex(ccw(i)), f->vertex(cw(i))}; + } + + std::array vertices(Edge e) const { + return {e.first->vertex(ccw(e.second)), e.first->vertex(cw(e.second))}; + } + + std::array vertices(Vertex_handle v) const { + return {v}; + } + const Point& point(Face_handle f, int i) const; const Point& point(Vertex_handle v) const; From b1ca55fb89bcbec00f7c9b61f45e0383bdc6b072 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 13 Oct 2026 16:48:27 +0200 Subject: [PATCH 143/151] fix the documentation --- .../CGAL/Constrained_triangulation_2.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Triangulation_2/doc/Triangulation_2/CGAL/Constrained_triangulation_2.h b/Triangulation_2/doc/Triangulation_2/CGAL/Constrained_triangulation_2.h index c8a49fb0ce72..24e9f0e3d8c0 100644 --- a/Triangulation_2/doc/Triangulation_2/CGAL/Constrained_triangulation_2.h +++ b/Triangulation_2/doc/Triangulation_2/CGAL/Constrained_triangulation_2.h @@ -327,8 +327,6 @@ is_valid(bool verbose = false, int level = 0) const; /// @} -}; /* end Constrained_triangulation_2 */ - /*! writes the triangulation as for `Triangulation_2` and, for each face `f`, and integers `i=0,1,2`, writes "C" or "N" depending whether edge @@ -336,7 +334,7 @@ writes "C" or "N" depending whether edge \relates Constrained_triangulation_2 */ template -std::ostream & operator<<(std::ostream& os, const Constrained_triangulation_2 &ct); +friend std::ostream & operator<<(std::ostream& os, const Constrained_triangulation_2 &ct); /*! reads a triangulation from stream `is` and assigns it to c`t`. Data in the stream must have the same format `operator<<` uses. @@ -344,11 +342,20 @@ Note that `ct` is first cleared. \relates Constrained_triangulation_2 */ template -std::istream& operator>>(std::istream& is,Constrained_triangulation_2 Ct& ct); +friend std::istream& operator>>(std::istream& is,Constrained_triangulation_2 Ct& ct); + +}; /* end Constrained_triangulation_2 */ + +/*! +\ingroup PkgTriangulation2TriangulationClasses -/*! Exception used by constrained triangulations configured with +Exception used by constrained triangulations configured with the tags `No_constraint_intersection_tag` or `No_constraint_intersection_requiring_constructions_tag` when the insertion of a new constraint would break the respective conditions associated to each tag. + +\inherit std::runtime_error +\relates Constrained_triangulation_2 */ -class Intersection_of_constraints_exception; +class Intersection_of_constraints_exception{}; + } /* end namespace CGAL */ From 2bbd003adfd9689d9878cabc1696779bb4efacd7 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 14 Oct 2026 11:09:28 +0200 Subject: [PATCH 144/151] fix for Partition_2: test if `std::cerr << v->point()` is possible --- .../CGAL/Constrained_triangulation_2.h | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h index 5dcc2f901d17..db2745e92c2f 100644 --- a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h +++ b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,19 @@ class Intersection_of_constraints_exception : public std::runtime_error return std::string("Unauthorized intersections of constraints"); } + template + struct Can_the_point_be_displayed : std::false_type + {}; + + template + struct Can_the_point_be_displayed< + Vertex_handle, + std::void_t() << std::declval()->point())>> : std::true_type + {}; + + template + static inline constexpr bool can_the_point_be_displayed = Can_the_point_be_displayed::value; + template static auto with_point(Vertex_handle v) { return IO::oformat([v](std::ostream& os) -> auto& { @@ -67,12 +81,16 @@ class Intersection_of_constraints_exception : public std::runtime_error Vertex_handle v1, Vertex_handle v2) { - std::stringstream ss; - ss.copyfmt(std::cerr); - ss << "Unauthorized intersections of constraints: ( " - << with_point(vaa) << " " << with_point(vbb) << " )" << " intersects with (" - << with_point(v1) << ", " << with_point(v2) << ")"; - return ss.str(); + if constexpr(can_the_point_be_displayed) { + std::stringstream ss; + ss.copyfmt(std::cerr); + ss << "Unauthorized intersections of constraints: ( " + << with_point(vaa) << " " << with_point(vbb) << " )" << " intersects with (" + << with_point(v1) << ", " << with_point(v2) << ")"; + return ss.str(); + } else { + return std::string("Unauthorized intersections of constraints"); + } } template ) { + std::stringstream ss; + ss.copyfmt(std::cerr); + ss << "Unauthorized intersections of constraints: point " + << p << " intersects with (" << with_point(v1) << ", " << with_point(v2) << ")"; + return ss.str(); + } else { + return std::string("Unauthorized intersections of constraints"); + } } public: From 4620a2d7aac6595aa2a9a969320ec7fbf1d15421 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 14 Oct 2026 11:46:39 +0200 Subject: [PATCH 145/151] fix warnings --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 3 ++- SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h | 1 - .../CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 42df42d01039..9c9b97020d7f 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -758,7 +758,7 @@ class Conforming_constrained_Delaunay_triangulation_3 { CGAL_precondition(!triangulation().is_infinite(c)); CGAL_precondition(index == Subdomain_index{1}); - CGAL_USE(index); + CGAL_USE(c); CGAL_USE(index); } void set_subdomain_index(const Cell_handle, const Subdomain_index&) { @@ -785,6 +785,7 @@ class Conforming_constrained_Delaunay_triangulation_3 void remove_from_complex(const Cell_handle c) { CGAL_precondition(!triangulation().is_infinite(c)); + CGAL_USE(c); } void remove_from_complex(const Facet& f) { diff --git a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h index 530bfb864017..150b8c183bd8 100644 --- a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h +++ b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h @@ -164,7 +164,6 @@ bool build_finite_cells(Tr& tr, { typedef typename Tr::Vertex_handle Vertex_handle; typedef typename Tr::Cell_handle Cell_handle; - typedef typename FacetPatchMap::value_type Surface_patch_index; bool success = true; diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index 51410ce26cc9..5c89a4f06979 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -440,7 +440,6 @@ bool is_link_condition_valid(const typename Triangulation::Edge& edge, const Triangulation& tr) { using Vertex_handle = typename Triangulation::Vertex_handle; - using Cell_handle = typename Triangulation::Cell_handle; using Cell_circulator = typename Triangulation::Cell_circulator; const Vertex_handle v0 = edge.first->vertex(edge.second); From e7840f3f7dcf8ae4632e32b3bff037f8ed6ee9d5 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 14 Oct 2026 11:46:52 +0200 Subject: [PATCH 146/151] use Intersection_of_constraints_exception::what --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 9c9b97020d7f..7a1ba376ceee 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -5954,8 +5954,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D try { fill_cdt_2(cdt_2, i); search_for_missing_subfaces(i); - } catch (typename CDT_2::Intersection_of_constraints_exception&) { - std::cerr << "ERROR: Intersection of constraints in face F#" << i << "\n"; + } catch (typename CDT_2::Intersection_of_constraints_exception& e) { + std::cerr << "ERROR: Intersection of constraints in face F#" << i << ": " + << e.what() << "\n"; this->face_data[i].skip_face = true; dump_face_polygons(build_face_border_handles(face_borders(i)), i); } catch (Null_normal_error& e) { From ac5067777d7de485f9b9ffd584e58a2a4f4a9dc0 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 14 Oct 2026 12:15:29 +0200 Subject: [PATCH 147/151] add vertices(Element) to TDS_2 and 2D triangulations --- .../include/CGAL/Periodic_2_triangulation_2.h | 18 +++++++++++++++++- .../CGAL/Triangulation_data_structure_2.h | 17 +++++++++++++++++ Triangulation_2/include/CGAL/Triangulation_2.h | 9 +++++---- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/Periodic_2_triangulation_2/include/CGAL/Periodic_2_triangulation_2.h b/Periodic_2_triangulation_2/include/CGAL/Periodic_2_triangulation_2.h index e50c12ae6f7c..c808c8ab8baf 100644 --- a/Periodic_2_triangulation_2/include/CGAL/Periodic_2_triangulation_2.h +++ b/Periodic_2_triangulation_2/include/CGAL/Periodic_2_triangulation_2.h @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -42,7 +43,6 @@ #include #include -#include namespace CGAL { @@ -330,6 +330,22 @@ class Periodic_2_triangulation_2 //\} + std::array vertices(Face_handle f) const { + return _tds.vertices(f); + } + + std::array vertices(Face_handle f, int i) const { + return _tds.vertices(f, i); + } + + std::array vertices(Edge e) const { + return _tds.vertices(e); + } + + std::array vertices(Vertex_handle v) const { + return _tds.vertices(v); + } + /// \name Number of simplices //\{ /// Returns whether the triangulation is empty. diff --git a/TDS_2/include/CGAL/Triangulation_data_structure_2.h b/TDS_2/include/CGAL/Triangulation_data_structure_2.h index 884c96ce73d8..e03e004953b7 100644 --- a/TDS_2/include/CGAL/Triangulation_data_structure_2.h +++ b/TDS_2/include/CGAL/Triangulation_data_structure_2.h @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -137,6 +138,22 @@ class Triangulation_data_structure_2 Vertex_range& vertices() const {return const_cast(this)->_vertices;} + std::array vertices(Face_handle f) const { + return {f->vertex(0), f->vertex(1), f->vertex(2)}; + } + + std::array vertices(Face_handle f, int i) const { + return {f->vertex(ccw(i)), f->vertex(cw(i))}; + } + + std::array vertices(Edge e) const { + return {e.first->vertex(ccw(e.second)), e.first->vertex(cw(e.second))}; + } + + std::array vertices(Vertex_handle v) const { + return {v}; + } + int dimension() const { return _dimension; } size_type number_of_vertices() const {return vertices().size();} size_type number_of_faces() const ; diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index 1fb240adf625..c65516bed1f0 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -311,19 +312,19 @@ class Triangulation_2 std::array vertices(Face_handle f) const { - return {f->vertex(0), f->vertex(1), f->vertex(2)}; + return _tds.vertices(f); } std::array vertices(Face_handle f, int i) const { - return {f->vertex(ccw(i)), f->vertex(cw(i))}; + return _tds.vertices(f, i); } std::array vertices(Edge e) const { - return {e.first->vertex(ccw(e.second)), e.first->vertex(cw(e.second))}; + return _tds.vertices(e); } std::array vertices(Vertex_handle v) const { - return {v}; + return _tds.vertices(v); } const Point& point(Face_handle f, int i) const; From eaa72b75d7cbd121aac6fd2968171a73f9ae0978 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 14 Oct 2026 16:18:40 +0200 Subject: [PATCH 148/151] modify With_point_tag to use tr->point(v) instead of v->point() --- .../Conforming_Delaunay_triangulation_3.h | 8 +-- ...ing_constrained_Delaunay_triangulation_3.h | 22 +++---- Mesh_2/include/CGAL/Mesh_2/Clusters.h | 5 +- Mesh_2/include/CGAL/Mesh_2/Refine_edges.h | 38 ++++++------ .../CGAL/Mesh_2/Refine_edges_with_clusters.h | 20 ++++--- Mesh_2/include/CGAL/Mesh_2/Refine_faces.h | 30 +++++----- Mesh_2/test/Mesh_2/test_mesh_obj.cpp | 10 ++-- Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h | 2 +- .../CGAL/Mesh_3/Protect_edges_sizing_field.h | 3 + .../include/CGAL/Compact_container.h | 18 ++++-- .../CGAL/Constrained_triangulation_2.h | 58 ++++++++++--------- .../CGAL/Constrained_triangulation_plus_2.h | 6 +- .../test/Triangulation_2/issue_4025.cpp | 10 ++-- 13 files changed, 127 insertions(+), 103 deletions(-) diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 06f383e0ff1d..2ae1a9e52bb6 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -292,8 +292,8 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { using Locate_type = typename T_3::Locate_type; inline static With_offset_tag with_offset{ -1 }; - inline static With_point_tag with_point{ {-1} }; - inline static With_point_and_info_tag with_point_and_info{ { {-1} } }; + With_point_tag with_point{ {-1}, this }; + With_point_and_info_tag with_point_and_info{ { {-1}, this } }; Conforming_Delaunay_triangulation_3(const Geom_traits& gt = Geom_traits()) : T_3(gt) @@ -440,7 +440,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { // vertices as sub-constraints. std::for_each(tr().segment_traverser_simplices_begin(va, vb), tr().segment_traverser_simplices_end(), [&, prev = Vertex_handle{}](auto simplex) mutable { - // std::cerr << "- " << oformat(simplex, With_point_tag{}) << '\n'; + // std::cerr << "- " << oformat(simplex, with_point) << '\n'; if(simplex.dimension() == 0) { const auto v = static_cast(simplex); v->ccdt_3_data().set_on_constraint(c_id); @@ -947,7 +947,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { }; auto fill_encroaching_vertices = [&](const auto simplex) { if(debug().encroaching_vertices()) { - std::cerr << " - " << IO::oformat(simplex, With_point_tag{}) << '\n'; + std::cerr << " - " << IO::oformat(simplex, with_point) << '\n'; } auto visit_cell = [&](Cell_handle cell) { for(int i = 0, end = this->tr().dimension() + 1; i < end; ++i) { diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 75286ad367c4..5cacbb97b2fd 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -565,8 +565,8 @@ concept Range_of_polygon_3 = std::ranges::common_range && Polygon_3, Kernel>; #endif // concepts -template -struct Output_rep, With_point_and_info_tag> +template +struct Output_rep, With_point_and_info_tag> : public Output_rep> { int offset = 0; @@ -577,7 +577,7 @@ struct Output_rep, With_point_and_info_t using Base::Base; - Output_rep(const CC_iterator& it, With_point_and_info_tag tag) + Output_rep(const CC_iterator& it, With_point_and_info_tag tag) : Base(it), offset(tag.offset) { } @@ -1986,8 +1986,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D static void print_minimum_distance_info(const Min_distance_result& min_dist) { std::cout << "Min distance between vertices: " << min_dist.min_distance << '\n' << " between vertices: : " - << CGAL::IO::oformat(min_dist.vertices_of_min_edge[0], CGAL::With_point_tag{}) << " " - << CGAL::IO::oformat(min_dist.vertices_of_min_edge[1], CGAL::With_point_tag{}) << "\n\n"; + << CGAL::IO::oformat(min_dist.vertices_of_min_edge[0], with_point) << " " + << CGAL::IO::oformat(min_dist.vertices_of_min_edge[1], with_point) << "\n\n"; } struct Constraint_distance_result { @@ -2079,10 +2079,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().display_statistics()) { std::cout << "Min distance between constraint segment and vertex: " << min_distance << '\n' << " between segment : " - << CGAL::IO::oformat(min_va, CGAL::With_point_tag{}) << " " - << CGAL::IO::oformat(min_vb, CGAL::With_point_tag{}) << '\n' + << CGAL::IO::oformat(min_va, with_point) << " " + << CGAL::IO::oformat(min_vb, with_point) << '\n' << " and vertex : " - << CGAL::IO::oformat(min_vertex, CGAL::With_point_tag{}) << "\n\n"; + << CGAL::IO::oformat(min_vertex, with_point) << "\n\n"; } this->check_segment_vertex_distance_or_throw(min_va, min_vb, min_vertex, min_distance); } @@ -2098,8 +2098,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().display_statistics()) { std::cout << "Min distance between vertices: " << result.min_distance << '\n' << " between vertices : " - << CGAL::IO::oformat(result.vertices_of_min_edge[0], CGAL::With_point_tag{}) << " " - << CGAL::IO::oformat(result.vertices_of_min_edge[1], CGAL::With_point_tag{}) << "\n\n"; + << CGAL::IO::oformat(result.vertices_of_min_edge[0], with_point) << " " + << CGAL::IO::oformat(result.vertices_of_min_edge[1], with_point) << "\n\n"; } this->check_vertex_vertex_distance_or_throw(result.vertices_of_min_edge[0], result.vertices_of_min_edge[1], @@ -4557,7 +4557,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto to_exact = CGAL::Cartesian_converter(); const auto& cdt_2 = this->face_cdt_2(face_index); - auto of = [](auto&&... args) { + auto of = [this](auto&&... args) { return IO::oformat(std::forward(args)..., with_point_and_info); }; diff --git a/Mesh_2/include/CGAL/Mesh_2/Clusters.h b/Mesh_2/include/CGAL/Mesh_2/Clusters.h index 73c07e795efa..ef33c9647f0f 100644 --- a/Mesh_2/include/CGAL/Mesh_2/Clusters.h +++ b/Mesh_2/include/CGAL/Mesh_2/Clusters.h @@ -351,8 +351,9 @@ update_cluster(Cluster& c, iterator it, Vertex_handle va, squared_distance(c.smallest_angle.first->point(), c.smallest_angle.second->point()) / FT(4); cluster_map.insert(Cluster_map_value_type(va,c)); #ifdef CGAL_MESH_2_DEBUG_CLUSTERS - std::cerr << "Cluster at " << IO::oformat(va, With_point_tag{}) << " is updated. " - << "\n vm: " << IO::oformat(vm, With_point_tag{}) + With_point_tag wpt{{-1}, tr }; + std::cerr << "Cluster at " << IO::oformat(va, wpt) << " is updated. " + << "\n vm: " << IO::oformat(vm, wpt) << "\n reduction: " << reduction << "\n min_sq_len: " << c.minimum_squared_length << "\n"; diff --git a/Mesh_2/include/CGAL/Mesh_2/Refine_edges.h b/Mesh_2/include/CGAL/Mesh_2/Refine_edges.h index a03051e56f71..31367306e30a 100644 --- a/Mesh_2/include/CGAL/Mesh_2/Refine_edges.h +++ b/Mesh_2/include/CGAL/Mesh_2/Refine_edges.h @@ -177,14 +177,15 @@ namespace Mesh_2 { auto is_conform = ( angle(a, p, b) != OBTUSE ); #if CGAL_MESH_2_DEBUG_BAD_EDGES + With_point_tag wpt{{-1}, &tr}; if(!is_conform) { - std::cerr << " edge " << CGAL::IO::oformat(va, CGAL::With_point_tag{}) - << " -- " << CGAL::IO::oformat(vb, CGAL::With_point_tag{}) + std::cerr << " edge " << CGAL::IO::oformat(va, wpt) + << " -- " << CGAL::IO::oformat(vb, wpt) << " is encroached by "; if(encroaching_vertex != Vertex_handle()) { - std::cerr << IO::oformat(encroaching_vertex, With_point_tag{}) << std::endl; + std::cerr << IO::oformat(encroaching_vertex, wpt) << std::endl; } else { - std::cerr << "point " << CGAL::IO::oformat(p) << std::endl; + std::cerr << "point " << CGAL::IO::oformat(p, wpt) << std::endl; } } #endif // CGAL_MESH_2_DEBUG_BAD_EDGES @@ -384,12 +385,15 @@ class Refine_edges_base : } } + With_point_tag with_point_tag; + public: /** \name CONSTRUCTORS */ Refine_edges_base(Tr& tr_) : Container(Is_a_constrained_edge(tr_)), tr(tr_), is_a_constrained_edge(tr_), + with_point_tag{{-1}, &tr_}, converter(tr_) { } @@ -420,8 +424,8 @@ class Refine_edges_base : bool result = tr.is_flipable(fh, i); #if CGAL_MESH_2_DEBUG_REFINEMENT_POINTS std::cerr << " is_flippable(" - << IO::oformat(fh->vertex(tr.cw(i)), With_point_tag{}) << ", " - << IO::oformat(fh->vertex(tr.ccw(i)), With_point_tag{}) << ") = " + << IO::oformat(fh->vertex(tr.cw(i)), with_point_tag) << ", " + << IO::oformat(fh->vertex(tr.ccw(i)), with_point_tag) << ") = " << (result ? "true" : "false") << std::endl; #endif // CGAL_MESH_2_DEBUG_REFINEMENT_POINTS return result; @@ -564,7 +568,7 @@ class Refine_edges_base : if( zone.locate_type == Tr::VERTEX ) { #ifdef CGAL_MESH_2_DEBUG_INSERTIONS std::cerr << "insert(p, zone): returned existing vertex " - << IO::oformat(zone.fh->vertex(zone.i), With_point_tag{}) + << IO::oformat(zone.fh->vertex(zone.i), with_point_tag) << std::endl; #endif // CGAL_MESH_2_DEBUG_INSERTIONS for(auto [f, i]: zone.edges_to_flip) { @@ -573,8 +577,8 @@ class Refine_edges_base : auto va = f->vertex(Tr::cw(i)); auto vb = f->vertex(Tr::ccw(i)); std::cerr << " flipping edge (" - << IO::oformat(va, With_point_tag{}) << ", " - << IO::oformat(vb, With_point_tag{}) << ")\n"; + << IO::oformat(va, with_point_tag) << ", " + << IO::oformat(vb, with_point_tag) << ")\n"; #endif // CGAL_MESH_2_DEBUG_INSERTIONS triangulation_ref_impl().flip(f, i); } @@ -587,7 +591,7 @@ class Refine_edges_base : zone.faces.end() ); #ifdef CGAL_MESH_2_DEBUG_INSERTIONS - std::cerr << " inserted new vertex " << IO::oformat(v, With_point_tag{}) << std::endl; + std::cerr << " inserted new vertex " << IO::oformat(v, with_point_tag) << std::endl; #endif // CGAL_MESH_2_DEBUG_INSERTIONS return v; } @@ -626,8 +630,8 @@ class Refine_edges_base : auto p = midpoint(va->point(), vb->point()); #ifdef CGAL_MESH_2_DEBUG_REFINEMENT_POINTS - std::cerr << "refinement_point_impl(Edge: " << IO::oformat(va, With_point_tag{}) << ", " - << IO::oformat(vb, With_point_tag{}) << ") = "; + std::cerr << "refinement_point_impl(Edge: " << IO::oformat(va, with_point_tag) << ", " + << IO::oformat(vb, with_point_tag) << ") = "; std::cerr << p << '\n'; #endif // CGAL_MESH_2_DEBUG_REFINEMENT_POINTS return maybe_snap_to_existing_vertex(edge, p); @@ -686,7 +690,7 @@ class Refine_edges_base : { #ifdef CGAL_MESH_2_DEBUG_REFINEMENT_POINTS std::cerr << "return point of existing vertex " - << IO::oformat(closest_encroaching_vertex->vertex, With_point_tag{}) << std::endl; + << IO::oformat(closest_encroaching_vertex->vertex, with_point_tag) << std::endl; #endif // CGAL_MESH_2_DEBUG_REFINEMENT_POINTS p = closest_encroaching_vertex->vertex->point(); return p; @@ -810,8 +814,8 @@ class Refine_edges_base : const Vertex_handle& vb = e.first->vertex(tr.ccw(e.second)); #if CGAL_MESH_2_DEBUG_BAD_EDGES std::cerr << " add_constrained_edge_to_be_conformed(" - << IO::oformat(va, With_point_tag{}) << ", " - << IO::oformat(vb, With_point_tag{}) << ")\n"; + << IO::oformat(va, with_point_tag) << ", " + << IO::oformat(vb, with_point_tag) << ")\n"; #endif this->add_bad_element(std::make_pair(va, vb)); // see the Container // base class @@ -823,8 +827,8 @@ class Refine_edges_base : { #if CGAL_MESH_2_DEBUG_BAD_EDGES std::cerr << " add_constrained_edge_to_be_conformed(" - << IO::oformat(va, With_point_tag{}) << ", " - << IO::oformat(vb, With_point_tag{}) << ")\n"; + << IO::oformat(va, with_point_tag) << ", " + << IO::oformat(vb, with_point_tag) << ")\n"; #endif this->add_bad_element(std::make_pair(va, vb)); // see the Container // base class diff --git a/Mesh_2/include/CGAL/Mesh_2/Refine_edges_with_clusters.h b/Mesh_2/include/CGAL/Mesh_2/Refine_edges_with_clusters.h index 755b7b0a849b..ef5f8fd12c3a 100644 --- a/Mesh_2/include/CGAL/Mesh_2/Refine_edges_with_clusters.h +++ b/Mesh_2/include/CGAL/Mesh_2/Refine_edges_with_clusters.h @@ -73,6 +73,7 @@ class Refine_edges_base_with_clusters : mutable bool cluster_splitted; Cluster ca, cb; clusters_iterator ca_it, cb_it; + With_point_tag wpt; public: using Super::triangulation_ref_impl; @@ -80,7 +81,7 @@ class Refine_edges_base_with_clusters : /** \name CONSTRUCTORS */ Refine_edges_base_with_clusters(Tr& tr_, Clusters& c_) - : Super(tr_), clusters(c_) + : Super(tr_), clusters(c_), wpt{{-1}, &tr_} { } @@ -98,8 +99,8 @@ class Refine_edges_base_with_clusters : auto display_return_point = [&](const Point& p) { #ifdef CGAL_MESH_2_DEBUG_REFINEMENT_POINTS std::cerr << "Refine_edges_with_clusters::refinement_point_impl(Edge: (" - << IO::oformat(this->va, With_point_tag{}) << ", " - << IO::oformat(this->vb, With_point_tag{}) << ") = " + << IO::oformat(this->va, wpt) << ", " + << IO::oformat(this->vb, wpt) << ") = " << p << '\n'; #endif // CGAL_MESH_2_DEBUG_REFINEMENT_POINTS return p; @@ -117,8 +118,8 @@ class Refine_edges_base_with_clusters : va_has_a_cluster = true; vb_has_a_cluster = true; #ifdef CGAL_MESH_2_DEBUG_CLUSTERS - std::cerr << "midpoint(" << IO::oformat(this->va, With_point_tag{}) - << " , " << IO::oformat(this->vb, With_point_tag{}) << ")\n"; + std::cerr << "midpoint(" << IO::oformat(this->va, wpt) + << " , " << IO::oformat(this->vb, wpt) << ")\n"; #endif // CGAL_MESH_2_DEBUG_CLUSTERS return display_return_point( this->maybe_snap_to_existing_vertex(edge, midpoint(this->va->point(), this->vb->point()))); @@ -138,8 +139,8 @@ class Refine_edges_base_with_clusters : // no cluster #ifdef CGAL_MESH_2_DEBUG_CLUSTERS - std::cerr << "midpoint(" << IO::oformat(this->va, With_point_tag{}) - << " , " << IO::oformat(this->vb, With_point_tag{}) << ")\n"; + std::cerr << "midpoint(" << IO::oformat(this->va, wpt) + << " , " << IO::oformat(this->vb, wpt) << ")\n"; #endif // CGAL_MESH_2_DEBUG_CLUSTERS return Super::refinement_point_impl(edge); @@ -272,8 +273,9 @@ class Refine_edges_base_with_clusters : const Point& b = vb->point(); #ifdef CGAL_MESH_2_DEBUG_CLUSTERS - std::cerr << "split_cluster_point(" << IO::oformat(va, With_point_tag{}) - << " , " << IO::oformat(vb, With_point_tag{}) << ")\n" + With_point_tag wpt{{-1}, &this->tr}; + std::cerr << "split_cluster_point(" << IO::oformat(va, wpt) + << " , " << IO::oformat(vb, wpt) << ")\n" << " reduced: " << c.is_reduced() << "\nresult: "; #endif // CGAL_MESH_2_DEBUG_CLUSTERS if( c.is_reduced() ) { diff --git a/Mesh_2/include/CGAL/Mesh_2/Refine_faces.h b/Mesh_2/include/CGAL/Mesh_2/Refine_faces.h index a71fe51f5b34..054c44e38bb9 100644 --- a/Mesh_2/include/CGAL/Mesh_2/Refine_faces.h +++ b/Mesh_2/include/CGAL/Mesh_2/Refine_faces.h @@ -13,6 +13,7 @@ #ifndef CGAL_MESH_2_REFINE_FACES_H #define CGAL_MESH_2_REFINE_FACES_H +#include #include #include @@ -106,12 +107,13 @@ class Refine_faces_base : Bad_faces bad_faces; Mesh_2::Face_badness current_badness; + With_point_tag with_point_tag; public: /** \name CONSTRUCTORS */ Refine_faces_base(Tr& t, Criteria& criteria_, Previous& prev) - : Triangulation_traits(t), criteria(criteria_), previous(prev) + : Triangulation_traits(t), criteria(criteria_), previous(prev), with_point_tag{{-1}, &t} { } @@ -194,9 +196,9 @@ class Refine_faces_base : auto v1 = f->vertex(1); auto v2 = f->vertex(2); std::cerr << "refinement_point_impl(Face: " - << IO::oformat(v0, With_point_tag{}) << ", " - << IO::oformat(v1, With_point_tag{}) << ", " - << IO::oformat(v2, With_point_tag{}) << ") = " + << IO::oformat(v0, with_point_tag) << ", " + << IO::oformat(v1, with_point_tag) << ", " + << IO::oformat(v2, with_point_tag) << ") = " << p << '\n'; #endif // CGAL_MESH_2_DEBUG_BAD_FACES return p; @@ -241,7 +243,7 @@ class Refine_faces_base : zone.faces.begin(), zone.faces.end()); #ifdef CGAL_MESH_2_DEBUG_INSERTIONS - std::cerr << " inserted new vertex " << IO::oformat(v, With_point_tag{}) << std::endl; + std::cerr << " inserted new vertex " << IO::oformat(v, with_point_tag) << std::endl; #endif // CGAL_MESH_2_DEBUG_INSERTIONS return v; } @@ -322,9 +324,9 @@ push_in_bad_faces(Face_handle fh, const Quality& q) { #ifdef CGAL_MESH_2_DEBUG_BAD_FACES std::cerr << "push_in_bad_faces(" - << IO::oformat(fh->vertex(0), With_point_tag{}) << ", " - << IO::oformat(fh->vertex(1), With_point_tag{}) << ", " - << IO::oformat(fh->vertex(2), With_point_tag{}) << ")\n"; + << IO::oformat(fh->vertex(0), with_point_tag) << ", " + << IO::oformat(fh->vertex(1), with_point_tag) << ", " + << IO::oformat(fh->vertex(2), with_point_tag) << ")\n"; #endif // CGAL_MESH_2_DEBUG_BAD_FACES CGAL_assertion_code (typename Geom_traits::Orientation_2 orientation = @@ -344,9 +346,9 @@ remove_bad_face(Face_handle fh) { #ifdef CGAL_MESH_2_DEBUG_BAD_FACES std::cerr << "bad_faces.erase(" - << IO::oformat(fh->vertex(0), With_point_tag{}) << ", " - << IO::oformat(fh->vertex(1), With_point_tag{}) << ", " - << IO::oformat(fh->vertex(2), With_point_tag{}) << ")\n"; + << IO::oformat(fh->vertex(0), with_point_tag) << ", " + << IO::oformat(fh->vertex(1), with_point_tag) << ", " + << IO::oformat(fh->vertex(2), with_point_tag) << ")\n"; #endif // CGAL_MESH_2_DEBUG_BAD_FACES bad_faces.erase(fh); } @@ -372,9 +374,9 @@ compute_new_bad_faces(Vertex_handle v) { #ifdef CGAL_MESH_2_DEBUG_BAD_FACES std::cerr << "Skipping bad face with too small bbox: " - << IO::oformat(fc->vertex(0), With_point_tag{}) << ", " - << IO::oformat(fc->vertex(1), With_point_tag{}) << ", " - << IO::oformat(fc->vertex(2), With_point_tag{}) << "\n"; + << IO::oformat(fc->vertex(0), with_point_tag) << ", " + << IO::oformat(fc->vertex(1), with_point_tag) << ", " + << IO::oformat(fc->vertex(2), with_point_tag) << "\n"; std::cerr << "Quality: " << IO::oformat(q) << "\n"; #endif // CGAL_MESH_2_DEBUG_BAD_FACES continue; diff --git a/Mesh_2/test/Mesh_2/test_mesh_obj.cpp b/Mesh_2/test/Mesh_2/test_mesh_obj.cpp index ade586ac99f2..d7f5600be960 100644 --- a/Mesh_2/test/Mesh_2/test_mesh_obj.cpp +++ b/Mesh_2/test/Mesh_2/test_mesh_obj.cpp @@ -7,6 +7,7 @@ // #define CGAL_MESH_2_DEBUG_INSERTIONS 1 // #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1 #include +#include #include #include #include @@ -92,6 +93,7 @@ static auto save_obj(const Obj_data& data, const std::filesystem::path& filename static int run_mesh(const Obj_data& data) { CDT cdt; + [[maybe_unused]] CGAL::With_point_tag with_point_tag{{-1}, &cdt}; double squared_distance = (std::numeric_limits::max)(); for(const std::vector& id_pl : data.polylines) { @@ -103,8 +105,8 @@ static int run_mesh(const Obj_data& data) va->info() = id_pl[pid - 1]; auto vb = cdt.insert(data.points[id_pl[pid]]); vb->info() = id_pl[pid]; - // std::cerr << "Inserting constraint between " << CGAL::IO::oformat(va, CGAL::With_point_tag{}) - // << " and " << CGAL::IO::oformat(vb, CGAL::With_point_tag{}) << "\n"; + // std::cerr << "Inserting constraint between " << CGAL::IO::oformat(va, with_point_tag) + // << " and " << CGAL::IO::oformat(vb, with_point_tag) << "\n"; cdt.insert_constraint(va, vb); } } @@ -119,8 +121,8 @@ static int run_mesh(const Obj_data& data) auto va = fc->vertex(cdt.cw(edge_index)); auto vb = fc->vertex(cdt.ccw(edge_index)); // std::cerr << "Add constrained edge on the convex hull: "; - // std::cerr << CGAL::IO::oformat(va, CGAL::With_point_tag{}) << " " - // << CGAL::IO::oformat(vb, CGAL::With_point_tag{}) << "\n"; + // std::cerr << CGAL::IO::oformat(va, with_point_tag) << " " + // << CGAL::IO::oformat(vb, with_point_tag) << "\n"; cdt.insert_constraint(va, vb); file_stream << "l " << (va->info() + 1 - points_ids_offsets[va->info()]) << " " << (vb->info() + 1 - points_ids_offsets[vb->info()]) << "\n"; diff --git a/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h b/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h index 4b900e0e6026..c37b93d6ef2a 100644 --- a/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h +++ b/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h @@ -2184,7 +2184,7 @@ class C3T3_helpers auto display_vertex(Vertex_handle v) const { return IO::oformat([&,v](std::ostream& os) -> std::ostream&{ - os << IO::oformat(v, With_point_tag{}); + os << IO::oformat(v, With_point_tag{{-1}, &tr_}); if (v != Vertex_handle()) { os << " (dimension: " << c3t3_.in_dimension(v) << ", index.index(): " << c3t3_.index(v).index() << ")"; diff --git a/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h b/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h index 54f4994839da..0776584bc1a6 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h +++ b/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h @@ -175,6 +175,8 @@ class Protect_edges_sizing_field typedef Delaunay_triangulation_3 Dt; typedef Triangulation_helpers
Dt_helpers; + With_point_tag with_point_tag; + public: Protect_edges_sizing_field(C3T3& c3t3, const MeshDomain& domain, @@ -859,6 +861,7 @@ Protect_edges_sizing_field(C3T3& c3t3, const MD& domain, #ifndef CGAL_NO_ATOMIC , stop_ptr_(stop_ptr) #endif + , with_point_tag{{-1}, &c3t3.triangulation()} { #ifndef CGAL_MESH_3_NO_PROTECTION_NON_LINEAR set_nonlinear_growth_of_balls(); diff --git a/STL_Extension/include/CGAL/Compact_container.h b/STL_Extension/include/CGAL/Compact_container.h index 1beb7796c328..68236e1e6f93 100644 --- a/STL_Extension/include/CGAL/Compact_container.h +++ b/STL_Extension/include/CGAL/Compact_container.h @@ -1240,10 +1240,14 @@ struct With_offset_tag { // from 0 to number_of_vertices-1. }; +template struct With_point_tag : public With_offset_tag { + Tr* tr; }; -struct With_point_and_info_tag : public With_point_tag {}; +template +struct With_point_and_info_tag : public With_point_tag { +}; template struct Output_rep, With_offset_tag> @@ -1266,8 +1270,8 @@ struct Output_rep, With_offset_tag> } }; -template -struct Output_rep, With_point_tag> +template +struct Output_rep, With_point_tag> : public Output_rep, With_offset_tag> { using CC_iterator = CGAL::internal::CC_iterator; @@ -1276,15 +1280,17 @@ struct Output_rep, With_point_tag> using Base::Base; - Output_rep(const CC_iterator it, With_point_tag tag = {}) - : Base(it, tag) {} + With_point_tag tag; + + Output_rep(const CC_iterator it, With_point_tag tag = {}) + : Base(it, tag), tag(tag) {} std::ostream& operator()(std::ostream& out) const { this->Base::operator()(out); if(this->it.operator->() != nullptr) { if(Time_stamper::time_stamp(this->it.operator->()) == 0) return out << "= infinite_vertex()"; - return out << "= " << this->it->point(); + return out << "= " << tag.tr->point(this->it); } else return out; diff --git a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h index db2745e92c2f..ab41ed98e965 100644 --- a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h +++ b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h @@ -55,55 +55,59 @@ class Intersection_of_constraints_exception : public std::runtime_error return std::string("Unauthorized intersections of constraints"); } - template + template struct Can_the_point_be_displayed : std::false_type {}; - template + template struct Can_the_point_be_displayed< + Tr, Vertex_handle, - std::void_t() << std::declval()->point())>> : std::true_type + std::void_t() << std::declval().point(std::declval()))>> + : std::true_type {}; - template - static inline constexpr bool can_the_point_be_displayed = Can_the_point_be_displayed::value; + template + static inline constexpr bool can_the_point_be_displayed = Can_the_point_be_displayed::value; - template - static auto with_point(Vertex_handle v) { - return IO::oformat([v](std::ostream& os) -> auto& { - return os << IO::oformat(v, With_point_tag{}); + template + static auto with_point(Tr* tr, Vertex_handle v) { + return IO::oformat([v, tr](std::ostream& os) -> auto& { + return os << IO::oformat(v, With_point_tag{{-1}, tr}); }, IO_manip_tag{}); } - template - static auto construct_what(Vertex_handle vaa, + template + static auto construct_what(Tr* tr, + Vertex_handle vaa, Vertex_handle vbb, Vertex_handle v1, Vertex_handle v2) { - if constexpr(can_the_point_be_displayed) { + if constexpr(can_the_point_be_displayed) { std::stringstream ss; ss.copyfmt(std::cerr); ss << "Unauthorized intersections of constraints: ( " - << with_point(vaa) << " " << with_point(vbb) << " )" << " intersects with (" - << with_point(v1) << ", " << with_point(v2) << ")"; + << with_point(tr, vaa) << " " << with_point(tr, vbb) << " )" << " intersects with (" + << with_point(tr, v1) << ", " << with_point(tr, v2) << ")"; return ss.str(); } else { return std::string("Unauthorized intersections of constraints"); } } - template - static auto construct_what(Point p, + static auto construct_what(Tr* tr, + Point p, Vertex_handle v1, Vertex_handle v2) { - if constexpr(can_the_point_be_displayed) { + if constexpr(can_the_point_be_displayed) { std::stringstream ss; ss.copyfmt(std::cerr); ss << "Unauthorized intersections of constraints: point " - << p << " intersects with (" << with_point(v1) << ", " << with_point(v2) << ")"; + << p << " intersects with (" << with_point(tr, v1) << ", " << with_point(tr, v2) << ")"; return ss.str(); } else { return std::string("Unauthorized intersections of constraints"); @@ -111,9 +115,9 @@ class Intersection_of_constraints_exception : public std::runtime_error } public: - template - Intersection_of_constraints_exception(Args&&... args) - : std::runtime_error(construct_what(std::forward(args)...)) + template + Intersection_of_constraints_exception(Tr* tr, Args&&... args) + : std::runtime_error(construct_what(tr, std::forward(args)...)) {} }; @@ -413,7 +417,7 @@ insert_constraint(Vertex_handle vaa, Vertex_handle vbb, OutputIterator out) // then this is an intersection if constexpr (std::is_same_v) { if(fr->is_constrained(dimension() == 1 ? 2 : i)) { - throw Intersection_of_constraints_exception(vaa, vbb, vaa, vi); + throw Intersection_of_constraints_exception(this, vaa, vbb, vaa, vi); } } @@ -661,7 +665,7 @@ insert_constraint(Vertex_handle vaa, Vertex_handle vbb, OutputIterator out) #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS auto display_vertex(Vertex_handle v) const { - With_point_tag point_tag; + With_point_tag point_tag{ {-1}, this }; using CGAL::IO::oformat; return IO::oformat(v, point_tag); } @@ -792,7 +796,7 @@ insert(const Point& a, Locate_type lt, Face_handle loc, int li) v1=loc->vertex(ccw(li)); //endpoint of the constraint v2=loc->vertex(cw(li)); // endpoint of the constraint if constexpr (std::is_same_v) - throw Intersection_of_constraints_exception(a, v1, v2); + throw Intersection_of_constraints_exception(this, a, v1, v2); insert_in_constrained_edge = true; } @@ -904,7 +908,7 @@ insert_constraint(Vertex_handle vaa, Vertex_handle vbb) // then this is an intersection if constexpr (std::is_same_v) { if(fr->is_constrained(dimension() == 1 ? 2 : i)) { - throw Intersection_of_constraints_exception(vaa, vbb, vaa, vi); + throw Intersection_of_constraints_exception(this, vaa, vbb, vaa, vi); } } @@ -1101,7 +1105,7 @@ intersect(Face_handle f, int i, No_constraint_intersection_tag) { auto [v1, v2] = this->vertices(f, i); - throw Intersection_of_constraints_exception(vaa, vbb, v1, v2); + throw Intersection_of_constraints_exception(this, vaa, vbb, v1, v2); return Vertex_handle() ; } @@ -1114,7 +1118,7 @@ intersect(Face_handle f, int i, No_constraint_intersection_requiring_constructions_tag) { auto [v1, v2] = this->vertices(f, i); - throw Intersection_of_constraints_exception(vaa, vbb, v1, v2); + throw Intersection_of_constraints_exception(this, vaa, vbb, v1, v2); return Vertex_handle() ; } diff --git a/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h b/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h index 7111696b19f1..c411543ef74a 100644 --- a/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h +++ b/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h @@ -1092,7 +1092,7 @@ insert(const Point& a, Locate_type lt, Face_handle loc, int li) v1=loc->vertex(ccw(li)); //endpoint of the constraint v2=loc->vertex(cw(li)); // endpoint of the constraint if(std::is_same::value) - throw typename Tr::Intersection_of_constraints_exception(a, v1, v2); + throw typename Tr::Intersection_of_constraints_exception(this, a, v1, v2); insert_in_constrained_edge = true; } @@ -1132,7 +1132,7 @@ intersect(Face_handle f, int i, No_constraint_intersection_tag) { auto [v1, v2] = this->vertices(f, i); - throw typename Tr::Intersection_of_constraints_exception(vaa, vbb, v1, v2); + throw typename Tr::Intersection_of_constraints_exception(this, vaa, vbb, v1, v2); return Vertex_handle(); } @@ -1145,7 +1145,7 @@ intersect(Face_handle f, int i, No_constraint_intersection_requiring_constructions_tag) { auto [v1, v2] = this->vertices(f, i); - throw typename Tr::Intersection_of_constraints_exception(vaa, vbb, v1, v2); + throw typename Tr::Intersection_of_constraints_exception(this, vaa, vbb, v1, v2); return Vertex_handle(); } diff --git a/Triangulation_2/test/Triangulation_2/issue_4025.cpp b/Triangulation_2/test/Triangulation_2/issue_4025.cpp index 150f7cb60e6b..83ea9f160022 100644 --- a/Triangulation_2/test/Triangulation_2/issue_4025.cpp +++ b/Triangulation_2/test/Triangulation_2/issue_4025.cpp @@ -39,11 +39,6 @@ decltype(auto) value_check_expected(V&& value, [[maybe_unused]] const E& expecte return std::forward(value); }; -auto oformat(Vertex_handle vh) -{ - return CGAL::IO::oformat(vh, CGAL::With_point_tag{}); -}; - int main() { CDTP cdtp; @@ -54,6 +49,11 @@ int main() cdtp.print_hierarchy(std::cout); }; + auto oformat = [tr = &cdtp](Vertex_handle vh) + { + return CGAL::IO::oformat(vh, CGAL::With_point_tag{ {-1}, tr }); + }; + const std::array collinear_points = { Point(0,0), Point(0,1), Point(0,2), Point(0,3), Point(0,4), Point(0,5) }; From 649da5796d9bb3da2d0f1cbeb49b3410ea66a370 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 15 Oct 2026 15:33:05 +0200 Subject: [PATCH 149/151] fix the compilation error: the forwarding constructor was masking the copy- and move-constructors --- .../include/CGAL/Constrained_triangulation_2.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h index ab41ed98e965..920f9c45fe43 100644 --- a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h +++ b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -115,10 +116,11 @@ class Intersection_of_constraints_exception : public std::runtime_error } public: - template - Intersection_of_constraints_exception(Tr* tr, Args&&... args) - : std::runtime_error(construct_what(tr, std::forward(args)...)) - {} + template , + Intersection_of_constraints_exception>)>* = nullptr> + Intersection_of_constraints_exception(Arg&& arg, Args&&... args) + : std::runtime_error(construct_what(std::forward(arg), std::forward(args)...)) {} }; // This was deprecated and replaced by ` No_constraint_intersection_tag` and `No_constraint_intersection_requiring_constructions_tag` From fd8fa0f3df4be574dfd7a88b8df4fad04a570cad Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 15 Oct 2026 17:31:55 +0200 Subject: [PATCH 150/151] fix CTAD compilation issues (accepted by C++20 but not C++17) --- Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h | 2 +- .../CGAL/Mesh_3/Protect_edges_sizing_field.h | 15 ++++++++------- .../include/CGAL/Constrained_triangulation_2.h | 2 +- .../test/Triangulation_2/issue_4025.cpp | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h b/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h index c37b93d6ef2a..6ec21531b65c 100644 --- a/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h +++ b/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h @@ -2184,7 +2184,7 @@ class C3T3_helpers auto display_vertex(Vertex_handle v) const { return IO::oformat([&,v](std::ostream& os) -> std::ostream&{ - os << IO::oformat(v, With_point_tag{{-1}, &tr_}); + os << IO::oformat(v, With_point_tag{{-1}, &tr_}); if (v != Vertex_handle()) { os << " (dimension: " << c3t3_.in_dimension(v) << ", index.index(): " << c3t3_.index(v).index() << ")"; diff --git a/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h b/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h index 0776584bc1a6..9ffdd6c63451 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h +++ b/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h @@ -175,8 +175,6 @@ class Protect_edges_sizing_field typedef Delaunay_triangulation_3 Dt; typedef Triangulation_helpers
Dt_helpers; - With_point_tag with_point_tag; - public: Protect_edges_sizing_field(C3T3& c3t3, const MeshDomain& domain, @@ -534,6 +532,10 @@ class Protect_edges_sizing_field return c3t3_.triangulation().point(vh); } + auto with_point_tag() const { + return With_point_tag{{-1}, &tr()}; + } + protected: using get_curve_output_type = Mesh_domain_get_curves_output_type_t; @@ -729,7 +731,7 @@ class Protect_edges_sizing_field std::cerr << "Register corner #" << key.first << " on curve " << curve_index << " with position_on_curve=" << (position_on_curve - start_position) << "\n" - << " vp: " << IO::oformat(corner_vertices_.at(key.first), With_point_tag{}) << "\n"; + << " vp: " << IO::oformat(corner_vertices_.at(key.first), with_point_tag()) << "\n"; #endif [[maybe_unused]] auto [_, inserted] = corners_on_curves_.try_emplace(key, position_on_curve); CGAL_assertion(true == inserted); @@ -748,7 +750,7 @@ class Protect_edges_sizing_field std::invoke([&] { std::ostringstream ss; ss.copyfmt(std::cerr); - ss << "position_on_curve(v=" << IO::oformat(v, With_point_tag{}) + ss << "position_on_curve(v=" << IO::oformat(v, with_point_tag()) << " (corner index: " << corner_index << ", curve_index=" << curve_index << "): v is a corner but its position is not found!"; return ss.str(); @@ -861,7 +863,6 @@ Protect_edges_sizing_field(C3T3& c3t3, const MD& domain, #ifndef CGAL_NO_ATOMIC , stop_ptr_(stop_ptr) #endif - , with_point_tag{{-1}, &c3t3.triangulation()} { #ifndef CGAL_MESH_3_NO_PROTECTION_NON_LINEAR set_nonlinear_growth_of_balls(); @@ -1129,7 +1130,7 @@ smart_insert_point(const Bare_point& p, Weight w, int dim, const Index& index, #if CGAL_MESH_3_PROTECTION_DEBUG & 16 std::cerr << "Nearest power vertex of (" << p << ") is " - << IO::oformat(nearest_vh, With_point_tag{}) + << IO::oformat(nearest_vh, with_point_tag()) << "at distance: " << sq_d << std::endl; #endif @@ -1365,7 +1366,7 @@ insert_balls_on_edges() if(tr().is_vertex(cwp(p), vp)) { #if CGAL_MESH_3_PROTECTION_DEBUG & 128 - std::cerr << "vp: " << IO::oformat(vp, With_point_tag{}) << "\n"; + std::cerr << "vp: " << IO::oformat(vp, with_point_tag()) << "\n"; std::cerr << "vp.in_dimension(): " << vp->in_dimension() << "\n"; std::cerr << "p_dim: " << p_dim << "\n"; std::cerr << "p_index: " << IO::oformat(p_index) << "\n"; diff --git a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h index 920f9c45fe43..3ccaa751a878 100644 --- a/Triangulation_2/include/CGAL/Constrained_triangulation_2.h +++ b/Triangulation_2/include/CGAL/Constrained_triangulation_2.h @@ -74,7 +74,7 @@ class Intersection_of_constraints_exception : public std::runtime_error template static auto with_point(Tr* tr, Vertex_handle v) { return IO::oformat([v, tr](std::ostream& os) -> auto& { - return os << IO::oformat(v, With_point_tag{{-1}, tr}); + return os << IO::oformat(v, With_point_tag{{-1}, tr}); }, IO_manip_tag{}); } diff --git a/Triangulation_2/test/Triangulation_2/issue_4025.cpp b/Triangulation_2/test/Triangulation_2/issue_4025.cpp index 83ea9f160022..27517591d2bb 100644 --- a/Triangulation_2/test/Triangulation_2/issue_4025.cpp +++ b/Triangulation_2/test/Triangulation_2/issue_4025.cpp @@ -51,7 +51,7 @@ int main() auto oformat = [tr = &cdtp](Vertex_handle vh) { - return CGAL::IO::oformat(vh, CGAL::With_point_tag{ {-1}, tr }); + return CGAL::IO::oformat(vh, CGAL::With_point_tag{{-1}, tr }); }; const std::array collinear_points = { From 2d68e1cb4f38024247464643f6a87a83d0340b55 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 15 Oct 2026 18:03:15 +0200 Subject: [PATCH 151/151] fix another compilation error --- Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h b/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h index 9ffdd6c63451..9844012664e3 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h +++ b/Mesh_3/include/CGAL/Mesh_3/Protect_edges_sizing_field.h @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -533,7 +534,7 @@ class Protect_edges_sizing_field } auto with_point_tag() const { - return With_point_tag{{-1}, &tr()}; + return With_point_tag{{-1}, &tr()}; } protected: