From f4aa3831771ffe9df51cd4c3b467b0e4edf6e3c3 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 30 Sep 2025 12:49:02 +0200 Subject: [PATCH 01/51] add new runtime debug flags --- .../Conforming_Delaunay_triangulation_3.h | 149 +++++++++++++----- ...ing_constrained_Delaunay_triangulation_3.h | 76 ++++----- .../cdt_3_from_off.cpp | 41 ++++- 3 files changed, 179 insertions(+), 87 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 b98eccbb60e..223886c3112 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -169,10 +169,10 @@ protected: void add_to_subconstraints_to_conform(Vertex_handle va, Vertex_handle vb, Constrained_polyline_id id) { const auto pair = make_subconstraint(va, vb); -#if CGAL_DEBUG_CDT_3 & 32 - std::cerr << "tr().subconstraints_to_conform.push(" - << display_subcstr(pair) << ")\n"; -#endif // CGAL_DEBUG_CDT_3 + if(debug_subconstraints_to_conform()) { + std::cerr << "tr().subconstraints_to_conform.push(" + << display_subcstr(pair) << ")\n"; + } subconstraints_to_conform.push({pair, id}); } @@ -385,6 +385,14 @@ public: debug_flags.set(static_cast(Debug_flags::debug_finite_edges_map), b); } + bool debug_subconstraints_to_conform() const { + return debug_flags[static_cast(Debug_flags::debug_subconstraints_to_conform)]; + } + + void debug_subconstraints_to_conform(bool b) { + debug_flags.set(static_cast(Debug_flags::debug_subconstraints_to_conform), b); + } + bool use_finite_edges_map() const { return update_all_finite_edges_ && debug_flags[static_cast(Debug_flags::use_finite_edges_map)]; } @@ -393,6 +401,54 @@ public: debug_flags.set(static_cast(Debug_flags::use_finite_edges_map), b); } + bool debug_verbose_special_cases() const { + return debug_flags[static_cast(Debug_flags::verbose_special_cases)]; + } + + void debug_verbose_special_cases(bool b) { + debug_flags.set(static_cast(Debug_flags::verbose_special_cases), b); + } + + bool debug_encroaching_vertices() const { + return debug_flags[static_cast(Debug_flags::debug_encroaching_vertices)]; + } + + void debug_encroaching_vertices(bool b) { + debug_flags.set(static_cast(Debug_flags::debug_encroaching_vertices), b); + } + + bool debug_conforming_validation() const { + return debug_flags[static_cast(Debug_flags::debug_conforming_validation)]; + } + + void debug_conforming_validation(bool b) { + debug_flags.set(static_cast(Debug_flags::debug_conforming_validation), b); + } + + bool debug_constraint_hierarchy() const { + return debug_flags[static_cast(Debug_flags::debug_constraint_hierarchy)]; + } + + void debug_constraint_hierarchy(bool b) { + debug_flags.set(static_cast(Debug_flags::debug_constraint_hierarchy), b); + } + + bool debug_geometric_errors() const { + return debug_flags[static_cast(Debug_flags::debug_geometric_errors)]; + } + + void debug_geometric_errors(bool b) { + debug_flags.set(static_cast(Debug_flags::debug_geometric_errors), b); + } + + bool debug_polygon_insertion() const { + return debug_flags[static_cast(Debug_flags::debug_polygon_insertion)]; + } + + void debug_polygon_insertion(bool b) { + debug_flags.set(static_cast(Debug_flags::debug_polygon_insertion), b); + } + Vertex_handle insert(const Point &p, Locate_type lt, Cell_handle c, int li, int lj) { @@ -446,12 +502,12 @@ public: [this](const auto &sc) { const auto [va, vb] = sc; const auto is_edge = this->is_edge(va, vb); -#if CGAL_DEBUG_CDT_3 & 128 && CGAL_CAN_USE_CXX20_FORMAT - 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)); -#endif // CGAL_DEBUG_CDT_3 + 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)); + } return is_edge; }); } @@ -587,10 +643,10 @@ protected: if(!constraint_hierarchy.is_subconstraint(va, vb)) { continue; } -#if CGAL_DEBUG_CDT_3 & 32 - std::cerr << "tr().subconstraints_to_conform.pop()=" - << display_subcstr(subconstraint) << "\n"; -#endif // CGAL_DEBUG_CDT_3 + if(debug_subconstraints_to_conform()) { + std::cerr << "tr().subconstraints_to_conform.pop()=" + << display_subcstr(subconstraint) << "\n"; + } conform_subconstraint(subconstraint, constrained_polyline_id, visitor); } } @@ -669,10 +725,10 @@ protected: 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)); -#if CGAL_DEBUG_CDT_3 & 8 - std::cerr << "constraint " << (void*) c_id.vl_ptr() << " has " - << c_id.vl_ptr()->skip_size() << " vertices\n"; -#endif // CGAL_DEBUG_CDT_3 + 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); const auto c_va = *begin; @@ -730,9 +786,9 @@ protected: encroaching_vertices.insert(v); }; auto fill_encroaching_vertices = [&](const auto simplex) { -#if CGAL_DEBUG_CDT_3 & 0x10 - std::cerr << " - " << IO::oformat(simplex, With_point_tag{}) << '\n'; -#endif // CGAL_DEBUG_CDT_3 + if(debug_encroaching_vertices()) { + std::cerr << " - " << IO::oformat(simplex, With_point_tag{}) << '\n'; + } auto visit_cell = [&](Cell_handle cell) { for(int i = 0, end = this->tr().dimension() + 1; i < end; ++i) { const auto v = cell->vertex(i); @@ -776,9 +832,9 @@ protected: std::cerr << "!! The constraint passes through a vertex!\n"; std::cerr << " -> constraint " << display_vert(va) << " " << display_vert(vb) << '\n'; std::cerr << " -> vertex " << display_vert(v) << '\n'; -#if CGAL_DEBUG_CDT_3 - debug_dump("bug-through-vertex"); -#endif + if(debug_geometric_errors()) { + debug_dump("bug-through-vertex"); + } CGAL_error(); } } break; @@ -788,14 +844,14 @@ protected: std::for_each(tr().segment_traverser_simplices_begin(va, vb), tr().segment_traverser_simplices_end(), fill_encroaching_vertices); auto vector_of_encroaching_vertices = encroaching_vertices.extract_sequence(); -#if CGAL_DEBUG_CDT_3 & 0x10 - std::cerr << " -> vector_of_encroaching_vertices (before filter):\n"; - std::for_each(vector_of_encroaching_vertices.begin(), - vector_of_encroaching_vertices.end(), - [this](Vertex_handle v){ - std::cerr << " " << this->display_vert(v) << '\n'; - }); -#endif // CGAL_DEBUG_CDT_3 + if(debug_encroaching_vertices()) { + std::cerr << " -> vector_of_encroaching_vertices (before filter):\n"; + std::for_each(vector_of_encroaching_vertices.begin(), + vector_of_encroaching_vertices.end(), + [this](Vertex_handle v){ + std::cerr << " " << this->display_vert(v) << '\n'; + }); + } auto end = std::remove_if(vector_of_encroaching_vertices.begin(), vector_of_encroaching_vertices.end(), [va, vb, pa, pb, &angle_functor, this](Vertex_handle v) { @@ -804,13 +860,13 @@ protected: this->tr().point(v), pb) == ACUTE; }); -#if CGAL_DEBUG_CDT_3 & 0x10 - std::cerr << " -> vector_of_encroaching_vertices (after filter):\n"; - std::for_each(vector_of_encroaching_vertices.begin(), end, [&](Vertex_handle v) { - std::cerr << " " << this->display_vert(v) << " angle " << approximate_angle(pa, this->tr().point(v), pb) - << '\n'; - }); -#endif // CGAL_DEBUG_CDT_3 + if(debug_encroaching_vertices()) { + std::cerr << " -> vector_of_encroaching_vertices (after filter):\n"; + std::for_each(vector_of_encroaching_vertices.begin(), end, [&](Vertex_handle v) { + std::cerr << " " << this->display_vert(v) << " angle " << approximate_angle(pa, this->tr().point(v), pb) + << '\n'; + }); + } vector_of_encroaching_vertices.erase(end, vector_of_encroaching_vertices.end()); return vector_of_encroaching_vertices; } @@ -840,10 +896,10 @@ protected: return {midpoint_functor(pa, pb), va->cell(), va}; } -#if CGAL_DEBUG_CDT_3 & 0x10 - std::cerr << "construct_Steiner_point( " << display_vert(va) << " , " - << display_vert(vb) << " )\n"; -#endif // CGAL_DEBUG_CDT_3 + if(debug_encroaching_vertices()) { + std::cerr << "construct_Steiner_point( " << display_vert(va) << " , " + << display_vert(vb) << " )\n"; + } const auto vector_of_encroaching_vertices = encroaching_vertices(va, vb); CGAL_assertion(vector_of_encroaching_vertices.size() > 0); @@ -984,6 +1040,13 @@ protected: use_older_cavity_algorithm, debug_finite_edges_map, use_finite_edges_map, + debug_subconstraints_to_conform, + verbose_special_cases, + debug_encroaching_vertices, + debug_conforming_validation, + debug_constraint_hierarchy, + debug_geometric_errors, + debug_polygon_insertion, nb_of_flags }; std::bitset(Debug_flags::nb_of_flags)> debug_flags{}; 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 21b63c5eb3a..1e019b9361a 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 @@ -1805,9 +1805,9 @@ private: if(this->is_edge(vb_3d, vd_3d)) { // let's insert the diagonal [bd] in the CDT_2 cdt_2.insert_constraint(vb, vd); -#if CGAL_DEBUG_CDT_3 & 64 - std::cerr << "NOTE: CDT_2 has 4 vertices. Flip the diagonal\n"; -#endif + if(this->debug_verbose_special_cases()) { + std::cerr << "NOTE: CDT_2 has 4 vertices. Flip the diagonal\n"; + } } } break; @@ -2762,12 +2762,6 @@ private: add_pseudo_cells_to_outer_map(upper_cavity_triangulation, map_upper_cavity_vertices_to_ambient_vertices, true); { -// #if CGAL_DEBUG_CDT_3 & 64 -// std::ofstream out("dump_upper_outer_map.off"); -// out.precision(17); -// write_facets(out, *this, std::ranges::views::values(outer_map)); -// out.close(); -// #endif // CGAL_DEBUG_CDT_3 const auto upper_inner_map = tr().create_triangulation_inner_map( upper_cavity_triangulation, map_upper_cavity_vertices_to_ambient_vertices, false); @@ -2788,9 +2782,9 @@ private: upper_inner_map, this->new_cells_output_iterator()); } -#if CGAL_DEBUG_CDT_3 & 64 - std::cerr << "# glu the lower triangulation of the cavity\n"; -#endif // CGAL_DEBUG_CDT_3 + if(this->debug_copy_triangulation_into_hole()) { + std::cerr << "# glu the lower triangulation of the cavity\n"; + } outer_map.clear(); std::vector> new_constrained_facets; @@ -3083,10 +3077,10 @@ private: { const auto& cdt_2 = non_const_cdt_2; auto steiner_pt = CGAL::centroid(cdt_2.triangle(fh_2d)); -#if CGAL_DEBUG_CDT_3 & 64 && CGAL_CAN_USE_CXX20_FORMAT - std::cerr << cdt_3_format("Trying to insert Steiner (centroid) point {} in non-coplanar face {}.\n", IO::oformat(steiner_pt), - IO::oformat(cdt_2.triangle(fh_2d))); -#endif // CGAL_DEBUG_CDT_3 + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_verbose_special_cases()) { + std::cerr << cdt_3_format("Trying to insert Steiner (centroid) point {} in non-coplanar face {}.\n", IO::oformat(steiner_pt), + IO::oformat(cdt_2.triangle(fh_2d))); + } auto encroached_edge_opt = return_encroached_constrained_edge(face_index, cdt_2, steiner_pt); if(encroached_edge_opt) { return encroached_edge_opt; @@ -3190,22 +3184,22 @@ private: IO::oformat(vb_3d, with_point_and_info)); } auto&& contexts = this->constraint_hierarchy.contexts(va_3d, vb_3d); -#if CGAL_DEBUG_CDT_3 & 64 && CGAL_CAN_USE_CXX20_FORMAT - if(std::next(contexts.begin()) != contexts.end()) { - std::cerr << "ERROR: Edge is constrained by more than one constraint\n"; - for(const auto& c : contexts) { - std::cerr << cdt_3_format(" - {} with {} vertices\n", IO::oformat(c.id().vl_ptr()), - c.number_of_vertices()); - for(auto vh_it = c.vertices_begin(), end = c.vertices_end(), current = c.current(); - vh_it != end; ++vh_it) - { - std::cerr << cdt_3_format(" {} {}\n", - (vh_it == current) ? '>' : '-', - IO::oformat(*vh_it, with_point_and_info)); + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_verbose_special_cases()) { + if(std::next(contexts.begin()) != contexts.end()) { + std::cerr << "ERROR: Edge is constrained by more than one constraint\n"; + for(const auto& c : contexts) { + std::cerr << cdt_3_format(" - {} with {} vertices\n", IO::oformat(c.id().vl_ptr()), + c.number_of_vertices()); + for(auto vh_it = c.vertices_begin(), end = c.vertices_end(), current = c.current(); + vh_it != end; ++vh_it) + { + std::cerr << cdt_3_format(" {} {}\n", + (vh_it == current) ? '>' : '-', + IO::oformat(*vh_it, with_point_and_info)); + } } } } -#endif // CGAL_DEBUG_CDT_3 & 64 CGAL_assertion(std::next(contexts.begin()) == contexts.end()); const auto& context = *contexts.begin(); const auto constrained_polyline_id = context.id(); @@ -3278,18 +3272,18 @@ private: std::cerr << "NOTE: " << what << " in sub-region " << (region_index - 1) << " of face #F" << face_index << '\n'; } -#if CGAL_DEBUG_CDT_3 & 64 && CGAL_CAN_USE_CXX20_FORMAT - std::cerr << " constrained edges are:\n"; - 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)); + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_verbose_special_cases()) { + std::cerr << " constrained edges are:\n"; + 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)); + } } -#endif // CGAL_DEBUG_CDT_3 const auto encroach_edge_opt = try_to_insert_circumcenter_in_face_or_return_encroached_edge(face_index, non_const_cdt_2, fh_2d); if(encroach_edge_opt) { @@ -3472,7 +3466,7 @@ public: if(restore_face(static_cast (i))) { face_constraint_misses_subfaces_reset(i); } else { - if(this->debug_missing_region()) { + if(this->debug_missing_region() || this->debug_Steiner_points()) { std::cerr << "restore_face(" << i << ") incomplete, back to conforming...\n"; } Conforming_Dt::restore_Delaunay(insert_in_conflict_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 c0bc1ce3e77..955ee804473 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 @@ -93,6 +93,13 @@ Usage: cdt_3_from_off [options] input.off output.off --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 --use-finite-edges-map: use a hash map for finite edges (default: false) --verbose/-V: verbose (can be used several times) @@ -122,6 +129,13 @@ struct CDT_options bool use_new_cavity_algorithm = true; bool debug_validity = false; bool debug_finite_edges_map = false; + bool debug_subconstraints_to_conform = false; + bool debug_verbose_special_cases = false; + bool debug_encroaching_vertices = false; + bool debug_conforming_validation = false; + bool debug_constraint_hierarchy = false; + bool debug_geometric_errors = false; + bool debug_polygon_insertion = false; bool use_finite_edges_map = false; bool call_is_valid = true; double ratio = 0.1; @@ -204,6 +218,20 @@ CDT_options::CDT_options(int argc, char* argv[]) { 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 == "--use-finite-edges-map"sv) { use_finite_edges_map = true; } else if(arg == "--verbose"sv || arg == "-V"sv) { @@ -272,6 +300,13 @@ int go(Mesh mesh, CDT_options options) { cdt.debug_regions(options.debug_regions); cdt.debug_validity(options.debug_validity); cdt.debug_finite_edges_map(options.debug_finite_edges_map); + cdt.debug_subconstraints_to_conform(options.debug_subconstraints_to_conform); + 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_constraint_hierarchy(options.debug_constraint_hierarchy); + cdt.debug_geometric_errors(options.debug_geometric_errors); + cdt.debug_polygon_insertion(options.debug_polygon_insertion); cdt.debug_copy_triangulation_into_hole(options.debug_copy_triangulation_into_hole); cdt.use_older_cavity_algorithm(!options.use_new_cavity_algorithm); cdt.use_finite_edges_map(options.use_finite_edges_map); @@ -704,9 +739,9 @@ int go(Mesh mesh, CDT_options options) { for(auto vertex_it : CGAL::vertices_around_face(he, mesh)) { polygon.push_back(get(pmap, vertex_it)); } - #if CGAL_DEBUG_CDT_3 - std::cerr << "NEW POLYGON #" << poly_id << '\n'; - #endif // CGAL_DEBUG_CDT_3 + if(cdt.debug_polygon_insertion()) { + std::cerr << "NEW POLYGON #" << poly_id << '\n'; + } try { [[maybe_unused]] auto id = cdt.insert_constrained_polygon(polygon, false); assert(id == poly_id); From d309bc89ce33cdb971d5fb17d628f5ba151c6c08 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 30 Sep 2025 17:11:15 +0200 Subject: [PATCH 02/51] fix typo "and and" --- .../ConformingConstrainedDelaunayTriangulationCellBase_3.h | 2 +- Documentation/doc/biblio/geom.bib | 2 +- Interval_skip_list/include/CGAL/Interval_skip_list.h | 2 +- .../include/CGAL/Polygon_mesh_processing/distance.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Constrained_triangulation_3/doc/Constrained_triangulation_3/Concepts/ConformingConstrainedDelaunayTriangulationCellBase_3.h b/Constrained_triangulation_3/doc/Constrained_triangulation_3/Concepts/ConformingConstrainedDelaunayTriangulationCellBase_3.h index e31bc212cd2..34ed4a4bcb4 100644 --- a/Constrained_triangulation_3/doc/Constrained_triangulation_3/Concepts/ConformingConstrainedDelaunayTriangulationCellBase_3.h +++ b/Constrained_triangulation_3/doc/Constrained_triangulation_3/Concepts/ConformingConstrainedDelaunayTriangulationCellBase_3.h @@ -3,7 +3,7 @@ \cgalConcept The concept `ConformingConstrainedDelaunayTriangulationCellBase_3` refines the concept -`TriangulationCellBase_3` and and describes the requirements for a base cell class of +`TriangulationCellBase_3` and describes the requirements for a base cell class of the `CGAL::Conforming_constrained_Delaunay_triangulation_3` class. \cgalRefines{TriangulationCellBase_3, BaseWithTimeStamp} diff --git a/Documentation/doc/biblio/geom.bib b/Documentation/doc/biblio/geom.bib index 8b08afe0107..07295e7aab1 100644 --- a/Documentation/doc/biblio/geom.bib +++ b/Documentation/doc/biblio/geom.bib @@ -118600,7 +118600,7 @@ both for rendering and for modeling. Contains C code." @article{ph-ddocs-92 , author = "J. P. Pratt and V. P. Heuring" -, title = "Designing digital optical computing systems: power and and crosstalk" +, title = "Designing digital optical computing systems: power and crosstalk" , journal = "Appl. Optics" , volume = 31 , number = 23 diff --git a/Interval_skip_list/include/CGAL/Interval_skip_list.h b/Interval_skip_list/include/CGAL/Interval_skip_list.h index d2721b844ff..576ebe76477 100644 --- a/Interval_skip_list/include/CGAL/Interval_skip_list.h +++ b/Interval_skip_list/include/CGAL/Interval_skip_list.h @@ -177,7 +177,7 @@ class Interval_for_container : public Interval_ // remove markers for Interval I starting at left, the left endpoint - // of I, and and stopping at the right endpoint of I. + // of I, and stopping at the right endpoint of I. Interval_handle removeMarkers(IntervalSLnode* left, const Interval& I); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h index 29d4d5810bb..d52812fb783 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h @@ -1363,7 +1363,7 @@ preprocess_bounded_error_squared_Hausdorff_distance_impl(const TriangleMesh1& tm if(is_one_sided_distance) // one-sided distance { - if(tm1_only.size() > 0) // create TM1 and and full TM2 + if(tm1_only.size() > 0) // create TM1 and full TM2 { tm1_tree.insert(tm1_only.begin(), tm1_only.end(), tm1, vpm1); tm2_tree.insert(faces2.begin(), faces2.end(), tm2, vpm2); From 8c150b865caaec2a41a0965304d0a2e72473525d Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 30 Sep 2025 17:35:12 +0200 Subject: [PATCH 03/51] remove completely the type traits `CGAL::is_complete_v` - its use would be undefined-behavior if `T` was later defined as complete, - its implementation was undefined-behavior anyway, because `sizeof` cannot be used on incomplete types --- STL_Extension/include/CGAL/type_traits.h | 29 ------------------------ 1 file changed, 29 deletions(-) diff --git a/STL_Extension/include/CGAL/type_traits.h b/STL_Extension/include/CGAL/type_traits.h index c97f28cfbee..d28840945e4 100644 --- a/STL_Extension/include/CGAL/type_traits.h +++ b/STL_Extension/include/CGAL/type_traits.h @@ -76,35 +76,6 @@ struct is_convertible_without_narrowing : details::is_convertible_without_narrow template inline constexpr bool is_convertible_without_narrowing_v = is_convertible_without_narrowing::value; -#if 0 -namespace is_complete_internals -{ - template - std::enable_if_t - check(T(*)()); - - std::false_type check(...); -}; - -template::type()))> -struct is_complete : Base { }; - -template -inline constexpr bool is_complete_v = is_complete::value; - -namespace is_complete_testsuite -{ - - struct S1; - static_assert(!is_complete::value, "error"); - struct S2 - { - static_assert(!is_complete::value, "error"); - }; - struct S3 {}; - static_assert(is_complete::value, "error"); -} -#endif } // end namespace CGAL From 2a815ff5103cd1ae0ceec54a83d4102ec6d192c2 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 3 Oct 2025 09:37:02 +0200 Subject: [PATCH 04/51] add a label to the fdata sets segfaulting with --merge-facets --- .../Constrained_triangulation_3/Thingi10k-CDT.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake b/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake index d53f0c247fa..fc072a5f1d7 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake @@ -109,6 +109,15 @@ set(thingi10k_FAILED_WITH_MERGE_FACETS_CTest_20240222_2201 1514904.stl ) +set(thingi10k_FAILED_WITH_SEGFAULT_CTest_20251002 +1439534.stl +196123.stl +200695.stl +135777.stl +285604.stl +822697.stl +) + function(CGAL_add_cdt3_test_from_Thingi10k data_name data_filename) set(options "ONLY_MERGE_FACETS") set(oneValueArgs TIMEOUT) @@ -145,6 +154,9 @@ foreach(thingi_file_name ${thingi10k_max_10k_solid}) if(thingi_file_name IN_LIST thingi10k_FAILED_WITH_MERGE_FACETS_CTest_20240222_2201) list(APPEND LABELS "CTest_20240222_2201_failed_merge_facets") endif() + if(thingi_file_name IN_LIST thingi10k_FAILED_WITH_SEGFAULT_CTest_20251002) + list(APPEND LABELS "CTest_20251002_failed_segfault") + endif() get_filename_component(thingi_ID "${thingi_file_name}" NAME_WE) CGAL_add_cdt3_test_from_Thingi10k(Thingi10K_${thingi_ID} ${thingi_file_name} TIMEOUT 600 LABELS ${LABELS} ${MY_ONLY_MERGE_FACETS}) From ba10efcbe477feb28f2a74a2d1c8a2fb20f750b0 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 3 Oct 2025 09:38:10 +0200 Subject: [PATCH 05/51] more runtime debug possibilities --- ...ing_constrained_Delaunay_triangulation_3.h | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 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 1e019b9361a..f2bfe4dcbcd 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 @@ -1498,6 +1498,18 @@ public: } } while(circ != circ_end); + if(this->debug_input_faces()) { + std::stringstream filename; + filename << "dump-input-face-" << polygon_contraint_id << "_polygon_" << borders.size() - 1 << ".polylines.txt"; + std::ofstream os(filename.str()); + os.precision(17); + os << vertex_handles.size() + 1 << " "; + for(auto v : vertex_handles) { + os << this->point(v) << " "; + } + os << this->point(*first_it) << std::endl; + } + if(face_index < 0) { const auto accumulated_normal = std::invoke([&] { const auto last_it = std::next(first_it, size - 1); @@ -3231,11 +3243,11 @@ private: 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 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()) { - 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)); + 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)); + } } CGAL_assertion(is_3d || !cdt_2.is_constrained(edge)); fh->info().is_edge_also_in_3d_triangulation[unsigned(i)] = is_3d; From 89393e1b7cef5561fd4ecd44f36e1f0d28bab662 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 3 Oct 2025 10:54:40 +0200 Subject: [PATCH 06/51] improve the minifier for errors --- .../cdt_3_from_off.cpp | 128 +++++++++++------- 1 file changed, 79 insertions(+), 49 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 955ee804473..a60d9cf0833 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 @@ -24,19 +24,19 @@ #include -#include #include +#include +#include #include -#include +#include #include +#include +#include #if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L #include #endif -#include -#include - #if CGAL_CDT_3_USE_EPECK #include @@ -85,6 +85,7 @@ Usage: cdt_3_from_off [options] input.off output.off --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-input-faces: debug input faces @@ -122,6 +123,7 @@ struct CDT_options bool merge_facets_old_method = false; bool reject_self_intersections = false; bool repair_mesh = true; + bool read_mesh_with_operator = false; bool debug_input_faces = false; bool debug_missing_regions = false; bool debug_regions = false; @@ -179,6 +181,8 @@ CDT_options::CDT_options(int argc, char* argv[]) { 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; @@ -886,46 +890,54 @@ int bissect_errors(Mesh mesh, CDT_options options) { std::cerr << "number of faces: " << m.number_of_faces() << '\n'; if(m.number_of_faces() >= nb_faces) { std::cerr << "ERROR: could not simplify mesh\n"; - std::exit(EXIT_FAILURE); + return false; } + return true; }; - simplify(mesh); - std::ofstream current("current_mesh.off"); - current.precision(17); - current << mesh; - current.close(); + if(simplify(mesh)) { + std::ofstream current("current_mesh.off"); + current.precision(17); + current << mesh; + current.close(); - try { - auto code = go(mesh, options); - if(code != EXIT_SUCCESS) { - exit_code = code; - } - } catch(CGAL::Failure_exception& e) { - std::cerr << "CAUGHT EXCEPTION: " << e.what() << '\n'; - if(std::string(e.what()).find(options.failure_assertion_expression) != std::string::npos) - { - exit_code = EXIT_FAILURE; - std::cerr << "BAD MESH! " << mesh.number_of_faces() << " faces\n"; - std::ofstream bad("bad_mesh.off"); - bad.precision(17); - bad << mesh; - bad_mesh = mesh; - bucket = 0; - continue; - } else { - exit_code = EXIT_FAILURE; - std::cerr << "ERROR MESH: " << e.what() << '\n'; - std::ofstream error("error_mesh.off"); - error.precision(17); - error << mesh; - std::cerr << "go on...\n"; + try { + auto code = go(mesh, options); + if(code != EXIT_SUCCESS) { + exit_code = code; + } + } catch(std::exception& e) { + std::cerr << "CAUGHT EXCEPTION: " << e.what() << '\n'; + if(std::string(e.what()).find(options.failure_assertion_expression) != std::string::npos) { + exit_code = EXIT_FAILURE; + std::cerr << "BAD MESH! " << mesh.number_of_faces() << " faces\n"; + std::ofstream bad("bad_mesh.off"); + bad.precision(17); + bad << mesh; + bad_mesh = mesh; + bucket = 0; + continue; + } else { + exit_code = EXIT_FAILURE; + std::cerr << "ERROR MESH: " << e.what() << '\n'; + std::ofstream error("error_mesh.off"); + error.precision(17); + error << mesh; + std::cerr << "go on...\n"; + } } + std::cerr << "GOOD MESH :-( " << mesh.number_of_faces() << " faces\n"; } - std::cerr << "GOOD MESH :-( " << mesh.number_of_faces() << " faces\n"; mesh = bad_mesh; ++bucket; } + if(bad_mesh.number_of_faces() < orig_mesh.number_of_faces()) { + std::cerr << "FINAL BAD MESH: " << bad_mesh.number_of_faces() << " faces\n"; + std::ofstream final_bad("final_bad_mesh.off"); + final_bad.precision(17); + final_bad << bad_mesh; + return go(bad_mesh, options); + } return exit_code; } @@ -945,14 +957,30 @@ int main(int argc, char* argv[]) { CGAL_CDT_3_TASK_BEGIN(read_input_task_handle); auto start_time = std::chrono::high_resolution_clock::now(); - auto read_options = CGAL::parameters::repair_polygon_soup(options.repair_mesh).verbose(options.verbose_level); - auto result = CGAL::read_polygon_mesh_for_cdt_3(options.input_filename, read_options); + CGAL::CDT_3_read_polygon_mesh_output result; + if(options.read_mesh_with_operator) { + std::ifstream in(options.input_filename); + if(!in) { + std::cerr << "Cannot open file: " << options.input_filename << std::endl; + return EXIT_FAILURE; + } + Mesh mesh; + in >> mesh; + if(!in) { + std::cerr << "Error reading mesh with operator>>" << std::endl; + return EXIT_FAILURE; + } + result.polygon_mesh = std::move(mesh); + } else { + auto read_options = CGAL::parameters::repair_polygon_soup(options.repair_mesh).verbose(options.verbose_level); + result = CGAL::read_polygon_mesh_for_cdt_3(options.input_filename, read_options); + } if (!result.polygon_mesh) { std::cerr << "Not a valid input file." << std::endl; std::cerr << "Details:\n" << result.polygon_mesh.error() << std::endl; - return 1; + return EXIT_FAILURE; } Mesh mesh = std::move(*result.polygon_mesh); if(!options.quiet) { @@ -962,16 +990,18 @@ int main(int argc, char* argv[]) { std::cout << "Number of edges: " << mesh.number_of_edges() << '\n'; std::cout << "Number of faces: " << mesh.number_of_faces() << "\n\n"; - std::cout << "Processing was successful.\n"; - std::cout << " Number of duplicated points: " << result.nb_of_duplicated_points << '\n'; - std::cout << " Number of simplified polygons: " << result.nb_of_simplified_polygons << '\n'; - std::cout << " Number of new polygons: " << result.nb_of_new_polygons << '\n'; - std::cout << " Number of removed invalid polygons: " << result.nb_of_removed_invalid_polygons << '\n'; - std::cout << " Number of removed duplicated polygons: " << result.nb_of_removed_duplicated_polygons << '\n'; - std::cout << " Number of removed isolated points: " << result.nb_of_removed_isolated_points << '\n'; - std::cout << " Polygon soup self-intersects: " << (result.polygon_soup_self_intersects ? "YES" : "no") << '\n'; - std::cout << " Polygon mesh is manifold: " << (result.polygon_mesh_is_manifold ? "yes" : "NO") << '\n'; - std::cout << std::endl; + if(!options.read_mesh_with_operator) { + std::cout << "Processing was successful.\n"; + std::cout << " Number of duplicated points: " << result.nb_of_duplicated_points << '\n'; + std::cout << " Number of simplified polygons: " << result.nb_of_simplified_polygons << '\n'; + std::cout << " Number of new polygons: " << result.nb_of_new_polygons << '\n'; + std::cout << " Number of removed invalid polygons: " << result.nb_of_removed_invalid_polygons << '\n'; + std::cout << " Number of removed duplicated polygons: " << result.nb_of_removed_duplicated_polygons << '\n'; + std::cout << " Number of removed isolated points: " << result.nb_of_removed_isolated_points << '\n'; + std::cout << " Polygon soup self-intersects: " << (result.polygon_soup_self_intersects ? "YES" : "no") << '\n'; + std::cout << " Polygon mesh is manifold: " << (result.polygon_mesh_is_manifold ? "yes" : "NO") << '\n'; + std::cout << std::endl; + } } CGAL_CDT_3_TASK_END(read_input_task_handle); From c26c013b5a8f8ee608c6e68a9fc36832378acd6c Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 8 Oct 2025 15:59:00 +0200 Subject: [PATCH 07/51] reorganize some of the debugging code --- ...ing_constrained_Delaunay_triangulation_3.h | 148 ++++++++---------- 1 file changed, 68 insertions(+), 80 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 f2bfe4dcbcd..50ff93d7356 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 @@ -1914,8 +1914,8 @@ private: }; template - int does_edge_intersect_region(Cell_handle cell, int index_vc, int index_vd, - const CDT_2& cdt_2, const Fh_region& fh_region) + int does_edge_interior_intersect_region(Cell_handle cell, int index_vc, int index_vd, + const CDT_2& cdt_2, const Fh_region& fh_region) { const auto vc = cell->vertex(index_vd); const auto vd = cell->vertex(index_vc); @@ -1938,7 +1938,7 @@ private: if(opc == CGAL::COPLANAR || opd == CGAL::COPLANAR || opc == opd) { continue; } else { - // otherwise the segment intersects the plane of the triangle + // otherwise the segment interior intersects the plane of the triangle if(CGAL::orientation(pc, pd, t0, t1) != opc && CGAL::orientation(pc, pd, t1, t2) != opc && CGAL::orientation(pc, pd, t2, t0) != opc) @@ -1987,7 +1987,7 @@ private: //write_segment(dump_edges_around, cell_circ->vertex(index_vc), cell_circ->vertex(index_vd)); if(is_marked(cell_circ->vertex(index_vc), Vertex_marker::REGION_BORDER)) continue; if(is_marked(cell_circ->vertex(index_vd), Vertex_marker::REGION_BORDER)) continue; - int cd_intersects_region = does_edge_intersect_region(cell_circ, index_vc, index_vd, cdt_2, fh_region); + int cd_intersects_region = does_edge_interior_intersect_region(cell_circ, index_vc, index_vd, cdt_2, fh_region); if(cd_intersects_region == 1) { return Search_first_intersection_result_type{ Edge{cell_circ, index_vc, index_vd}, border_edge }; } @@ -2044,7 +2044,7 @@ private: std::set> non_intersecting_edges_set; if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { - debug_dump_tetrahedra_intersect_region(face_index, region_index, cdt_2, fh_region); + expensive_debug_dump_tetrahedra_intersect_region(face_index, region_index, cdt_2, fh_region); } detect_edges_and_cells_intersecting_region(face_index, region_index, cdt_2, fh_region, region_border_vertices, @@ -2085,7 +2085,9 @@ private: } } - // use edges of the border facets to unify sets + if(this->debug_regions()) { + std::cerr << "...use edges of the border facets to unify sets\n"; + } for(auto facet: facets_of_border) { auto vertices = tr().vertices(facet); for(int i = 0; i < 3; ++i) { @@ -2098,8 +2100,10 @@ private: } } - // use non-intersecting edges to unify sets, until we have at most 2 sets if(vertices_of_cavity_union_find.number_of_sets() > 2) { + if(this->debug_regions()) { + std::cerr << "...use non-intersecting edges to unify sets, until we have at most 2 sets\n"; + } for(auto c : cr_intersecting_cells) { for(int i = 0; i < 4; ++i) { @@ -2147,10 +2151,15 @@ private: return std::make_pair(Vertex_handle{}, Facet{}); }); 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"; + } // if there are still more than 2 sets, we need to propagate the information // using a DFS on the border facets if(vertices_of_cavity_union_find.number_of_sets() > 2) { + if(this->debug_regions()) { + std::cerr << "...propagate the information using a DFS on the border facets\n"; + } const auto border_edges_set = std::invoke([&] { using Ordered_pair = std::pair; using Hash = boost::hash; @@ -2527,8 +2536,12 @@ private: // std::ofstream dump(std::string("dump_no_segment_found_") + std::to_string(face_index) + "_" + // std::to_string(region_index) + ".binary.cgal"); // CGAL::IO::save_binary_file(dump, *this); - dump_region(face_index, region_index, cdt_2); // } + if(this->debug_regions()) { + std::cerr << "ERROR: No first segment found intersecting region " << region_index + << " of face #" << face_index << "\n"; + dump_region(face_index, region_index, cdt_2); + } throw Next_region{"No segment found", fh_region[0]}; } CGAL_assertion(found_edge_opt != std::nullopt); @@ -2571,7 +2584,7 @@ private: }; if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { - std::cerr << cdt_3_format("Cavity has {} cells and {} edges, " + std::cerr << cdt_3_format("Cavity has {} piercing cells and {} piercing edges, " "{} vertices in upper cavity and {} in lower, " "{} facets in upper cavity and {} in lower\n", original_intersecting_cells.size(), @@ -2580,20 +2593,6 @@ private: original_vertices_of_lower_cavity.size(), original_facets_of_upper_cavity.size(), original_facets_of_lower_cavity.size()); - if(original_intersecting_cells.size() > 3 || intersecting_edges.size() > 1) { - std::cerr << "!! Interesting case !!\n"; - // dump_region(face_index, region_index, cdt_2); - // { - // std::ofstream out(std::string("dump_intersecting_edges_") + std::to_string(face_index) + "_" + - // std::to_string(region_index) + ".polylines.txt"); - // out.precision(17); - // for(auto edge: intersecting_edges) { - // write_segment(out, edge); - // } - // } - // dump_facets_of_cavity(face_index, region_index, "lower", original_facets_of_lower_cavity); - // dump_facets_of_cavity(face_index, region_index, "upper", original_facets_of_upper_cavity); - } } auto register_internal_constrained_facet = [this](Facet f) { this->register_facet_to_be_constrained(f); }; @@ -3234,9 +3233,10 @@ private: bool restore_face(CDT_3_signed_index face_index) { CDT_2& non_const_cdt_2 = face_cdt_2[face_index]; const CDT_2& cdt_2 = non_const_cdt_2; - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_copy_triangulation_into_hole()) { - std::cerr << cdt_3_format("restore_face({}): CDT_2 has {} vertices\n", face_index, cdt_2.number_of_vertices()); - } + if constexpr (cdt_3_can_use_cxx20_format()) + if(this->debug_copy_triangulation_into_hole() || this->debug_verbose_special_cases()) { + std::cerr << cdt_3_format("restore_face({}): CDT_2 has {} vertices\n", face_index, cdt_2.number_of_vertices()); + } for(const auto& edge : cdt_2.finite_edges()) { const auto fh = edge.first; const auto i = edge.second; @@ -3280,7 +3280,7 @@ private: processed_faces.insert(fh_region.begin(), fh_region.end()); auto handle_error_with_region = [&](const char* what, CDT_2_face_handle fh_2d) { - if(this->debug_regions()) { + if(this->debug_regions() || this->debug_verbose_special_cases()) { std::cerr << "NOTE: " << what << " in sub-region " << (region_index - 1) << " of face #F" << face_index << '\n'; } @@ -3490,7 +3490,9 @@ public: i = face_constraint_misses_subfaces_find_next(i); if(i == npos) { std::cerr << "ERROR: No more missing face to restore after a PLC error\n"; - dump_region(e.face_index, e.region_index); + if(this->debug_regions() || this->debug_verbose_special_cases()) { + dump_region(e.face_index, e.region_index); + } throw; } std::cerr << "Next face is face #F " << i << '\n'; @@ -3625,9 +3627,6 @@ public: return visited_edges.emplace(CGAL::make_sorted_pair(v0, v1), does_intersect); }; - // Alias for C++20 extension warning workaround - auto& intersecting_edges_ = intersecting_edges; - intersecting_edges.push_back(first_intersecting_edge); const auto [v0, v1] = tr().vertices(first_intersecting_edge); (void)new_edge(v0, v1, true); @@ -3640,24 +3639,25 @@ public: } auto test_edge = [&](Cell_handle cell, Vertex_handle v0, int index_v0, Vertex_handle v1, int index_v1, - int expected) { - auto value_returned = [this](bool b) { + int expected) + { + auto value_returned = [this, v0, v1](bool b, bool not_visited) { CGAL_USE(this); if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { - std::cerr << cdt_3_format(" return {}\n", b); + std::cerr << cdt_3_format(" test_edge {} {} return {} {}\n", + IO::oformat(v0, with_point_and_info), + IO::oformat(v1, with_point_and_info), + b, + not_visited ? "(new)" : "(cached)"); } return b; }; - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { - std::cerr << cdt_3_format(" test_edge {} {} ", IO::oformat(v0, with_point_and_info), - IO::oformat(v1, with_point_and_info)); - } auto [cached_value_it, not_visited] = new_edge(v0, v1, false); - if(!not_visited) return value_returned(cached_value_it->second); - int v0v1_intersects_region = (is_marked(v0, Vertex_marker::REGION_INSIDE) || - is_marked(v1, Vertex_marker::REGION_INSIDE)) - ? expected - : does_edge_intersect_region(cell, index_v0, index_v1, cdt_2, fh_region); + if(!not_visited) return value_returned(cached_value_it->second, not_visited); + int v0v1_intersects_region = + (is_marked(v0, Vertex_marker::REGION_INSIDE) || is_marked(v1, Vertex_marker::REGION_INSIDE)) + ? expected + : does_edge_interior_intersect_region(cell, index_v0, index_v1, cdt_2, fh_region); if(v0v1_intersects_region != 0) { if(this->use_older_cavity_algorithm()) { if(v0v1_intersects_region != expected) { @@ -3669,13 +3669,13 @@ public: if(v0v1_intersects_region < 0) { std::swap(index_v0, index_v1); } - intersecting_edges_.emplace_back(cell, index_v0, index_v1); + intersecting_edges.emplace_back(cell, index_v0, index_v1); cached_value_it->second = true; - return value_returned(true); + return value_returned(true, not_visited); } else { non_intersecting_edges_set.insert(make_sorted_pair(v0, v1)); cached_value_it->second = false; - return value_returned(false); + return value_returned(false, not_visited); } }; @@ -3741,10 +3741,10 @@ public: { intersecting_cells.insert(n_ch); if(this->debug_regions()) { - std::cerr << "tetrahedron #" << n_ch->time_stamp() << " intersects the region\n"; + std::cerr << "new tetrahedron #" << n_ch->time_stamp() << " intersects the region\n"; } } else if(this->debug_regions()) { - std::cerr << "NO, tetrahedron #" << n_ch->time_stamp() << " does not intersect the region\n"; + std::cerr << "NO, new tetrahedron #" << n_ch->time_stamp() << " does not intersect the region\n"; } for(int i = 0; i < 4; ++i) { for(int j = i + 1; j < 4; ++j) { @@ -3820,12 +3820,13 @@ public: { using EK = CGAL::Exact_predicates_exact_constructions_kernel; const auto to_exact = CGAL::Cartesian_converter(); + const auto& cdt_2 = this->face_cdt_2[face_index]; std::cerr << cdt_3_format("restore_subface_region face index: {}, region #{}, intersecting edge #{}: ({} {})\n", face_index, region_index, edge_index, IO::oformat(v_above, with_point_and_info), IO::oformat(v_below, with_point_and_info)); - dump_region(face_index, region_index, this->face_cdt_2[face_index]); + dump_region(face_index, region_index, cdt_2); const auto p_above = this->point(v_above); const auto p_below = this->point(v_below); @@ -3851,30 +3852,17 @@ public: intersect_out.close(); auto cells_around_intersecting_edge = Container_from_circulator{this->incident_cells(intersecting_edge)}; - for(const auto& cell: cells_around_intersecting_edge) { - CGAL_assertion(!cell.has_vertex(tr().infinite_vertex())); - auto tetrahedron = - typename Geom_traits::Tetrahedron_3{tr().point(cell.vertex(0)), tr().point(cell.vertex(1)), - tr().point(cell.vertex(2)), tr().point(cell.vertex(3))}; - 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 triangle = typename Geom_traits::Triangle_3{tr().point(v0), tr().point(v1), tr().point(v2)}; - auto exact_triangle = to_exact(triangle); - } - + for(const auto& ch: make_prevent_deref_range(cells_around_intersecting_edge)) { + CGAL_assertion(!ch->has_vertex(tr().infinite_vertex())); + auto tetrahedron = tr().tetrahedron(ch.current_circulator()); std::cerr << cdt_3_format("Test tetrahedron (#{}):\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)); + ch->time_stamp(), + IO::oformat(ch->vertex(0), with_point_and_info), + IO::oformat(ch->vertex(1), with_point_and_info), + IO::oformat(ch->vertex(2), with_point_and_info), + IO::oformat(ch->vertex(3), with_point_and_info)); if(!std::any_of(fh_region.begin(), fh_region.end(), [&](const auto fh) { - 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 triangle = typename Geom_traits::Triangle_3{tr().point(v0), tr().point(v1), tr().point(v2)}; + auto triangle = cdt_2.triangle(fh); bool b = does_tetrahedron_intersect_triangle_interior(tetrahedron, triangle, tr().geom_traits()); if(b) { std::cerr << " intersects the region\n"; @@ -3884,18 +3872,18 @@ public: { std::cerr << cdt_3_format( "ERROR: The following tetrahedron (#{}) does not intersect the region:\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)); + ch->time_stamp(), + IO::oformat(ch->vertex(0), with_point_and_info), IO::oformat(ch->vertex(1), with_point_and_info), + IO::oformat(ch->vertex(2), with_point_and_info), IO::oformat(ch->vertex(3), with_point_and_info)); } } } template - void debug_dump_tetrahedra_intersect_region(CDT_3_signed_index face_index, - int region_index, - const CDT_2& cdt_2, - const Fh_region& fh_region) + void expensive_debug_dump_tetrahedra_intersect_region(CDT_3_signed_index face_index, + int region_index, + const CDT_2& cdt_2, + const Fh_region& fh_region) { using Mesh = Surface_mesh; using Face_index = typename Mesh::Face_index; @@ -3922,7 +3910,7 @@ public: bool intersects = false; for(int i = 0; i < 4; ++i) { for(int j = i + 1; j < 4; ++j) { - int intersects_region = does_edge_intersect_region(ch, i, j, cdt_2, fh_region); + int intersects_region = does_edge_interior_intersect_region(ch, i, j, cdt_2, fh_region); if(intersects_region != 0) { intersects = true; } From 71c2425b6ea7c495bb26ea07dbb17c0cf0180f58 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Oct 2025 10:33:27 +0200 Subject: [PATCH 08/51] more debug --- ...onforming_constrained_Delaunay_triangulation_3.h | 13 +++++++++++++ 1 file changed, 13 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 50ff93d7356..ef60b53778a 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 @@ -1589,6 +1589,19 @@ public: private: void set_mark(Vertex_handle v, Vertex_marker m) { + if(this->debug_regions()) { + std::cerr << " set_mark(" << this->display_vert(v) << ", "; + switch(m) { + case Vertex_marker::CLEAR: std::cerr << "CLEAR"; break; + case Vertex_marker::REGION_BORDER: std::cerr << "REGION_BORDER"; break; + case Vertex_marker::REGION_INSIDE: std::cerr << "REGION_INSIDE"; break; + 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"; + } v->ccdt_3_data().set_mark(m); } From 626675ea08192ab55169a26867f905dc60df839d Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Oct 2025 10:33:46 +0200 Subject: [PATCH 09/51] use the traits class --- ...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 ef60b53778a..9ae26fb1334 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 @@ -1930,11 +1930,11 @@ private: int does_edge_interior_intersect_region(Cell_handle cell, int index_vc, int index_vd, const CDT_2& cdt_2, const Fh_region& fh_region) { + auto orientation = tr().geom_traits().orientation_3_object(); const auto vc = cell->vertex(index_vd); const auto vd = cell->vertex(index_vc); const auto pc = this->point(vc); const auto pd = this->point(vd); - const typename Geom_traits::Segment_3 seg{pc, pd}; 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; @@ -1946,15 +1946,15 @@ private: const auto t1 = cdt_2.point(fh_2d->vertex(1)); const auto t2 = cdt_2.point(fh_2d->vertex(2)); - const auto opc = CGAL::orientation(t0, t1, t2, pc); - const auto opd = CGAL::orientation(t0, t1, t2, pd); + const auto opc = orientation(t0, t1, t2, pc); + const auto opd = orientation(t0, t1, t2, pd); if(opc == CGAL::COPLANAR || opd == CGAL::COPLANAR || opc == opd) { continue; } else { // otherwise the segment interior intersects the plane of the triangle - if(CGAL::orientation(pc, pd, t0, t1) != opc && - CGAL::orientation(pc, pd, t1, t2) != opc && - CGAL::orientation(pc, pd, t2, t0) != opc) + if(orientation(pc, pd, t0, t1) != opc && + orientation(pc, pd, t1, t2) != opc && + orientation(pc, pd, t2, t0) != opc) { return static_cast(opc); } From 4d3d2f4f030a374781b533ece7a6f210bd2060c3 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Oct 2025 10:35:38 +0200 Subject: [PATCH 10/51] simplify the code --- .../Conforming_constrained_Delaunay_triangulation_3.h | 8 ++------ 1 file changed, 2 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 9ae26fb1334..dc224c7e129 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 @@ -3908,13 +3908,9 @@ public: auto [color_vpmap, _] = tets_intersect_region_mesh.template add_property_map("f:patch_id"); for(auto ch : tr().finite_cell_handles()) { - auto tetrahedron = typename Geom_traits::Tetrahedron_3{tr().point(ch->vertex(0)), tr().point(ch->vertex(1)), - tr().point(ch->vertex(2)), tr().point(ch->vertex(3))}; + auto tetrahedron = tr().tetrahedron(ch); if(!std::any_of(fh_region.begin(), fh_region.end(), [&](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 triangle = typename Geom_traits::Triangle_3{tr().point(v0), tr().point(v1), tr().point(v2)}; + const auto triangle = cdt_2.triangle(fh); return does_tetrahedron_intersect_triangle_interior(tetrahedron, triangle, tr().geom_traits()); })) { From 21df7dad8658f36acf6f813d2dc750e046f3f5da Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 10 Oct 2025 10:36:10 +0200 Subject: [PATCH 11/51] fix a bug fixes Thingi test cases 200695 and 822697 --- ...onforming_constrained_Delaunay_triangulation_vertex_data_3.h | 2 ++ 1 file changed, 2 insertions(+) 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 bf4a493f778..21336b05806 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 @@ -91,6 +91,8 @@ public: } int number_of_incident_constraints() const { + if(vertex_type() == CDT_3_vertex_type::STEINER_IN_FACE) + return 0; CGAL_assertion(u.on_edge.nb_of_incident_constraints >= 0); return u.on_edge.nb_of_incident_constraints; } From 7d9dbdafcdf54d45e56392edc18fd04159277b95 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 13 Oct 2025 17:12:09 +0200 Subject: [PATCH 12/51] WIP: copy-paste to merge the two implementation TODO: extract a common function --- ...ing_constrained_Delaunay_triangulation_3.h | 24 +++---- .../cdt_3_from_off.cpp | 66 +++++++++---------- 2 files changed, 38 insertions(+), 52 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 dc224c7e129..4118f77b223 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 @@ -684,7 +684,7 @@ public: using Vertex_handle = typename Triangulation::Vertex_handle; using Cell_handle = typename Triangulation::Cell_handle; - auto tr_vertex_map = get(CGAL::dynamic_vertex_property_t(), mesh); + auto tr_vertex_pmap = get(CGAL::dynamic_vertex_property_t(), mesh); Cell_handle hint_ch{}; for(auto v : vertices(mesh)) { if constexpr(has_plc_face_id) { @@ -694,16 +694,11 @@ public: auto vh = cdt_impl.insert(p, hint_ch, false); vh->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::CORNER); hint_ch = vh->cell(); - put(tr_vertex_map, v, vh); + put(tr_vertex_pmap, v, vh); } cdt_impl.add_bbox_points_if_not_dimension_3(); - struct { - decltype(tr_vertex_map)* vertex_map; - auto operator()(vertex_descriptor v) const { return get(*vertex_map, v); } - } tr_vertex_fct{&tr_vertex_map}; - if constexpr(has_plc_face_id) { for(int i = 0; i < number_of_patches; ++i) { auto& edges = patch_edges[i]; @@ -725,7 +720,8 @@ public: } polylines.erase(non_closed_polylines_begin, polylines.end()); auto other_polylines = segment_soup_to_polylines(edges); - polylines.insert(polylines.end(), std::make_move_iterator(other_polylines.begin()), + polylines.insert(polylines.end(), + std::make_move_iterator(other_polylines.begin()), std::make_move_iterator(other_polylines.end())); } @@ -733,20 +729,16 @@ public: for(auto& polyline : polylines) { CGAL_assertion(polyline.front() == polyline.back()); polyline.pop_back(); - auto begin = boost::make_transform_iterator(polyline.begin(), tr_vertex_fct); - auto end = boost::make_transform_iterator(polyline.end(), tr_vertex_fct); - face_index = cdt_impl.insert_constrained_face(CGAL::make_range(begin, end), false, + auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, tr_vertex_pmap); + face_index = cdt_impl.insert_constrained_face(range_of_vertices, false, face_index ? *face_index : -1); } } } else { for(auto f : faces(mesh)) { auto face_vertices = vertices_around_face(halfedge(f, mesh), mesh); - - auto begin = boost::make_transform_iterator(face_vertices.begin(), tr_vertex_fct); - auto end = boost::make_transform_iterator(face_vertices.end(), tr_vertex_fct); - - cdt_impl.insert_constrained_face(CGAL::make_range(begin, end), false); + auto range_of_vertices = CGAL::make_transform_range_from_property_map(face_vertices, tr_vertex_pmap); + cdt_impl.insert_constrained_face(range_of_vertices, false); } } cdt_impl.restore_Delaunay(); 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 a60d9cf0833..51d051d5da4 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 @@ -331,7 +331,7 @@ int go(Mesh mesh, CDT_options options) { << "Epsilon * Bbox width : " << epsilon * bbox_max_width << "\n\n"; } - auto pmap = get(CGAL::vertex_point, mesh); + auto mesh_vp_map = get(CGAL::vertex_point, mesh); auto [patch_id_map, patch_id_map_ok] = mesh.add_property_map("f:patch_id", -2); assert(patch_id_map_ok); CGAL_USE(patch_id_map_ok); @@ -341,7 +341,7 @@ int go(Mesh mesh, CDT_options options) { mesh.add_property_map("e:is_border_of_patch", false); assert(edge_is_border_of_patch_map_ok); CGAL_USE(edge_is_border_of_patch_map_ok); - int nb_patches = 0; + int number_of_patches = 0; std::vector>> patch_edges; if(options.merge_facets) { CGAL_CDT_3_TASK_BEGIN(merge_facets_task_handle); @@ -357,17 +357,17 @@ int go(Mesh mesh, CDT_options options) { auto f = f_stack.top(); f_stack.pop(); if(get(patch_id_map, f) >= 0) continue; - put(patch_id_map, f, nb_patches); + put(patch_id_map, f, number_of_patches); for(auto h: CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) { auto opp = opposite(h, mesh); if(is_border_edge(opp, mesh)) { continue; } auto n = face(opp, mesh); - auto a = get(pmap, source(h, mesh)); - auto b = get(pmap, target(h, mesh)); - auto c = get(pmap, target(next(h, mesh), mesh)); - auto d = get(pmap, target(next(opp, mesh), mesh)); + auto a = get(mesh_vp_map, source(h, mesh)); + auto b = get(mesh_vp_map, target(h, mesh)); + auto c = get(mesh_vp_map, target(next(h, mesh), mesh)); + auto d = get(mesh_vp_map, target(next(opp, mesh), mesh)); if(CGAL::orientation(a, b, c, d) != CGAL::COPLANAR) { continue; } @@ -375,21 +375,21 @@ int go(Mesh mesh, CDT_options options) { f_stack.push(n); } } - ++nb_patches; + ++number_of_patches; } } else { namespace np = CGAL::parameters; - nb_patches = CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces( + number_of_patches = CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces( mesh, patch_id_map, np::maximum_distance(options.coplanar_polygon_max_distance * bbox_max_width) .maximum_angle(options.coplanar_polygon_max_angle)); for(auto f: faces(mesh)) { if(get(patch_id_map, f) < 0) { - std::cerr << "warning: face " << f << " has no patch id! Reassign it to " << nb_patches << '\n'; + std::cerr << "warning: face " << f << " has no patch id! Reassign it to " << number_of_patches << '\n'; for(auto h: CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) { std::cerr << " " << target(h, mesh) << ", point " << mesh.point(target(h, mesh)) << '\n'; } - put(patch_id_map, f, nb_patches++); + put(patch_id_map, f, number_of_patches++); } } if(!options.dump_surface_mesh_after_merge_filename.empty()) { @@ -397,13 +397,13 @@ int go(Mesh mesh, CDT_options options) { assert(corner_id_map_ok); CGAL_USE(corner_id_map_ok); const auto nb_corners = CGAL::Polygon_mesh_processing::detect_corners_of_regions( - mesh, patch_id_map, nb_patches, corner_id_map, + mesh, patch_id_map, number_of_patches, corner_id_map, np::maximum_distance(options.coplanar_polygon_max_distance * bbox_max_width) .maximum_angle(options.coplanar_polygon_max_angle) .edge_is_constrained_map(edge_is_border_of_patch_map)); Mesh merged_mesh; CGAL::Polygon_mesh_processing::remesh_almost_planar_patches( - mesh, merged_mesh, nb_patches, nb_corners, patch_id_map, corner_id_map, edge_is_border_of_patch_map, + mesh, merged_mesh, number_of_patches, nb_corners, patch_id_map, corner_id_map, edge_is_border_of_patch_map, CGAL::parameters::default_values(), CGAL::parameters::do_not_triangulate_faces(true)); mesh.remove_property_map(corner_id_map); @@ -413,10 +413,10 @@ int go(Mesh mesh, CDT_options options) { } } if (!options.quiet) { - std::cout << "[timings] detected " << nb_patches << " patches in " << std::chrono::duration_cast( + std::cout << "[timings] detected " << number_of_patches << " patches in " << std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; } - patch_edges.resize(nb_patches); + patch_edges.resize(number_of_patches); for(auto h: halfedges(mesh)) { if(is_border(h, mesh)) continue; @@ -442,7 +442,7 @@ int go(Mesh mesh, CDT_options options) { if(!options.dump_patches_borders_prefix.empty()) { CGAL_CDT_3_TASK_BEGIN(output_task_handle); std::set> all_edges; - for(int i = 0; i < nb_patches; ++i) { + for(int i = 0; i < number_of_patches; ++i) { std::stringstream ss; ss << options.dump_patches_borders_prefix << i << ".polylines.txt"; std::ofstream out(ss.str()); @@ -456,7 +456,7 @@ int go(Mesh mesh, CDT_options options) { for(const auto& polyline: polylines) { out << polyline.size() << " "; for(auto v: polyline) { - out << get(pmap, v) << " "; + out << get(mesh_vp_map, v) << " "; } out << '\n'; } @@ -475,7 +475,7 @@ int go(Mesh mesh, CDT_options options) { for(const auto& polyline: polylines) { out << polyline.size() << " "; for(auto v: polyline) { - out << get(pmap, v) << " "; + out << get(mesh_vp_map, v) << " "; } out << '\n'; } @@ -580,7 +580,7 @@ int go(Mesh mesh, CDT_options options) { CDT::Cell_handle hint{}; for(auto v: vertices(mesh)) { if(options.merge_facets && false == get(v_selected_map, v)) continue; - auto vh = cdt.insert(get(pmap, v), hint, false); + auto vh = cdt.insert(get(mesh_vp_map, v), hint, false); hint = vh->cell(); put(tr_vertex_pmap, v, vh); } @@ -642,7 +642,7 @@ int go(Mesh mesh, CDT_options options) { double min_distance = (std::numeric_limits::max)(); CDT::Vertex_handle min_va, min_vb, min_vertex; if(options.merge_facets) { - for(int i = 0; i < nb_patches; ++i) { + for(int i = 0; i < number_of_patches; ++i) { const auto& edges = patch_edges[i]; for(auto [vda, vdb]: edges) { auto va = get(tr_vertex_pmap, vda); @@ -696,8 +696,10 @@ int go(Mesh mesh, CDT_options options) { CDT_3_try { start_time = std::chrono::high_resolution_clock::now(); if(options.merge_facets) { - for(int i = 0; i < nb_patches; ++i) { + for(int i = 0; i < number_of_patches; ++i) { auto& edges = patch_edges[i]; + if(edges.empty()) + continue; auto polylines = segment_soup_to_polylines(edges); while(true) { const auto non_closed_polylines_begin = @@ -720,20 +722,12 @@ int go(Mesh mesh, CDT_options options) { } std::optional face_index; - for(auto& polyline: polylines) { - assert(polyline.front() == polyline.back()); + for(auto& polyline : polylines) { + CGAL_assertion(polyline.front() == polyline.back()); polyline.pop_back(); -#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L - face_index = cdt.insert_constrained_face( - polyline | std::views::transform([&](vertex_descriptor v) { return get(tr_vertex_pmap, v); }), - false, - face_index ? *face_index : -1); -#else - face_index = cdt.insert_constrained_face( CGAL::Iterator_range(CGAL::make_transform_iterator_from_property_map(polyline.begin(), tr_vertex_pmap), - CGAL::make_transform_iterator_from_property_map(polyline.end(), tr_vertex_pmap)), - false, - face_index ? *face_index : -1); -#endif + auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, tr_vertex_pmap); + face_index = cdt.insert_constrained_face(range_of_vertices, false, + face_index ? *face_index : -1); } } } else { @@ -741,7 +735,7 @@ int go(Mesh mesh, CDT_options options) { std::vector polygon; const auto he = halfedge(face_descriptor, mesh); for(auto vertex_it : CGAL::vertices_around_face(he, mesh)) { - polygon.push_back(get(pmap, vertex_it)); + polygon.push_back(get(mesh_vp_map, vertex_it)); } if(cdt.debug_polygon_insertion()) { std::cerr << "NEW POLYGON #" << poly_id << '\n'; @@ -791,7 +785,7 @@ int go(Mesh mesh, CDT_options options) { CGAL_assertion(it != end); auto he = CGAL::Euler::split_edge(*it, mesh); auto mesh_v = target(he, mesh); - put(pmap, mesh_v, v->point()); + put(mesh_vp_map, mesh_v, v->point()); assert(mesh_v == Vertex_index{static_cast(time_stamp - 1)}); } // for(auto e: edges(mesh)) { From b11e42c4a71ae094a43c1aceb218b5fa676c7c9e Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 13 Oct 2025 17:12:26 +0200 Subject: [PATCH 13/51] improve the assertion --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 2 +- .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 2 +- 2 files changed, 2 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 4118f77b223..4e576dcccfd 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 @@ -727,7 +727,7 @@ public: std::optional face_index; for(auto& polyline : polylines) { - CGAL_assertion(polyline.front() == polyline.back()); + CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); polyline.pop_back(); auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, tr_vertex_pmap); face_index = cdt_impl.insert_constrained_face(range_of_vertices, 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 51d051d5da4..526d14ea0a7 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 @@ -723,7 +723,7 @@ int go(Mesh mesh, CDT_options options) { std::optional face_index; for(auto& polyline : polylines) { - CGAL_assertion(polyline.front() == polyline.back()); + CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); polyline.pop_back(); auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, tr_vertex_pmap); face_index = cdt.insert_constrained_face(range_of_vertices, false, From 75c2ac5a68633c3a981833e7899d22fd6d775459 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 13 Oct 2025 17:18:56 +0200 Subject: [PATCH 14/51] bug-fix: use longest border polyline to compute the normal fix bug of Thingi 1439534 --- ...ing_constrained_Delaunay_triangulation_3.h | 23 +++++++++++++++++++ .../cdt_3_from_off.cpp | 23 +++++++++++++++++++ 2 files changed, 46 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 4e576dcccfd..ecce1d7c23c 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 @@ -725,6 +725,29 @@ public: std::make_move_iterator(other_polylines.end())); } + if(polylines.size() > 1) { + double max_sq_length = 0; + auto longest_it = polylines.begin(); + for(auto it = polylines.begin(); it != polylines.end(); ++it) { + auto& polyline = *it; + using CGAL::Bbox_3; + Bbox_3 bb; + for(auto v : polyline) { + bb = bb + Bbox_3(get(mesh_vp_map, v).bbox()); + } + double sq_diagonal_length = CGAL::square(bb.xmax() - bb.xmin()) + + CGAL::square(bb.ymax() - bb.ymin()) + + CGAL::square(bb.zmax() - bb.zmin()); + if(sq_diagonal_length > max_sq_length) { + max_sq_length = sq_diagonal_length; + longest_it = it; + } + } + if(longest_it != polylines.begin()) { + std::iter_swap(longest_it, polylines.begin()); + } + } + std::optional face_index; for(auto& polyline : polylines) { CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); 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 526d14ea0a7..2acb2888c37 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 @@ -721,6 +721,29 @@ int go(Mesh mesh, CDT_options options) { std::make_move_iterator(other_polylines.end())); } + if(polylines.size() > 1) { + double max_sq_length = 0; + auto longest_it = polylines.begin(); + for(auto it = polylines.begin(); it != polylines.end(); ++it) { + auto& polyline = *it; + using CGAL::Bbox_3; + Bbox_3 bb; + for(auto v : polyline) { + bb = bb + Bbox_3(get(mesh_vp_map, v).bbox()); + } + double sq_diagonal_length = CGAL::square(bb.xmax() - bb.xmin()) + + CGAL::square(bb.ymax() - bb.ymin()) + + CGAL::square(bb.zmax() - bb.zmin()); + if(sq_diagonal_length > max_sq_length) { + max_sq_length = sq_diagonal_length; + longest_it = it; + } + } + if(longest_it != polylines.begin()) { + std::iter_swap(longest_it, polylines.begin()); + } + } + std::optional face_index; for(auto& polyline : polylines) { CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); From bac2c060269bc215d3113c4e862e5f0ffbb1321c Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 14 Oct 2025 23:50:17 +0200 Subject: [PATCH 15/51] refactoring of cdt_3_from_off.cpp --- .../cdt_3_from_off.cpp | 1003 ++++++++++------- 1 file changed, 577 insertions(+), 426 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 2acb2888c37..ada8928f3e1 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 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include #if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L @@ -111,6 +113,7 @@ Usage: cdt_3_from_off [options] input.off output.off [[noreturn]] void error(std::string_view message, std::string_view extra = "") { std::cerr << "Error: " << message << extra << '\n'; + help(std::cerr); std::exit(EXIT_FAILURE); } @@ -296,8 +299,7 @@ CDT_options::CDT_options(int argc, char* argv[]) { # define CGAL_CDT_3_TASK_END(task_handle) #endif // no ITT -int go(Mesh mesh, CDT_options options) { - CDT cdt; +void configure_cdt_debug_options(CDT& cdt, const CDT_options& options) { cdt.debug_Steiner_points(options.verbose_level > 0); cdt.debug_input_faces(options.debug_input_faces); cdt.debug_missing_region(options.verbose_level > 1 || options.debug_missing_regions); @@ -315,7 +317,9 @@ int go(Mesh mesh, CDT_options options) { cdt.use_older_cavity_algorithm(!options.use_new_cavity_algorithm); cdt.use_finite_edges_map(options.use_finite_edges_map); cdt.set_segment_vertex_epsilon(options.segment_vertex_epsilon); +} +auto compute_bounding_box(const Mesh& mesh, const CDT_options& options) { const auto bbox = CGAL::Polygon_mesh_processing::bbox(mesh); double d_x = bbox.xmax() - bbox.xmin(); double d_y = bbox.ymax() - bbox.ymin(); @@ -323,168 +327,18 @@ int go(Mesh mesh, CDT_options options) { const double bbox_max_width = (std::max)(d_x, (std::max)(d_y, d_z)); - double epsilon = options.vertex_vertex_epsilon; - if(!options.quiet) { + double epsilon = options.vertex_vertex_epsilon; std::cout << "Bbox width : " << bbox_max_width << '\n' << "Epsilon : " << epsilon << '\n' << "Epsilon * Bbox width : " << epsilon * bbox_max_width << "\n\n"; } - auto mesh_vp_map = get(CGAL::vertex_point, mesh); + return bbox; +} - auto [patch_id_map, patch_id_map_ok] = mesh.add_property_map("f:patch_id", -2); - assert(patch_id_map_ok); CGAL_USE(patch_id_map_ok); - auto [v_selected_map, v_selected_map_ok] = mesh.add_property_map("v:selected", false); - assert(v_selected_map_ok); CGAL_USE(v_selected_map_ok); - auto [edge_is_border_of_patch_map, edge_is_border_of_patch_map_ok] = - mesh.add_property_map("e:is_border_of_patch", false); - assert(edge_is_border_of_patch_map_ok); - CGAL_USE(edge_is_border_of_patch_map_ok); - int number_of_patches = 0; - std::vector>> patch_edges; - if(options.merge_facets) { - CGAL_CDT_3_TASK_BEGIN(merge_facets_task_handle); - auto start_time = std::chrono::high_resolution_clock::now(); - - if(options.merge_facets_old_method) { - for(auto f: faces(mesh)) - { - if(get(patch_id_map, f) >= 0) continue; - std::stack f_stack; - f_stack.push(f); - while(!f_stack.empty()) { - auto f = f_stack.top(); - f_stack.pop(); - if(get(patch_id_map, f) >= 0) continue; - put(patch_id_map, f, number_of_patches); - for(auto h: CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) { - auto opp = opposite(h, mesh); - if(is_border_edge(opp, mesh)) { - continue; - } - auto n = face(opp, mesh); - auto a = get(mesh_vp_map, source(h, mesh)); - auto b = get(mesh_vp_map, target(h, mesh)); - auto c = get(mesh_vp_map, target(next(h, mesh), mesh)); - auto d = get(mesh_vp_map, target(next(opp, mesh), mesh)); - if(CGAL::orientation(a, b, c, d) != CGAL::COPLANAR) { - continue; - } - if(get(patch_id_map, n) >= 0) continue; - f_stack.push(n); - } - } - ++number_of_patches; - } - } else { - namespace np = CGAL::parameters; - number_of_patches = CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces( - mesh, patch_id_map, - np::maximum_distance(options.coplanar_polygon_max_distance * bbox_max_width) - .maximum_angle(options.coplanar_polygon_max_angle)); - for(auto f: faces(mesh)) { - if(get(patch_id_map, f) < 0) { - std::cerr << "warning: face " << f << " has no patch id! Reassign it to " << number_of_patches << '\n'; - for(auto h: CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) { - std::cerr << " " << target(h, mesh) << ", point " << mesh.point(target(h, mesh)) << '\n'; - } - put(patch_id_map, f, number_of_patches++); - } - } - if(!options.dump_surface_mesh_after_merge_filename.empty()) { - auto [corner_id_map, corner_id_map_ok] = mesh.add_property_map("v:corner_id", -1); - assert(corner_id_map_ok); - CGAL_USE(corner_id_map_ok); - const auto nb_corners = CGAL::Polygon_mesh_processing::detect_corners_of_regions( - mesh, patch_id_map, number_of_patches, corner_id_map, - np::maximum_distance(options.coplanar_polygon_max_distance * bbox_max_width) - .maximum_angle(options.coplanar_polygon_max_angle) - .edge_is_constrained_map(edge_is_border_of_patch_map)); - Mesh merged_mesh; - CGAL::Polygon_mesh_processing::remesh_almost_planar_patches( - mesh, merged_mesh, number_of_patches, nb_corners, patch_id_map, corner_id_map, edge_is_border_of_patch_map, - CGAL::parameters::default_values(), - CGAL::parameters::do_not_triangulate_faces(true)); - mesh.remove_property_map(corner_id_map); - std::ofstream out(options.dump_surface_mesh_after_merge_filename); - out.precision(17); - out << merged_mesh; - } - } - if (!options.quiet) { - std::cout << "[timings] detected " << number_of_patches << " patches in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - } - patch_edges.resize(number_of_patches); - for(auto h: halfedges(mesh)) - { - if(is_border(h, mesh)) continue; - auto f = face(h, mesh); - auto patch_id = get(patch_id_map, f); - auto opp = opposite(h, mesh); - if(is_border(opp, mesh) || patch_id != get(patch_id_map, face(opp, mesh))) { - auto va = source(h, mesh); - auto vb = target(h, mesh); - patch_edges[patch_id].emplace_back(va, vb); - put(v_selected_map, va, true); - put(v_selected_map, vb, true); - } - } - CGAL_CDT_3_TASK_END(merge_facets_task_handle); - if(!options.dump_patches_after_merge_filename.empty()) { - CGAL_CDT_3_TASK_BEGIN(output_task_handle); - std::ofstream out(options.dump_patches_after_merge_filename); - CGAL::IO::write_PLY(out, mesh, CGAL::parameters::stream_precision(17)); - CGAL_CDT_3_TASK_END(output_task_handle); - } - } - if(!options.dump_patches_borders_prefix.empty()) { - CGAL_CDT_3_TASK_BEGIN(output_task_handle); - std::set> all_edges; - for(int i = 0; i < number_of_patches; ++i) { - std::stringstream ss; - ss << options.dump_patches_borders_prefix << i << ".polylines.txt"; - std::ofstream out(ss.str()); - out.precision(17); - const auto& edges = patch_edges[i]; - for(auto [va, vb]: edges) { - all_edges.insert(CGAL::make_sorted_pair(va, vb)); - } - std::cerr << "Patch p#" << i << " has " << edges.size() << " edges\n"; - const auto polylines = segment_soup_to_polylines(edges); - for(const auto& polyline: polylines) { - out << polyline.size() << " "; - for(auto v: polyline) { - out << get(mesh_vp_map, v) << " "; - } - out << '\n'; - } - out.close(); - std::cerr << " " << polylines.size() << " polylines\n"; - for(const auto& polyline: polylines) { - std::cerr << " - " << polyline.size() << " vertices\n"; - assert(polyline.front() == polyline.back()); - } - } - std::stringstream ss; - ss << options.dump_patches_borders_prefix << "all_edges.polylines.txt"; - std::ofstream out(ss.str()); - out.precision(17); - const auto polylines = segment_soup_to_polylines(all_edges); - for(const auto& polyline: polylines) { - out << polyline.size() << " "; - for(auto v: polyline) { - out << get(mesh_vp_map, v) << " "; - } - out << '\n'; - } - CGAL_CDT_3_TASK_END(output_task_handle); - } - - int exit_code = EXIT_SUCCESS; - - auto finally = [&cdt, &options]() { +std::function create_output_finalizer(const CDT& cdt, const CDT_options& options) { + return [&cdt, &options]() { CGAL_CDT_3_TASK_BEGIN(output_task_handle); { auto dump_tets_to_medit = [](std::string fname, @@ -571,18 +425,458 @@ int go(Mesh mesh, CDT_options options) { } CGAL_CDT_3_TASK_END(output_task_handle); }; +} - auto [tr_vertex_pmap, tr_vertex_pmap_ok] = mesh.add_property_map("tr_vertex"); +struct Min_distance_result { + double min_distance; + std::array vertices_of_min_edge; +}; + +Min_distance_result compute_minimum_vertex_distance(const CDT& cdt) { +#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L + auto [min_sq_distance, min_edge] = + (std::ranges::min)(cdt.finite_edges() | std::views::transform([&](auto edge) { + return std::make_pair(cdt.segment(edge).squared_length(), edge); + })); +#else + auto transform_fct = [&](auto edge) { return std::make_pair(cdt.segment(edge).squared_length(), edge); }; + auto min_p = transform_fct(*cdt.finite_edges_begin()); + for (auto ite=cdt.finite_edges_begin(); ite!=cdt.finite_edges_end(); ++ite) + { + auto p = transform_fct(*ite); + if (p < min_p) + p = min_p; + } + auto [min_sq_distance, min_edge] = min_p; +#endif + auto min_distance = CGAL::approximate_sqrt(min_sq_distance); + auto vertices_of_min_edge = cdt.vertices(min_edge); + + return {min_distance, vertices_of_min_edge}; +} + +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"; +} + +int validate_minimum_vertex_distances(const CDT& cdt, double vertex_vertex_min_distance, const CDT_options& options) { + auto result = compute_minimum_vertex_distance(cdt); + + if(!options.quiet) { + print_minimum_distance_info(result); + } + if(result.min_distance < vertex_vertex_min_distance) { + std::cerr << "ERROR: min distance between vertices is too small\n"; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +struct Constraint_distance_result { + double min_distance; + CDT::Vertex_handle min_va, min_vb, min_vertex; +}; + +template +Constraint_distance_result compute_constraint_vertex_distances_from_patches_borders( + CDT& cdt, + const BordersOfPatches& patch_edges, + const VertexPointMap& tr_vertex_pmap) { + +#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L + auto edge_results = patch_edges + | std::views::join + | std::views::transform([&](const auto& edge_pair) { + auto [vda, vdb] = edge_pair; + auto va = get(tr_vertex_pmap, vda); + auto vb = get(tr_vertex_pmap, vdb); + auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); + return std::make_tuple(CGAL::to_double(min_dist), va, vb, min_v); + }); + + auto min_result = std::ranges::min_element(edge_results, {}, [](const auto& tuple) { + return std::get<0>(tuple); + }); + + auto [min_distance, min_va, min_vb, min_vertex] = *min_result; +#else + double min_distance = (std::numeric_limits::max)(); + CDT::Vertex_handle min_va, min_vb, min_vertex; + + for(const auto& edges : patch_edges) { + for(auto [vda, vdb]: edges) { + auto va = get(tr_vertex_pmap, vda); + auto vb = get(tr_vertex_pmap, vdb); + auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); + if(min_dist < min_distance) { + min_distance = CGAL::to_double(min_dist); + min_va = va; + min_vb = vb; + min_vertex = min_v; + } + } + } +#endif + + return {min_distance, min_va, min_vb, min_vertex}; +} + +template +Constraint_distance_result compute_constraint_vertex_distances_from_faces( + CDT& cdt, + const Mesh& mesh, + const VertexPointMap& tr_vertex_pmap) { + + double min_distance = (std::numeric_limits::max)(); + CDT::Vertex_handle min_va, min_vb, min_vertex; + + for(auto face_descriptor : faces(mesh)) { + auto he = halfedge(face_descriptor, mesh); + const auto end = he; + do { + auto va = get(tr_vertex_pmap, source(he, mesh)); + auto vb = get(tr_vertex_pmap, target(he, mesh)); + auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); + if(min_dist < min_distance) { + min_distance = CGAL::to_double(min_dist); + min_va = va; + min_vb = vb; + min_vertex = min_v; + } + he = next(he, mesh); + } while((he = next(he, mesh)) != end); + } + + return {min_distance, min_va, min_vb, min_vertex}; +} + +template +void validate_constraint_vertex_distances_or_throw( + CDT& cdt, + const Mesh& mesh, + const CDT_options& options, + const BordersOfPatches& patch_edges, + const VertexPointMap& tr_vertex_pmap) { + + auto [min_distance, min_va, min_vb, min_vertex] = + options.merge_facets ? compute_constraint_vertex_distances_from_patches_borders(cdt, patch_edges, tr_vertex_pmap) + : compute_constraint_vertex_distances_from_faces(cdt, mesh, tr_vertex_pmap); + + if(!options.quiet) { + std::cout << "Min distance between constraint segment and vertex: " << min_distance << '\n' + << " between segment : " + << CGAL::IO::oformat(min_va, CDT::Conforming_Dt::with_point) << " " + << CGAL::IO::oformat(min_vb, CDT::Conforming_Dt::with_point) << '\n' + << " and vertex : " + << CGAL::IO::oformat(min_vertex, CDT::Conforming_Dt::with_point) << "\n\n"; + } + cdt.check_segment_vertex_distance_or_throw(min_va, min_vb, min_vertex, min_distance, + CDT::Check_distance::NON_SQUARED_DISTANCE); +} + +template +struct Mesh_property_maps { + PatchIdMap patch_id_map; + VertexSelectedMap v_selected_map; + EdgeBorderMap edge_is_border_of_patch_map; + VertexPointMap mesh_vertex_point_map; +}; + +auto setup_mesh_property_maps(Mesh& mesh) { + auto [patch_id_map, patch_id_map_ok] = mesh.add_property_map("f:patch_id", -2); + assert(patch_id_map_ok); CGAL_USE(patch_id_map_ok); + auto [v_selected_map, v_selected_map_ok] = mesh.add_property_map("v:selected", false); + assert(v_selected_map_ok); CGAL_USE(v_selected_map_ok); + auto [edge_is_border_of_patch_map, edge_is_border_of_patch_map_ok] = + mesh.add_property_map("e:is_border_of_patch", false); + assert(edge_is_border_of_patch_map_ok); CGAL_USE(edge_is_border_of_patch_map_ok); + auto mesh_vertex_point_map = get(CGAL::vertex_point, mesh); + + return Mesh_property_maps{patch_id_map, v_selected_map, edge_is_border_of_patch_map, mesh_vertex_point_map}; +} + +using Borders_of_patches = std::vector>>; + +template +auto extract_patch_edges(Mesh& mesh, MeshPropertyMaps pmaps, int number_of_patches) { + Borders_of_patches patch_edges(number_of_patches); + + for(auto h: halfedges(mesh)) + { + if(is_border(h, mesh)) continue; + auto f = face(h, mesh); + auto patch_id = get(pmaps.patch_id_map, f); + auto opp = opposite(h, mesh); + if(is_border(opp, mesh) || patch_id != get(pmaps.patch_id_map, face(opp, mesh))) { + auto va = source(h, mesh); + auto vb = target(h, mesh); + patch_edges[patch_id].emplace_back(va, vb); + put(pmaps.v_selected_map, va, true); + put(pmaps.v_selected_map, vb, true); + } + } + + return patch_edges; +} + +template +int merge_facets_region_growing(Mesh& mesh, + MeshPropertyMaps pmaps, + double coplanar_polygon_max_distance, + double coplanar_polygon_max_angle, + const std::string& dump_surface_mesh_after_merge_filename) { + namespace np = CGAL::parameters; + int number_of_patches = CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces( + mesh, pmaps.patch_id_map, + np::maximum_distance(coplanar_polygon_max_distance) + .maximum_angle(coplanar_polygon_max_angle)); + for(auto f: faces(mesh)) { + if(get(pmaps.patch_id_map, f) < 0) { + std::cerr << "warning: face " << f << " has no patch id! Reassign it to " << number_of_patches << '\n'; + for(auto h: CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) { + std::cerr << " " << target(h, mesh) << ", point " << mesh.point(target(h, mesh)) << '\n'; + } + put(pmaps.patch_id_map, f, number_of_patches++); + } + } + if(!dump_surface_mesh_after_merge_filename.empty()) { + auto [corner_id_map, corner_id_map_ok] = mesh.add_property_map("v:corner_id", -1); + assert(corner_id_map_ok); + CGAL_USE(corner_id_map_ok); + const auto nb_corners = CGAL::Polygon_mesh_processing::detect_corners_of_regions( + mesh, pmaps.patch_id_map, number_of_patches, corner_id_map, + np::maximum_distance(coplanar_polygon_max_distance) + .maximum_angle(coplanar_polygon_max_angle) + .edge_is_constrained_map(pmaps.edge_is_border_of_patch_map)); + Mesh merged_mesh; + CGAL::Polygon_mesh_processing::remesh_almost_planar_patches( + mesh, merged_mesh, number_of_patches, nb_corners, pmaps.patch_id_map, + corner_id_map, pmaps.edge_is_border_of_patch_map, + CGAL::parameters::default_values(), + CGAL::parameters::do_not_triangulate_faces(true)); + mesh.remove_property_map(corner_id_map); + std::ofstream out(dump_surface_mesh_after_merge_filename); + out.precision(17); + out << merged_mesh; + } + + return number_of_patches; +} + +template +int merge_facets_old_method(Mesh& mesh, MeshPropertyMaps pmaps, int initial_number_of_patches) { + int number_of_patches = initial_number_of_patches; + + for(auto f: faces(mesh)) + { + if(get(pmaps.patch_id_map, f) >= 0) continue; + std::stack f_stack; + f_stack.push(f); + while(!f_stack.empty()) { + auto f = f_stack.top(); + f_stack.pop(); + if(get(pmaps.patch_id_map, f) >= 0) continue; + put(pmaps.patch_id_map, f, number_of_patches); + for(auto h: CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) { + auto opp = opposite(h, mesh); + if(is_border_edge(opp, mesh)) { + continue; + } + auto n = face(opp, mesh); + auto a = get(pmaps.mesh_vertex_point_map, source(h, mesh)); + auto b = get(pmaps.mesh_vertex_point_map, target(h, mesh)); + auto c = get(pmaps.mesh_vertex_point_map, target(next(h, mesh), mesh)); + auto d = get(pmaps.mesh_vertex_point_map, target(next(opp, mesh), mesh)); + if(CGAL::orientation(a, b, c, d) != CGAL::COPLANAR) { + continue; + } + if(get(pmaps.patch_id_map, n) >= 0) continue; + f_stack.push(n); + } + } + ++number_of_patches; + } + + return number_of_patches; +} + +template +Borders_of_patches maybe_merge_facets( + Mesh& mesh, + const CDT_options& options, + MeshPropertyMaps pmaps, + double bbox_max_span) { + + int number_of_patches = 0; + Borders_of_patches patch_edges; + + if(options.merge_facets) { + CGAL_CDT_3_TASK_BEGIN(merge_facets_task_handle); + auto start_time = std::chrono::high_resolution_clock::now(); + + if(options.merge_facets_old_method) { + number_of_patches = merge_facets_old_method(mesh, pmaps, number_of_patches); + } else { + number_of_patches = merge_facets_region_growing( + mesh, pmaps, options.coplanar_polygon_max_distance * bbox_max_span, + options.coplanar_polygon_max_angle, options.dump_surface_mesh_after_merge_filename); + } + if (!options.quiet) { + std::cout << "[timings] detected " << number_of_patches << " patches in " << std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; + } + patch_edges = extract_patch_edges(mesh, pmaps, number_of_patches); + CGAL_CDT_3_TASK_END(merge_facets_task_handle); + if(!options.dump_patches_after_merge_filename.empty()) { + CGAL_CDT_3_TASK_BEGIN(output_task_handle); + std::ofstream out(options.dump_patches_after_merge_filename); + CGAL::IO::write_PLY(out, mesh, CGAL::parameters::stream_precision(17)); + CGAL_CDT_3_TASK_END(output_task_handle); + } + } + + return patch_edges; +} + +template +void insert_patches_borders_as_constraints(CDT& cdt, + BordersOfPatches patch_edges, + VdToVhPmap mesh_descriptor_to_vertex_handle_pmap, + VertexPointPmap vertex_point_pmap) { + + for(auto& edges : patch_edges) { + if(edges.empty()) + continue; + auto polylines = CGAL::segment_soup_to_polylines(edges); + while(true) { + const auto non_closed_polylines_begin = + std::partition(polylines.begin(), polylines.end(), + [](const auto& polyline) { return polyline.front() == polyline.back(); }); + if(non_closed_polylines_begin == polylines.end()) + break; + edges.clear(); + for(auto it = non_closed_polylines_begin; it != polylines.end(); ++it) { + auto& polyline = *it; + for(auto it = polyline.begin(), end = polyline.end() - 1; it != end; ++it) { + edges.emplace_back(*it, *(it + 1)); + } + } + polylines.erase(non_closed_polylines_begin, polylines.end()); + auto other_polylines = CGAL::segment_soup_to_polylines(edges); + polylines.insert(polylines.end(), + std::make_move_iterator(other_polylines.begin()), + std::make_move_iterator(other_polylines.end())); + } + + if(polylines.size() > 1) { + double max_sq_length = 0; + auto longest_it = polylines.begin(); + for(auto it = polylines.begin(); it != polylines.end(); ++it) { + auto& polyline = *it; + using CGAL::Bbox_3; + Bbox_3 bb; + for(auto v : polyline) { + bb = bb + Bbox_3(get(vertex_point_pmap, v).bbox()); + } + double sq_diagonal_length = CGAL::square(bb.xmax() - bb.xmin()) + + CGAL::square(bb.ymax() - bb.ymin()) + + CGAL::square(bb.zmax() - bb.zmin()); + if(sq_diagonal_length > max_sq_length) { + max_sq_length = sq_diagonal_length; + longest_it = it; + } + } + if(longest_it != polylines.begin()) { + std::iter_swap(longest_it, polylines.begin()); + } + } + + std::optional face_index; + for(auto& polyline : polylines) { + CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); + polyline.pop_back(); + auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, mesh_descriptor_to_vertex_handle_pmap); + face_index = cdt.insert_constrained_face(range_of_vertices, false, + face_index ? *face_index : -1); + } + } +} + +template +void dump_patches_borders(const BordersOfPatches& patch_edges, + const MeshPropertyMaps& pmaps, + const std::string& dump_patches_borders_prefix) { + std::set> all_edges; + for(auto i = 0u; i < patch_edges.size(); ++i) { + std::stringstream ss; + ss << dump_patches_borders_prefix << i << ".polylines.txt"; + std::ofstream out(ss.str()); + out.precision(17); + const auto& edges = patch_edges[i]; + for(auto [va, vb]: edges) { + all_edges.insert(CGAL::make_sorted_pair(va, vb)); + } + std::cerr << "Patch p#" << i << " has " << edges.size() << " edges\n"; + const auto polylines = CGAL::segment_soup_to_polylines(edges); + for(const auto& polyline: polylines) { + out << polyline.size() << " "; + for(auto v: polyline) { + out << get(pmaps.mesh_vertex_point_map, v) << " "; + } + out << '\n'; + } + out.close(); + std::cerr << " " << polylines.size() << " polylines\n"; + for(const auto& polyline: polylines) { + std::cerr << " - " << polyline.size() << " vertices\n"; + assert(polyline.front() == polyline.back()); + } + } + std::stringstream ss; + ss << dump_patches_borders_prefix << "all_edges.polylines.txt"; + std::ofstream out(ss.str()); + out.precision(17); + const auto polylines = CGAL::segment_soup_to_polylines(all_edges); + for(const auto& polyline: polylines) { + out << polyline.size() << " "; + for(auto v: polyline) { + out << get(pmaps.mesh_vertex_point_map, v) << " "; + } + out << '\n'; + } +} + +int go(Mesh mesh, CDT_options options) { + CDT cdt; + configure_cdt_debug_options(cdt, options); + + auto bbox = compute_bounding_box(mesh, options); + auto bbox_max_span = (std::max)(bbox.x_span(), (std::max)(bbox.y_span(), bbox.z_span())); + + auto pmaps = setup_mesh_property_maps(mesh); + auto patch_edges = maybe_merge_facets(mesh, options, pmaps, bbox_max_span); + if(!options.dump_patches_borders_prefix.empty()) { + CGAL_CDT_3_TASK_BEGIN(output_task_handle); + dump_patches_borders(patch_edges, pmaps, options.dump_patches_borders_prefix); + CGAL_CDT_3_TASK_END(output_task_handle); + } + + int exit_code = EXIT_SUCCESS; + + auto [mesh_descriptor_to_vertex_handle_pmap, tr_vertex_pmap_ok] = mesh.add_property_map("tr_vertex"); assert(tr_vertex_pmap_ok); CGAL_USE(tr_vertex_pmap_ok); CGAL_CDT_3_TASK_BEGIN(insert_vertices_task_handle); auto start_time = std::chrono::high_resolution_clock::now(); CDT::Cell_handle hint{}; for(auto v: vertices(mesh)) { - if(options.merge_facets && false == get(v_selected_map, v)) continue; - auto vh = cdt.insert(get(mesh_vp_map, v), hint, false); + if(options.merge_facets && false == get(pmaps.v_selected_map, v)) continue; + auto vh = cdt.insert(get(pmaps.mesh_vertex_point_map, v), hint, false); hint = vh->cell(); - put(tr_vertex_pmap, v, vh); + put(mesh_descriptor_to_vertex_handle_pmap, v, vh); } if(!options.quiet) { std::cout << "[timings] inserted vertices in " << std::chrono::duration_cast( @@ -593,277 +887,134 @@ int go(Mesh mesh, CDT_options options) { if(!options.quiet) { std::cout << "current is 2D... inserting the 8 vertices of an extended bounding box\n"; } - if(d_x == 0) d_x = bbox_max_width; - if(d_y == 0) d_y = bbox_max_width; - if(d_z == 0) d_z = bbox_max_width; - cdt.insert(Point(bbox.xmin() - d_x, bbox.ymin() - d_y, bbox.zmin() - d_z)); - cdt.insert(Point(bbox.xmin() - d_x, bbox.ymax() + d_y, bbox.zmin() - d_z)); - cdt.insert(Point(bbox.xmin() - d_x, bbox.ymin() - d_y, bbox.zmax() + d_z)); - cdt.insert(Point(bbox.xmin() - d_x, bbox.ymax() + d_y, bbox.zmax() + d_z)); - cdt.insert(Point(bbox.xmax() + d_x, bbox.ymin() - d_y, bbox.zmin() - d_z)); - cdt.insert(Point(bbox.xmax() + d_x, bbox.ymax() + d_y, bbox.zmin() - d_z)); - cdt.insert(Point(bbox.xmax() + d_x, bbox.ymin() - d_y, bbox.zmax() + d_z)); - cdt.insert(Point(bbox.xmax() + d_x, bbox.ymax() + d_y, bbox.zmax() + d_z)); + auto bbox = CGAL::Polygon_mesh_processing::bbox(mesh); + auto dx = bbox.x_span(); + auto dy = bbox.y_span(); + auto dz = bbox.z_span(); + auto max_span = (std::max)(dx, (std::max)(dy, dz)); + if(dx == 0) dx = max_span; + if(dy == 0) dy = max_span; + if(dz == 0) dz = max_span; + + cdt.insert(Point(bbox.xmin() - dx, bbox.ymin() - dy, bbox.zmin() - dz)); + cdt.insert(Point(bbox.xmin() - dx, bbox.ymax() + dy, bbox.zmin() - dz)); + cdt.insert(Point(bbox.xmin() - dx, bbox.ymin() - dy, bbox.zmax() + dz)); + cdt.insert(Point(bbox.xmin() - dx, bbox.ymax() + dy, bbox.zmax() + dz)); + cdt.insert(Point(bbox.xmax() + dx, bbox.ymin() - dy, bbox.zmin() - dz)); + cdt.insert(Point(bbox.xmax() + dx, bbox.ymax() + dy, bbox.zmin() - dz)); + cdt.insert(Point(bbox.xmax() + dx, bbox.ymin() - dy, bbox.zmax() + dz)); + cdt.insert(Point(bbox.xmax() + dx, bbox.ymax() + dy, bbox.zmax() + dz)); } CGAL_CDT_3_TASK_END(insert_vertices_task_handle); start_time = std::chrono::high_resolution_clock::now(); CGAL_CDT_3_TASK_BEGIN(compute_distances_task_handle); - { -#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L - auto [min_sq_distance, min_edge] = (std::ranges::min)( - cdt.finite_edges() | std::views::transform([&](auto edge) { return std::make_pair(cdt.segment(edge).squared_length(), edge); })); -#else - auto trsf = [&](auto edge) { return std::make_pair(cdt.segment(edge).squared_length(), edge); }; - auto min_p = trsf(*cdt.finite_edges_begin()); - for (auto ite=cdt.finite_edges_begin(); ite!=cdt.finite_edges_end(); ++ite) - { - auto p = trsf(*ite); - if (p < min_p) - p = min_p; - } - auto [min_sq_distance, min_edge] = min_p; -#endif - auto min_distance = CGAL::approximate_sqrt(min_sq_distance); - auto vertices_of_min_edge = cdt.vertices(min_edge); - if(!options.quiet) { - std::cout << "Min distance between vertices: " << min_distance << '\n' - << " between vertices: : " << CGAL::IO::oformat(vertices_of_min_edge[0], CGAL::With_point_tag{}) - << " " << CGAL::IO::oformat(vertices_of_min_edge[1], CGAL::With_point_tag{}) << "\n\n"; - } - if(min_distance < epsilon * bbox_max_width) { - std::cerr << "ERROR: min distance between vertices is too small\n"; - exit_code = EXIT_FAILURE; - return exit_code; - } - } - { - double min_distance = (std::numeric_limits::max)(); - CDT::Vertex_handle min_va, min_vb, min_vertex; - if(options.merge_facets) { - for(int i = 0; i < number_of_patches; ++i) { - const auto& edges = patch_edges[i]; - for(auto [vda, vdb]: edges) { - auto va = get(tr_vertex_pmap, vda); - auto vb = get(tr_vertex_pmap, vdb); - auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); - if(min_dist < min_distance) { - min_distance = CGAL::to_double(min_dist); - min_va = va; - min_vb = vb; - min_vertex = min_v; - } - } - } - } else { - for(auto face_descriptor : faces(mesh)) { - auto he = halfedge(face_descriptor, mesh); - const auto end = he; - do { - auto va = get(tr_vertex_pmap, source(he, mesh)); - auto vb = get(tr_vertex_pmap, target(he, mesh)); - auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); - if(min_dist < min_distance) { - min_distance = CGAL::to_double(min_dist); - min_va = va; - min_vb = vb; - min_vertex = min_v; - } - he = next(he, mesh); - } while((he = next(he, mesh)) != end); - } - } - if(!options.quiet) { - std::cout << "Min distance between constraint segment and vertex: " << min_distance << '\n' - << " between segment : " - << CGAL::IO::oformat(min_va, CDT::Conforming_Dt::with_point) << " " - << CGAL::IO::oformat(min_vb, CDT::Conforming_Dt::with_point) << '\n' - << " and vertex : " - << CGAL::IO::oformat(min_vertex, CDT::Conforming_Dt::with_point) << "\n\n"; - } - cdt.check_segment_vertex_distance_or_throw(min_va, min_vb, min_vertex, min_distance, - CDT::Check_distance::NON_SQUARED_DISTANCE); + + exit_code = validate_minimum_vertex_distances(cdt, options.vertex_vertex_epsilon * bbox_max_span, options); + if(exit_code != EXIT_SUCCESS) { + return exit_code; } + + validate_constraint_vertex_distances_or_throw(cdt, mesh, options, patch_edges, mesh_descriptor_to_vertex_handle_pmap); CGAL_CDT_3_TASK_END(compute_distances_task_handle); if(!options.quiet) { std::cout << "[timings] compute distances on " << std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; } - int poly_id = 0; + CGAL_CDT_3_TASK_BEGIN(conforming_task_handle); - CDT_3_try { - start_time = std::chrono::high_resolution_clock::now(); - if(options.merge_facets) { - for(int i = 0; i < number_of_patches; ++i) { - auto& edges = patch_edges[i]; - if(edges.empty()) - continue; - auto polylines = segment_soup_to_polylines(edges); - while(true) { - const auto non_closed_polylines_begin = - std::partition(polylines.begin(), polylines.end(), - [](const auto& polyline) { return polyline.front() == polyline.back(); }); - if(non_closed_polylines_begin == polylines.end()) - break; - edges.clear(); - for(auto it = non_closed_polylines_begin; it != polylines.end(); ++it) { - auto& polyline = *it; - for(auto it = polyline.begin(), end = polyline.end() - 1; it != end; ++it) { - edges.emplace_back(*it, *(it + 1)); - } - } - polylines.erase(non_closed_polylines_begin, polylines.end()); - auto other_polylines = segment_soup_to_polylines(edges); - polylines.insert(polylines.end(), - std::make_move_iterator(other_polylines.begin()), - std::make_move_iterator(other_polylines.end())); - } - if(polylines.size() > 1) { - double max_sq_length = 0; - auto longest_it = polylines.begin(); - for(auto it = polylines.begin(); it != polylines.end(); ++it) { - auto& polyline = *it; - using CGAL::Bbox_3; - Bbox_3 bb; - for(auto v : polyline) { - bb = bb + Bbox_3(get(mesh_vp_map, v).bbox()); - } - double sq_diagonal_length = CGAL::square(bb.xmax() - bb.xmin()) + - CGAL::square(bb.ymax() - bb.ymin()) + - CGAL::square(bb.zmax() - bb.zmin()); - if(sq_diagonal_length > max_sq_length) { - max_sq_length = sq_diagonal_length; - longest_it = it; - } - } - if(longest_it != polylines.begin()) { - std::iter_swap(longest_it, polylines.begin()); - } - } - - std::optional face_index; - for(auto& polyline : polylines) { - CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); - polyline.pop_back(); - auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, tr_vertex_pmap); - face_index = cdt.insert_constrained_face(range_of_vertices, false, - face_index ? *face_index : -1); - } + int poly_id = 0; + auto output_on_exit_scope_guard = CGAL::make_scope_exit(create_output_finalizer(cdt, options)); + start_time = std::chrono::high_resolution_clock::now(); + if(options.merge_facets) { + insert_patches_borders_as_constraints(cdt, std::move(patch_edges), mesh_descriptor_to_vertex_handle_pmap, + pmaps.mesh_vertex_point_map); + } else { + for(auto face_descriptor : faces(mesh)) { + std::vector polygon; + const auto he = halfedge(face_descriptor, mesh); + for(auto vertex_it : CGAL::vertices_around_face(he, mesh)) { + polygon.push_back(get(pmaps.mesh_vertex_point_map, vertex_it)); } - } else { - for(auto face_descriptor : faces(mesh)) { - std::vector polygon; - const auto he = halfedge(face_descriptor, mesh); - for(auto vertex_it : CGAL::vertices_around_face(he, mesh)) { - polygon.push_back(get(mesh_vp_map, vertex_it)); - } - if(cdt.debug_polygon_insertion()) { - std::cerr << "NEW POLYGON #" << poly_id << '\n'; - } - try { - [[maybe_unused]] auto id = cdt.insert_constrained_polygon(polygon, false); - assert(id == poly_id); - ++poly_id; - } catch(int error) { - exit_code = error; - } - // std::ofstream dump("dump.binary.cgal"); - // CGAL::Mesh_3::save_binary_file(dump, cdt); + if(cdt.debug_polygon_insertion()) { + std::cerr << "NEW POLYGON #" << poly_id << '\n'; } - } // not merge_facets - if(!options.quiet) { - std::cout << "[timings] registered facets in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - } - start_time = std::chrono::high_resolution_clock::now(); - cdt.restore_Delaunay(); - if(!options.quiet) { - std::cout << "[timings] restored Delaunay (conforming of facets borders) in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - } - CGAL_CDT_3_TASK_END(conforming_task_handle); - - if(!options.dump_after_conforming_filename.empty()) { - CGAL_CDT_3_TASK_BEGIN(output_task_handle); - using Vertex_index = Mesh::Vertex_index; - [[maybe_unused]] std::size_t time_stamp_counter = 0u; - for(auto v: cdt.finite_vertex_handles()) { - [[maybe_unused]] const auto time_stamp = v->time_stamp(); - assert(++time_stamp_counter == time_stamp); - if(!v->ccdt_3_data().is_Steiner_vertex_on_edge()) continue; - const auto [va, vb] = cdt.ancestors_of_Steiner_vertex_on_edge(v); - const auto index_va = Vertex_index{static_cast(va->time_stamp() - 1)}; - const auto index_vb = Vertex_index{static_cast(vb->time_stamp() - 1)}; - auto [it, end] = CGAL::halfedges_around_source(index_va, mesh); - // std::cerr << " around mesh vertex " << index_va << ", search for vertex " << index_vb << '\n'; - // for(auto it2 = it; it2 != end; ++it2) { - // auto he = *it2; - // auto vd = target(he, mesh); - // std::cerr << " " << vd << '\n'; - // } - it = std::find_if(it, end, [&mesh, index_vb](auto he) { return target(he, mesh) == index_vb; }); - CGAL_assertion(it != end); - auto he = CGAL::Euler::split_edge(*it, mesh); - auto mesh_v = target(he, mesh); - put(mesh_vp_map, mesh_v, v->point()); - assert(mesh_v == Vertex_index{static_cast(time_stamp - 1)}); - } - // for(auto e: edges(mesh)) { - // auto he = halfedge(e, mesh); - // auto vd1 = target(he, mesh); - // auto vd2 = source(he, mesh); - // if(!get(v_selected_map, vd1) || !get(v_selected_map, vd2)) continue; - // auto p1 = get(pmap, vd1); - // auto p2 = get(pmap, vd2); - // auto n = cdt.number_of_vertices(); - // auto v1 = cdt.insert(p1); - // auto v2 = cdt.insert(p2); - // CGAL_assertion(n == cdt.number_of_vertices()); - // auto steiner_vertices = cdt.sequence_of_Steiner_vertices(v1, v2); - // if(!steiner_vertices) continue; - // for(auto v: *steiner_vertices) { - // he = CGAL::Euler::split_edge(he, mesh); - // put(pmap, target(he, mesh), v->point()); - // } - // } - std::ofstream out_mesh(options.dump_after_conforming_filename); - out_mesh.precision(17); - out_mesh << mesh; - out_mesh.close(); - CGAL_CDT_3_TASK_END(output_task_handle); - } - - if(!options.quiet) { - std::cout << "Number of vertices after conforming: " << cdt.number_of_vertices() << "\n\n"; - } - CGAL_CDT_3_TASK_BEGIN(validation_task_handle); - CGAL_assertion(!options.call_is_valid || cdt.Base_triantulation::is_valid(true)); - CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); - CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); - CGAL_CDT_3_TASK_END(validation_task_handle); - if(exit_code == EXIT_SUCCESS) { try { - CGAL_CDT_3_TASK_BEGIN(cdt_task_handle); - start_time = std::chrono::high_resolution_clock::now(); - cdt.restore_constrained_Delaunay(); - if(!options.quiet) { - std::cout << "[timings] restored constrained Delaunay in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - std::cout << "Number of vertices after CDT: " << cdt.number_of_vertices() << "\n\n"; - } - CGAL_CDT_3_TASK_END(cdt_task_handle); + [[maybe_unused]] auto id = cdt.insert_constrained_polygon(polygon, false); + assert(id == poly_id); + ++poly_id; } catch(int error) { exit_code = error; } + // std::ofstream dump("dump.binary.cgal"); + // CGAL::Mesh_3::save_binary_file(dump, cdt); + } + } // not merge_facets + if(!options.quiet) { + std::cout << "[timings] registered facets in " << std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; + } + start_time = std::chrono::high_resolution_clock::now(); + cdt.restore_Delaunay(); + if(!options.quiet) { + std::cout << "[timings] restored Delaunay (conforming of facets borders) in " << std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; + } + + CGAL_CDT_3_TASK_END(conforming_task_handle); + + if(!options.dump_after_conforming_filename.empty()) { + CGAL_CDT_3_TASK_BEGIN(output_task_handle); + using Vertex_index = Mesh::Vertex_index; + [[maybe_unused]] std::size_t time_stamp_counter = 0u; + for(auto v: cdt.finite_vertex_handles()) { + [[maybe_unused]] const auto time_stamp = v->time_stamp(); + assert(++time_stamp_counter == time_stamp); + if(!v->ccdt_3_data().is_Steiner_vertex_on_edge()) continue; + const auto [va, vb] = cdt.ancestors_of_Steiner_vertex_on_edge(v); + const auto index_va = Vertex_index{static_cast(va->time_stamp() - 1)}; + const auto index_vb = Vertex_index{static_cast(vb->time_stamp() - 1)}; + auto [it, end] = CGAL::halfedges_around_source(index_va, mesh); + it = std::find_if(it, end, [&mesh, index_vb](auto he) { return target(he, mesh) == index_vb; }); + CGAL_assertion(it != end); + auto he = CGAL::Euler::split_edge(*it, mesh); + auto mesh_v = target(he, mesh); + put(pmaps.mesh_vertex_point_map, mesh_v, v->point()); + assert(mesh_v == Vertex_index{static_cast(time_stamp - 1)}); + } + std::ofstream out_mesh(options.dump_after_conforming_filename); + out_mesh.precision(17); + out_mesh << mesh; + out_mesh.close(); + CGAL_CDT_3_TASK_END(output_task_handle); + } + + if(!options.quiet) { + std::cout << "Number of vertices after conforming: " << cdt.number_of_vertices() << "\n\n"; + } + CGAL_CDT_3_TASK_BEGIN(validation_task_handle); + CGAL_assertion(!options.call_is_valid || cdt.Base_triantulation::is_valid(true)); + CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); + CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); + CGAL_CDT_3_TASK_END(validation_task_handle); + if(exit_code == EXIT_SUCCESS) { + try { + CGAL_CDT_3_TASK_BEGIN(cdt_task_handle); + start_time = std::chrono::high_resolution_clock::now(); + cdt.restore_constrained_Delaunay(); + if(!options.quiet) { + std::cout << "[timings] restored constrained Delaunay in " << std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; + std::cout << "Number of vertices after CDT: " << cdt.number_of_vertices() << "\n\n"; + } + CGAL_CDT_3_TASK_END(cdt_task_handle); + } catch(int error) { + exit_code = error; } - } CDT_3_catch(CGAL::Failure_exception&) { - finally(); - CDT_3_throw_exception_again; } - finally(); CGAL_CDT_3_TASK_BEGIN(validation_task_handle); CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); @@ -873,7 +1024,7 @@ int go(Mesh mesh, CDT_options options) { return exit_code; } -int bissect_errors(Mesh mesh, CDT_options options) { +int bisect_errors(Mesh mesh, CDT_options options) { auto nb_buckets = static_cast(std::floor(1 / options.ratio)) + 1; std::cerr << "RATIO: " << options.ratio << '\n'; @@ -974,7 +1125,7 @@ int main(int argc, char* argv[]) { CGAL_CDT_3_TASK_BEGIN(read_input_task_handle); auto start_time = std::chrono::high_resolution_clock::now(); - CGAL::CDT_3_read_polygon_mesh_output result; + CGAL::CDT_3_read_polygon_mesh_output read_mesh_result; if(options.read_mesh_with_operator) { std::ifstream in(options.input_filename); if(!in) { @@ -987,19 +1138,19 @@ int main(int argc, char* argv[]) { std::cerr << "Error reading mesh with operator>>" << std::endl; return EXIT_FAILURE; } - result.polygon_mesh = std::move(mesh); + read_mesh_result.polygon_mesh = std::move(mesh); } else { auto read_options = CGAL::parameters::repair_polygon_soup(options.repair_mesh).verbose(options.verbose_level); - result = CGAL::read_polygon_mesh_for_cdt_3(options.input_filename, read_options); + read_mesh_result = CGAL::read_polygon_mesh_for_cdt_3(options.input_filename, read_options); } - if (!result.polygon_mesh) + if (!read_mesh_result.polygon_mesh) { std::cerr << "Not a valid input file." << std::endl; - std::cerr << "Details:\n" << result.polygon_mesh.error() << std::endl; + std::cerr << "Details:\n" << read_mesh_result.polygon_mesh.error() << std::endl; return EXIT_FAILURE; } - Mesh mesh = std::move(*result.polygon_mesh); + Mesh mesh = std::move(*read_mesh_result.polygon_mesh); if(!options.quiet) { std::cout << "[timings] read mesh in " << std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; @@ -1009,26 +1160,26 @@ int main(int argc, char* argv[]) { if(!options.read_mesh_with_operator) { std::cout << "Processing was successful.\n"; - std::cout << " Number of duplicated points: " << result.nb_of_duplicated_points << '\n'; - std::cout << " Number of simplified polygons: " << result.nb_of_simplified_polygons << '\n'; - std::cout << " Number of new polygons: " << result.nb_of_new_polygons << '\n'; - std::cout << " Number of removed invalid polygons: " << result.nb_of_removed_invalid_polygons << '\n'; - std::cout << " Number of removed duplicated polygons: " << result.nb_of_removed_duplicated_polygons << '\n'; - std::cout << " Number of removed isolated points: " << result.nb_of_removed_isolated_points << '\n'; - std::cout << " Polygon soup self-intersects: " << (result.polygon_soup_self_intersects ? "YES" : "no") << '\n'; - std::cout << " Polygon mesh is manifold: " << (result.polygon_mesh_is_manifold ? "yes" : "NO") << '\n'; + std::cout << " Number of duplicated points: " << read_mesh_result.nb_of_duplicated_points << '\n'; + std::cout << " Number of simplified polygons: " << read_mesh_result.nb_of_simplified_polygons << '\n'; + std::cout << " Number of new polygons: " << read_mesh_result.nb_of_new_polygons << '\n'; + std::cout << " Number of removed invalid polygons: " << read_mesh_result.nb_of_removed_invalid_polygons << '\n'; + std::cout << " Number of removed duplicated polygons: " << read_mesh_result.nb_of_removed_duplicated_polygons << '\n'; + std::cout << " Number of removed isolated points: " << read_mesh_result.nb_of_removed_isolated_points << '\n'; + std::cout << " Polygon soup self-intersects: " << (read_mesh_result.polygon_soup_self_intersects ? "YES" : "no") << '\n'; + std::cout << " Polygon mesh is manifold: " << (read_mesh_result.polygon_mesh_is_manifold ? "yes" : "NO") << '\n'; std::cout << std::endl; } } CGAL_CDT_3_TASK_END(read_input_task_handle); - if(options.reject_self_intersections && result.polygon_soup_self_intersects) { + if(options.reject_self_intersections && read_mesh_result.polygon_soup_self_intersects) { std::cerr << "ERROR: input mesh self-intersects\n"; return EXIT_FAILURE; } if(!options.failure_assertion_expression.empty()) { - return bissect_errors(std::move(mesh), options); + return bisect_errors(std::move(mesh), options); } auto exit_code = go(std::move(mesh), options); From d7faad95dd4a21d9fd5463983464e04e10a22435 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 15 Oct 2025 16:30:25 +0200 Subject: [PATCH 16/51] factoring part of the code into a function --- .../Conforming_Delaunay_triangulation_3.h | 10 +- ...ing_constrained_Delaunay_triangulation_3.h | 144 +++++++++++------- .../cdt_3_from_off.cpp | 71 +-------- 3 files changed, 97 insertions(+), 128 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 223886c3112..4133f2844c6 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -177,8 +177,7 @@ protected: } template - Constrained_polyline_id insert_constrained_edge_impl(Vertex_handle va, Vertex_handle vb, - Visitor&) { + Constrained_polyline_id insert_constrained_edge_impl(Vertex_handle va, Vertex_handle vb, Visitor&) { if(va != vb) { if(segment_vertex_epsilon != 0.) { auto [min_dist, min_vertex] = min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); @@ -518,13 +517,14 @@ public: Vertex_handle vb, Vertex_handle min_vertex, double min_dist, - Check_distance option) + Check_distance distance_type) { if(!max_bbox_edge_length) { update_max_bbox_edge_length(); } - if((option == Check_distance::NON_SQUARED_DISTANCE && min_dist < segment_vertex_epsilon * *max_bbox_edge_length) || - (option == Check_distance::SQUARED_DISTANCE && + if((distance_type == Check_distance::NON_SQUARED_DISTANCE && + min_dist < segment_vertex_epsilon * *max_bbox_edge_length) || + (distance_type == Check_distance::SQUARED_DISTANCE && min_dist < CGAL::square(segment_vertex_epsilon * *max_bbox_edge_length))) { std::stringstream ss; 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 ecce1d7c23c..71119655024 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 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -73,6 +74,7 @@ #include #include #include +#include #include #include #include @@ -700,62 +702,8 @@ public: cdt_impl.add_bbox_points_if_not_dimension_3(); if constexpr(has_plc_face_id) { - for(int i = 0; i < number_of_patches; ++i) { - auto& edges = patch_edges[i]; - if(edges.empty()) - continue; - auto polylines = segment_soup_to_polylines(edges); - while(true) { - const auto non_closed_polylines_begin = - std::partition(polylines.begin(), polylines.end(), - [](const auto& polyline) { return polyline.front() == polyline.back(); }); - if(non_closed_polylines_begin == polylines.end()) - break; - edges.clear(); - for(auto it = non_closed_polylines_begin; it != polylines.end(); ++it) { - auto& polyline = *it; - for(auto it = polyline.begin(), end = polyline.end() - 1; it != end; ++it) { - edges.emplace_back(*it, *(it + 1)); - } - } - polylines.erase(non_closed_polylines_begin, polylines.end()); - auto other_polylines = segment_soup_to_polylines(edges); - polylines.insert(polylines.end(), - std::make_move_iterator(other_polylines.begin()), - std::make_move_iterator(other_polylines.end())); - } - - if(polylines.size() > 1) { - double max_sq_length = 0; - auto longest_it = polylines.begin(); - for(auto it = polylines.begin(); it != polylines.end(); ++it) { - auto& polyline = *it; - using CGAL::Bbox_3; - Bbox_3 bb; - for(auto v : polyline) { - bb = bb + Bbox_3(get(mesh_vp_map, v).bbox()); - } - double sq_diagonal_length = CGAL::square(bb.xmax() - bb.xmin()) + - CGAL::square(bb.ymax() - bb.ymin()) + - CGAL::square(bb.zmax() - bb.zmin()); - if(sq_diagonal_length > max_sq_length) { - max_sq_length = sq_diagonal_length; - longest_it = it; - } - } - if(longest_it != polylines.begin()) { - std::iter_swap(longest_it, polylines.begin()); - } - } - - std::optional face_index; - for(auto& polyline : polylines) { - CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); - polyline.pop_back(); - auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, tr_vertex_pmap); - face_index = cdt_impl.insert_constrained_face(range_of_vertices, false, - face_index ? *face_index : -1); - } + for(auto&& edges : patch_edges) { + cdt_impl.insert_constrained_face_defined_by_its_border_edges(edges, mesh_vp_map, tr_vertex_pmap); } } else { for(auto f : faces(mesh)) { @@ -1405,6 +1353,90 @@ public: Conforming_Dt::restore_Delaunay(insert_in_conflict_visitor); } + /** + * Insert a constrained face defined by its border edges. + * + * This function takes a set of border edges (as a segment soup) describing + * the borders of a polygonal face in the input mesh. It reconstructs one or + * more closed polylines from these segments, prioritizes the longest loop + * when several exist, and inserts the corresponding face(s) as constrained + * face(s) in the triangulation. + * + * @tparam Edges A range whose value_type is pair-like (u, v) with u and v + * being vertex descriptors of the PolygonMesh. + * @tparam VertexPointMap A readable property map from vertex descriptor to Point_3. + * @tparam VertexHandleMap A readable property map from vertex descriptor to Vertex_handle. + * + * @param edges The collection of border edges as a segment soup. + * @param mesh_vd_to_point_pmap Property map used to access point coordinates + * for bounding box computation (to identify the longest polyline). + * @param mesh_vd_to_vh_pmap Property map used to convert mesh vertex descriptors + * to triangulation Vertex_handle objects for insertion. + */ + template + void insert_constrained_face_defined_by_its_border_edges(Edges edges, + VertexPointMap mesh_vd_to_point_pmap, + VertexHandleMap mesh_vd_to_vh_pmap) + { + if(edges.empty()) + return; + auto polylines = segment_soup_to_polylines(std::move(edges)); + while(true) { + const auto non_closed_polylines_begin = + std::partition(polylines.begin(), polylines.end(), + [](const auto& polyline) { return polyline.front() == polyline.back(); }); + if(non_closed_polylines_begin == polylines.end()) + break; + Edges edges_copy; + for(auto it = non_closed_polylines_begin; it != polylines.end(); ++it) { + auto& polyline = *it; + for(auto pit = polyline.begin(), end = polyline.end() - 1; pit != end; ++pit) { + edges_copy.emplace_back(*pit, *(pit + 1)); + } + } + polylines.erase(non_closed_polylines_begin, polylines.end()); + auto other_polylines = segment_soup_to_polylines(std::move(edges_copy)); + polylines.insert(polylines.end(), + std::make_move_iterator(other_polylines.begin()), + std::make_move_iterator(other_polylines.end())); + } + + auto get_point_bbox = tr().geom_traits().construct_bbox_3_object(); + auto get_bbox = [&](auto vd) { return get_point_bbox(get(mesh_vd_to_point_pmap, vd)); }; + struct Fake_geom_traits { + using Construct_bbox_3 = std::reference_wrapper>; + Construct_bbox_3 get_bbox_ref; + Construct_bbox_3 construct_bbox_3_object() const { return get_bbox_ref; } + } fake_geom_traits{std::cref(get_bbox)}; + + if(polylines.size() > 1) { + double max_sq_length = 0; + auto longest_it = polylines.begin(); + for(auto it = polylines.begin(); it != polylines.end(); ++it) { + auto& polyline = *it; + auto bb = CGAL::bbox_3(polyline.begin(), polyline.end(), fake_geom_traits); + double sq_diagonal_length = CGAL::square(bb.x_span()) + + CGAL::square(bb.y_span()) + + CGAL::square(bb.z_span()); + if(sq_diagonal_length > max_sq_length) { + max_sq_length = sq_diagonal_length; + longest_it = it; + } + } + if(longest_it != polylines.begin()) { + std::iter_swap(longest_it, polylines.begin()); + } + } + + std::optional face_index; + for(auto& polyline : polylines) { + CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); + polyline.pop_back(); + auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, mesh_vd_to_vh_pmap); + face_index = insert_constrained_face(range_of_vertices, false, face_index.value_or(-1)); + } + } + bool is_facet_constrained(Facet f) const { return f.first->ccdt_3_data().is_facet_constrained(f.second); } 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 ada8928f3e1..754bff240e5 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 @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -741,70 +740,6 @@ Borders_of_patches maybe_merge_facets( return patch_edges; } -template -void insert_patches_borders_as_constraints(CDT& cdt, - BordersOfPatches patch_edges, - VdToVhPmap mesh_descriptor_to_vertex_handle_pmap, - VertexPointPmap vertex_point_pmap) { - - for(auto& edges : patch_edges) { - if(edges.empty()) - continue; - auto polylines = CGAL::segment_soup_to_polylines(edges); - while(true) { - const auto non_closed_polylines_begin = - std::partition(polylines.begin(), polylines.end(), - [](const auto& polyline) { return polyline.front() == polyline.back(); }); - if(non_closed_polylines_begin == polylines.end()) - break; - edges.clear(); - for(auto it = non_closed_polylines_begin; it != polylines.end(); ++it) { - auto& polyline = *it; - for(auto it = polyline.begin(), end = polyline.end() - 1; it != end; ++it) { - edges.emplace_back(*it, *(it + 1)); - } - } - polylines.erase(non_closed_polylines_begin, polylines.end()); - auto other_polylines = CGAL::segment_soup_to_polylines(edges); - polylines.insert(polylines.end(), - std::make_move_iterator(other_polylines.begin()), - std::make_move_iterator(other_polylines.end())); - } - - if(polylines.size() > 1) { - double max_sq_length = 0; - auto longest_it = polylines.begin(); - for(auto it = polylines.begin(); it != polylines.end(); ++it) { - auto& polyline = *it; - using CGAL::Bbox_3; - Bbox_3 bb; - for(auto v : polyline) { - bb = bb + Bbox_3(get(vertex_point_pmap, v).bbox()); - } - double sq_diagonal_length = CGAL::square(bb.xmax() - bb.xmin()) + - CGAL::square(bb.ymax() - bb.ymin()) + - CGAL::square(bb.zmax() - bb.zmin()); - if(sq_diagonal_length > max_sq_length) { - max_sq_length = sq_diagonal_length; - longest_it = it; - } - } - if(longest_it != polylines.begin()) { - std::iter_swap(longest_it, polylines.begin()); - } - } - - std::optional face_index; - for(auto& polyline : polylines) { - CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); - polyline.pop_back(); - auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, mesh_descriptor_to_vertex_handle_pmap); - face_index = cdt.insert_constrained_face(range_of_vertices, false, - face_index ? *face_index : -1); - } - } -} - template void dump_patches_borders(const BordersOfPatches& patch_edges, const MeshPropertyMaps& pmaps, @@ -930,8 +865,10 @@ int go(Mesh mesh, CDT_options options) { auto output_on_exit_scope_guard = CGAL::make_scope_exit(create_output_finalizer(cdt, options)); start_time = std::chrono::high_resolution_clock::now(); if(options.merge_facets) { - insert_patches_borders_as_constraints(cdt, std::move(patch_edges), mesh_descriptor_to_vertex_handle_pmap, - pmaps.mesh_vertex_point_map); + for(auto& edges: patch_edges) { + cdt.insert_constrained_face_defined_by_its_border_edges(std::move(edges), pmaps.mesh_vertex_point_map, + mesh_descriptor_to_vertex_handle_pmap); + } } else { for(auto face_descriptor : faces(mesh)) { std::vector polygon; From 61013d505382cf82368a749e46aa7c5066210b0e Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 16 Oct 2025 17:20:20 +0200 Subject: [PATCH 17/51] more refactoring --- ...ing_constrained_Delaunay_triangulation_3.h | 81 ++++-- .../cdt_3_from_off.cpp | 235 +++++++++--------- 2 files changed, 169 insertions(+), 147 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 71119655024..077bba235e2 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 @@ -646,25 +646,27 @@ public: // ---------------------------------- using parameters::choose_parameter; using parameters::get_parameter; + namespace p = parameters; - auto mesh_vp_map = choose_parameter(get_parameter(np, internal_np::vertex_point), get(CGAL::vertex_point, mesh)); + auto mesh_vp_map = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(vertex_point, mesh)); using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; std::vector>> patch_edges; - auto v_selected_map = get(CGAL::dynamic_vertex_property_t{}, mesh); + auto tr_vertex_pmap = get(CGAL::dynamic_vertex_property_t(), mesh); - int number_of_patches = 0; - constexpr bool has_plc_face_id = !parameters::is_default_parameter::value; + constexpr bool has_plc_face_id = !p::is_default_parameter::value; if constexpr (has_plc_face_id) { - auto mesh_plc_face_id = parameters::get_parameter(np, internal_np::plc_face_id); + auto v_selected_map = get(CGAL::dynamic_vertex_property_t{}, mesh); + auto mesh_plc_face_id = get_parameter(np, internal_np::plc_face_id); using Patch_id_type = CGAL::cpp20::remove_cvref_t; Patch_id_type max_patch_id{0}; for(auto f : faces(mesh)) { max_patch_id = (std::max)(max_patch_id, get(mesh_plc_face_id, f)); } - number_of_patches = static_cast(max_patch_id + 1); + auto number_of_patches = max_patch_id + 1; patch_edges.resize(number_of_patches); for(auto h : halfedges(mesh)) { if(is_border(h, mesh)) @@ -680,38 +682,20 @@ public: put(v_selected_map, vb, true); } } - } else { - number_of_patches = num_faces(mesh); - } - using Vertex_handle = typename Triangulation::Vertex_handle; - using Cell_handle = typename Triangulation::Cell_handle; - auto tr_vertex_pmap = get(CGAL::dynamic_vertex_property_t(), mesh); - Cell_handle hint_ch{}; - for(auto v : vertices(mesh)) { - if constexpr(has_plc_face_id) { - if(false == get(v_selected_map, v)) continue; - } - auto p = get(mesh_vp_map, v); - auto vh = cdt_impl.insert(p, hint_ch, false); - vh->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::CORNER); - hint_ch = vh->cell(); - put(tr_vertex_pmap, v, vh); - } - - cdt_impl.add_bbox_points_if_not_dimension_3(); - - if constexpr(has_plc_face_id) { + cdt_impl.insert_mesh_vertices(mesh, mesh_vp_map, tr_vertex_pmap, p::vertex_is_constrained_map(v_selected_map)); for(auto&& edges : patch_edges) { cdt_impl.insert_constrained_face_defined_by_its_border_edges(edges, mesh_vp_map, tr_vertex_pmap); } } else { + cdt_impl.insert_mesh_vertices(mesh, mesh_vp_map, tr_vertex_pmap); for(auto f : faces(mesh)) { auto face_vertices = vertices_around_face(halfedge(f, mesh), mesh); auto range_of_vertices = CGAL::make_transform_range_from_property_map(face_vertices, tr_vertex_pmap); cdt_impl.insert_constrained_face(range_of_vertices, false); } } + cdt_impl.restore_Delaunay(); cdt_impl.restore_constrained_Delaunay(); // std::cerr << cdt_3_format("cdt_impl: {} vertices, {} cells\n", cdt_impl.number_of_vertices(), @@ -1353,6 +1337,49 @@ public: Conforming_Dt::restore_Delaunay(insert_in_conflict_visitor); } + /** + * Insert vertices from a polygon mesh into the triangulation. + * + * @tparam PolygonMesh A model of FaceGraph. + * @tparam VertexPointMap A readable property map from vertex descriptor to Point_3. + * @tparam VertexHandleMap A writable property map from vertex descriptor to Vertex_handle. + * @tparam NamedParameters A sequence of named parameters. + * + * @param mesh The polygon mesh. + * @param mesh_vp_map Property map to access vertex coordinates. + * @param tr_vertex_pmap Property map to store the mapping from mesh vertices to triangulation vertices. + * @param np Optional named parameters: + * - `vertex_is_constrained_map`: A readable property map from vertex descriptor to bool. + * If provided, only vertices where the map returns true will be inserted. + */ + template + void insert_mesh_vertices(const PolygonMesh& mesh, + VertexPointMap mesh_vp_map, + VertexHandleMap tr_vertex_pmap, + const NamedParameters& np = parameters::default_values()) + { + using parameters::get_parameter; + using parameters::is_default_parameter; + + constexpr bool has_v_filter = !is_default_parameter::value; + auto v_filter_map = get_parameter(np, internal_np::vertex_is_constrained); + + Cell_handle hint_ch{}; + for(auto v : vertices(mesh)) { + if constexpr(has_v_filter) { + if(false == get(v_filter_map, v)) continue; + } + auto p = get(mesh_vp_map, v); + auto vh = this->insert(p, hint_ch, false); + vh->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::CORNER); + hint_ch = vh->cell(); + put(tr_vertex_pmap, v, vh); + } + + add_bbox_points_if_not_dimension_3(); + } + /** * Insert a constrained face defined by its border edges. * 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 754bff240e5..156d1d51aa0 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 @@ -9,13 +9,14 @@ // #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1 #define NO_TRY_CATCH 1 // #define CGAL_DEBUG_CDT_3 1 -#include -#include #include #include -#include #include +#include +#include #include +#include +#include #include #include @@ -448,7 +449,7 @@ Min_distance_result compute_minimum_vertex_distance(const CDT& cdt) { } auto [min_sq_distance, min_edge] = min_p; #endif - auto min_distance = CGAL::approximate_sqrt(min_sq_distance); + auto min_distance = CGAL::to_double(CGAL::approximate_sqrt(min_sq_distance)); auto vertices_of_min_edge = cdt.vertices(min_edge); return {min_distance, vertices_of_min_edge}; @@ -707,13 +708,15 @@ Borders_of_patches maybe_merge_facets( Mesh& mesh, const CDT_options& options, MeshPropertyMaps pmaps, - double bbox_max_span) { - + double bbox_max_span) +{ int number_of_patches = 0; Borders_of_patches patch_edges; - if(options.merge_facets) { - CGAL_CDT_3_TASK_BEGIN(merge_facets_task_handle); + if(!options.merge_facets) return patch_edges; + + CGAL_CDT_3_TASK_BEGIN(merge_facets_task_handle); + { auto start_time = std::chrono::high_resolution_clock::now(); if(options.merge_facets_old_method) { @@ -723,19 +726,23 @@ Borders_of_patches maybe_merge_facets( mesh, pmaps, options.coplanar_polygon_max_distance * bbox_max_span, options.coplanar_polygon_max_angle, options.dump_surface_mesh_after_merge_filename); } - if (!options.quiet) { - std::cout << "[timings] detected " << number_of_patches << " patches in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - } patch_edges = extract_patch_edges(mesh, pmaps, number_of_patches); - CGAL_CDT_3_TASK_END(merge_facets_task_handle); - if(!options.dump_patches_after_merge_filename.empty()) { - CGAL_CDT_3_TASK_BEGIN(output_task_handle); - std::ofstream out(options.dump_patches_after_merge_filename); - CGAL::IO::write_PLY(out, mesh, CGAL::parameters::stream_precision(17)); - CGAL_CDT_3_TASK_END(output_task_handle); + 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"; } } + CGAL_CDT_3_TASK_END(merge_facets_task_handle); + + if(options.dump_patches_after_merge_filename.empty()) return patch_edges; + + CGAL_CDT_3_TASK_BEGIN(output_task_handle); + { + std::ofstream out(options.dump_patches_after_merge_filename); + CGAL::IO::write_PLY(out, mesh, CGAL::parameters::stream_precision(17)); + } + CGAL_CDT_3_TASK_END(output_task_handle); return patch_edges; } @@ -792,115 +799,101 @@ int go(Mesh mesh, CDT_options options) { auto bbox_max_span = (std::max)(bbox.x_span(), (std::max)(bbox.y_span(), bbox.z_span())); auto pmaps = setup_mesh_property_maps(mesh); + auto patch_edges = maybe_merge_facets(mesh, options, pmaps, bbox_max_span); + if(!options.dump_patches_borders_prefix.empty()) { CGAL_CDT_3_TASK_BEGIN(output_task_handle); dump_patches_borders(patch_edges, pmaps, options.dump_patches_borders_prefix); CGAL_CDT_3_TASK_END(output_task_handle); } - int exit_code = EXIT_SUCCESS; - - auto [mesh_descriptor_to_vertex_handle_pmap, tr_vertex_pmap_ok] = mesh.add_property_map("tr_vertex"); - assert(tr_vertex_pmap_ok); CGAL_USE(tr_vertex_pmap_ok); + auto mesh_descriptor_to_vertex_handle_pmap = get(CGAL::dynamic_vertex_property_t(), mesh); CGAL_CDT_3_TASK_BEGIN(insert_vertices_task_handle); - auto start_time = std::chrono::high_resolution_clock::now(); - CDT::Cell_handle hint{}; - for(auto v: vertices(mesh)) { - if(options.merge_facets && false == get(pmaps.v_selected_map, v)) continue; - auto vh = cdt.insert(get(pmaps.mesh_vertex_point_map, v), hint, false); - hint = vh->cell(); - put(mesh_descriptor_to_vertex_handle_pmap, v, vh); - } - if(!options.quiet) { - std::cout << "[timings] inserted vertices in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - std::cout << "Number of vertices: " << cdt.number_of_vertices() << "\n\n"; - } - if(cdt.dimension() < 3) { - if(!options.quiet) { - std::cout << "current is 2D... inserting the 8 vertices of an extended bounding box\n"; + { + auto start_time = std::chrono::high_resolution_clock::now(); + if(options.merge_facets) { + cdt.insert_mesh_vertices(mesh, pmaps.mesh_vertex_point_map, mesh_descriptor_to_vertex_handle_pmap, + CGAL::parameters::vertex_is_constrained_map(pmaps.v_selected_map)); + } else { + cdt.insert_mesh_vertices(mesh, pmaps.mesh_vertex_point_map, mesh_descriptor_to_vertex_handle_pmap); } - - auto bbox = CGAL::Polygon_mesh_processing::bbox(mesh); - auto dx = bbox.x_span(); - auto dy = bbox.y_span(); - auto dz = bbox.z_span(); - auto max_span = (std::max)(dx, (std::max)(dy, dz)); - if(dx == 0) dx = max_span; - if(dy == 0) dy = max_span; - if(dz == 0) dz = max_span; - - cdt.insert(Point(bbox.xmin() - dx, bbox.ymin() - dy, bbox.zmin() - dz)); - cdt.insert(Point(bbox.xmin() - dx, bbox.ymax() + dy, bbox.zmin() - dz)); - cdt.insert(Point(bbox.xmin() - dx, bbox.ymin() - dy, bbox.zmax() + dz)); - cdt.insert(Point(bbox.xmin() - dx, bbox.ymax() + dy, bbox.zmax() + dz)); - cdt.insert(Point(bbox.xmax() + dx, bbox.ymin() - dy, bbox.zmin() - dz)); - cdt.insert(Point(bbox.xmax() + dx, bbox.ymax() + dy, bbox.zmin() - dz)); - cdt.insert(Point(bbox.xmax() + dx, bbox.ymin() - dy, bbox.zmax() + dz)); - cdt.insert(Point(bbox.xmax() + dx, bbox.ymax() + dy, bbox.zmax() + dz)); + CDT::Cell_handle hint{}; + for(auto v: vertices(mesh)) { + if(options.merge_facets && false == get(pmaps.v_selected_map, v)) continue; + auto vh = cdt.insert(get(pmaps.mesh_vertex_point_map, v), hint, false); + hint = vh->cell(); + put(mesh_descriptor_to_vertex_handle_pmap, v, vh); + } + if(!options.quiet) { + auto timing = std::chrono::high_resolution_clock::now() - start_time; + std::cout << "[timings] inserted vertices in " + << std::chrono::duration_cast(timing).count() << " ms\n"; + std::cout << "Number of vertices: " << cdt.number_of_vertices() << "\n\n"; + } + cdt.add_bbox_points_if_not_dimension_3(); } CGAL_CDT_3_TASK_END(insert_vertices_task_handle); - start_time = std::chrono::high_resolution_clock::now(); CGAL_CDT_3_TASK_BEGIN(compute_distances_task_handle); + { + auto start_time = std::chrono::high_resolution_clock::now(); - exit_code = validate_minimum_vertex_distances(cdt, options.vertex_vertex_epsilon * bbox_max_span, options); - if(exit_code != EXIT_SUCCESS) { - return exit_code; + auto exit_code = validate_minimum_vertex_distances(cdt, options.vertex_vertex_epsilon * bbox_max_span, options); + if(exit_code != EXIT_SUCCESS) { + return exit_code; + } + + validate_constraint_vertex_distances_or_throw(cdt, mesh, options, patch_edges, mesh_descriptor_to_vertex_handle_pmap); + + if(!options.quiet) { + auto timing = std::chrono::high_resolution_clock::now() - start_time; + std::cout << "[timings] compute distances on " + << std::chrono::duration_cast(timing).count() << " ms\n"; + } } - - validate_constraint_vertex_distances_or_throw(cdt, mesh, options, patch_edges, mesh_descriptor_to_vertex_handle_pmap); CGAL_CDT_3_TASK_END(compute_distances_task_handle); - if(!options.quiet) { - std::cout << "[timings] compute distances on " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - } - CGAL_CDT_3_TASK_BEGIN(conforming_task_handle); - - int poly_id = 0; - auto output_on_exit_scope_guard = CGAL::make_scope_exit(create_output_finalizer(cdt, options)); - start_time = std::chrono::high_resolution_clock::now(); - if(options.merge_facets) { - for(auto& edges: patch_edges) { - cdt.insert_constrained_face_defined_by_its_border_edges(std::move(edges), pmaps.mesh_vertex_point_map, - mesh_descriptor_to_vertex_handle_pmap); - } - } else { - for(auto face_descriptor : faces(mesh)) { - std::vector polygon; - const auto he = halfedge(face_descriptor, mesh); - for(auto vertex_it : CGAL::vertices_around_face(he, mesh)) { - polygon.push_back(get(pmaps.mesh_vertex_point_map, vertex_it)); + { + int poly_id = 0; + auto output_on_exit_scope_guard = CGAL::make_scope_exit(create_output_finalizer(cdt, options)); + auto start_time = std::chrono::high_resolution_clock::now(); + if(options.merge_facets) { + for(auto& edges: patch_edges) { + cdt.insert_constrained_face_defined_by_its_border_edges(std::move(edges), pmaps.mesh_vertex_point_map, + mesh_descriptor_to_vertex_handle_pmap); } - if(cdt.debug_polygon_insertion()) { - std::cerr << "NEW POLYGON #" << poly_id << '\n'; - } - try { + } else { + for(auto face_descriptor : faces(mesh)) { + std::vector polygon; + const auto he = halfedge(face_descriptor, mesh); + for(auto vertex_it : CGAL::vertices_around_face(he, mesh)) { + polygon.push_back(get(pmaps.mesh_vertex_point_map, vertex_it)); + } + if(cdt.debug_polygon_insertion()) { + std::cerr << "NEW POLYGON #" << poly_id << '\n'; + } [[maybe_unused]] auto id = cdt.insert_constrained_polygon(polygon, false); assert(id == poly_id); ++poly_id; - } catch(int error) { - exit_code = error; + // std::ofstream dump("dump.binary.cgal"); + // CGAL::Mesh_3::save_binary_file(dump, cdt); } - // std::ofstream dump("dump.binary.cgal"); - // CGAL::Mesh_3::save_binary_file(dump, cdt); + } // not merge_facets + if(!options.quiet) { + auto timing = std::chrono::high_resolution_clock::now() - start_time; + std::cout << "[timings] registered facets in " + << std::chrono::duration_cast(timing).count() << " ms\n"; + } + start_time = std::chrono::high_resolution_clock::now(); + cdt.restore_Delaunay(); + if(!options.quiet) { + std::cout << "[timings] restored Delaunay (conforming of facets borders) in " << std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; } - } // not merge_facets - if(!options.quiet) { - std::cout << "[timings] registered facets in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; } - start_time = std::chrono::high_resolution_clock::now(); - cdt.restore_Delaunay(); - if(!options.quiet) { - std::cout << "[timings] restored Delaunay (conforming of facets borders) in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - } - CGAL_CDT_3_TASK_END(conforming_task_handle); if(!options.dump_after_conforming_filename.empty()) { @@ -932,33 +925,35 @@ int go(Mesh mesh, CDT_options options) { if(!options.quiet) { std::cout << "Number of vertices after conforming: " << cdt.number_of_vertices() << "\n\n"; } + CGAL_CDT_3_TASK_BEGIN(validation_task_handle); - CGAL_assertion(!options.call_is_valid || cdt.Base_triantulation::is_valid(true)); - CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); - CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); + { + CGAL_assertion(!options.call_is_valid || cdt.Base_triantulation::is_valid(true)); + CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); + CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); + } CGAL_CDT_3_TASK_END(validation_task_handle); - if(exit_code == EXIT_SUCCESS) { - try { - CGAL_CDT_3_TASK_BEGIN(cdt_task_handle); - start_time = std::chrono::high_resolution_clock::now(); - cdt.restore_constrained_Delaunay(); - if(!options.quiet) { - std::cout << "[timings] restored constrained Delaunay in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - std::cout << "Number of vertices after CDT: " << cdt.number_of_vertices() << "\n\n"; - } - CGAL_CDT_3_TASK_END(cdt_task_handle); - } catch(int error) { - exit_code = error; + + CGAL_CDT_3_TASK_BEGIN(cdt_task_handle); + { + auto start_time = std::chrono::high_resolution_clock::now(); + cdt.restore_constrained_Delaunay(); + if(!options.quiet) { + std::cout << "[timings] restored constrained Delaunay in " << std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; + std::cout << "Number of vertices after CDT: " << cdt.number_of_vertices() << "\n\n"; } } + CGAL_CDT_3_TASK_END(cdt_task_handle); CGAL_CDT_3_TASK_BEGIN(validation_task_handle); - CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); - CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); + { + CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); + CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); + } CGAL_CDT_3_TASK_END(validation_task_handle); - return exit_code; + return EXIT_SUCCESS; } int bisect_errors(Mesh mesh, CDT_options options) { From bc770242a59a9acd812d0c0ff87ba72ee0d21713 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 16 Oct 2025 17:20:34 +0200 Subject: [PATCH 18/51] minor: fix a warning --- .../test/Constrained_triangulation_3/test_ccdt_remeshing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e799250049e..3310fa00a64 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 @@ -40,7 +40,7 @@ int main(int argc, char* argv[]) auto cdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh); static_assert(std::is_same_v); CDT cdt2(mesh); - const auto nb_cstr_facets = cdt2.number_of_constrained_facets(); + [[maybe_unused]] const auto nb_cstr_facets = cdt2.number_of_constrained_facets(); assert(cdt.triangulation().number_of_vertices() == cdt2.triangulation().number_of_vertices()); assert(cdt.number_of_constrained_facets() == cdt2.number_of_constrained_facets()); From 8746a29fa9906c8fc4dfe6e9d6657a3d9d3ed451 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 17 Oct 2025 10:19:00 +0200 Subject: [PATCH 19/51] fix warnings about unused arguments or captures --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 2 +- Installation/include/CGAL/use.h | 6 +++--- 2 files changed, 4 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 077bba235e2..973985619d8 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 @@ -3729,7 +3729,6 @@ public: int expected) { auto value_returned = [this, v0, v1](bool b, bool not_visited) { - CGAL_USE(this); 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), @@ -3737,6 +3736,7 @@ public: b, not_visited ? "(new)" : "(cached)"); } + CGAL_USE(this, v0, v1, b, not_visited); return b; }; auto [cached_value_it, not_visited] = new_edge(v0, v1, false); diff --git a/Installation/include/CGAL/use.h b/Installation/include/CGAL/use.h index f90486205ce..d31f4f8ee69 100644 --- a/Installation/include/CGAL/use.h +++ b/Installation/include/CGAL/use.h @@ -14,15 +14,15 @@ namespace CGAL { namespace internal { -template < typename T > inline -void use(const T&) {} +template inline +void use(T&&...) {} template void use_type() {} } } /// CGAL_USE() is a macro which aims at removing "variable is unused" warnings. -#define CGAL_USE(x) ::CGAL::internal::use(x) +#define CGAL_USE(...) ::CGAL::internal::use(__VA_ARGS__) /// CGAL_USE_TYPE() is a macro which aims at removing "typedef locally /// defined but not used" warnings. From 940ac3d6e4465cafeda46cb735fc76b783d8cd0c Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 17 Oct 2025 10:56:40 +0200 Subject: [PATCH 20/51] try to please AppleCLang 15 - ...by adding an explicit deduction guide. - and add -fexperimental-library (so that ranges::join is found). --- .../test/Constrained_triangulation_3/CMakeLists.txt | 3 +++ .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index 0c5e6e59ee1..1465facf854 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -15,6 +15,9 @@ include(CGAL_setup_tl-excepted) 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 tl::expected) +if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") + target_compile_options(CDT_3_dependencies INTERFACE -fexperimental-library) +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) 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 156d1d51aa0..b4c64afa4b1 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 @@ -585,6 +585,11 @@ struct Mesh_property_maps { VertexPointMap mesh_vertex_point_map; }; +// CTAD deduction guide +template +Mesh_property_maps(PatchIdMap, VertexSelectedMap, EdgeBorderMap, VertexPointMap) + -> Mesh_property_maps; + auto setup_mesh_property_maps(Mesh& mesh) { auto [patch_id_map, patch_id_map_ok] = mesh.add_property_map("f:patch_id", -2); assert(patch_id_map_ok); CGAL_USE(patch_id_map_ok); From e7ab5002a3b53109710ccdd7c73899a4fa44d434 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 17 Oct 2025 16:31:14 +0200 Subject: [PATCH 21/51] add the test non_manifold_face_graph.off --- .../CMakeLists.txt | 1 + Data/data/meshes/non_manifold_face_graph.off | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 Data/data/meshes/non_manifold_face_graph.off diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index 1465facf854..9ed7e0f55e7 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -98,6 +98,7 @@ CGAL_add_cdt3_from_off_test("mpi") CGAL_add_cdt3_from_off_test("3torus") CGAL_add_cdt3_from_off_test("cheese-selection") CGAL_add_cdt3_from_off_test("cheese-selection-2") +CGAL_add_cdt3_from_off_test("non_manifold_face_graph") function(CGAL_add_cdt3_from_local_off_test data_name) CGAL_add_cdt3_from_off_test_aux(${data_name} ${CMAKE_CURRENT_SOURCE_DIR}/data) diff --git a/Data/data/meshes/non_manifold_face_graph.off b/Data/data/meshes/non_manifold_face_graph.off new file mode 100644 index 00000000000..b18f4729134 --- /dev/null +++ b/Data/data/meshes/non_manifold_face_graph.off @@ -0,0 +1,39 @@ +OFF +24 11 0 + +1.5 0.5 1 +0.5 0.5 0 +0.5 0.5 1 +0 0 0 +0 0.5 0 +0.5 0.5 0 +0.5 1 0 +0 1 0 +0.5 0 0 +1 0 0 +1 0.5 0 +1 1 0 +1.5 1.5 1 +2.5 1.5 1 +2.5 0.5 1 +-0.5 -0.5 1 +2.5 -0.5 1 +0.5 1.5 1 +-0.5 0.5 1 +1.5 0.5 1 +1.5 -0.5 1 +-0.5 1.5 1 +0.5 0.5 1 +0.5 -0.5 1 +3 2 1 0 +4 4 3 8 5 +4 7 4 5 6 +4 5 8 9 10 +4 6 5 10 11 +4 12 19 14 13 +4 19 20 16 14 +4 17 22 19 12 +4 22 23 20 19 +4 21 18 22 17 +4 18 15 23 22 + From 7fe01008557a43f8ed9f165e8f228e370809e19b Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 17 Oct 2025 17:15:46 +0200 Subject: [PATCH 22/51] add other minified data sets from Thingi --- .../CMakeLists.txt | 8 +- .../data/135777-min3.off | 69 ++++++++++ .../data/196123-min3.off | 75 +++++++++++ .../data/200695-min3.off | 46 +++++++ .../data/285604-min8.off | 119 ++++++++++++++++++ 5 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/135777-min3.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/196123-min3.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/200695-min3.off create mode 100644 Constrained_triangulation_3/test/Constrained_triangulation_3/data/285604-min8.off diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index 9ed7e0f55e7..b11f6c5d1d9 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -88,7 +88,7 @@ function(CGAL_add_cdt3_from_off_test_aux data_name data_dir) endfunction() function(CGAL_add_cdt3_from_off_test data_name) - CGAL_add_cdt3_from_off_test_aux(${data_name} ${CGAL_DATA_DIR}/meshes) + CGAL_add_cdt3_from_off_test_aux(${data_name} ${CGAL_DATA_DIR}/meshes ${ARGN}) endfunction() CGAL_add_cdt3_from_off_test("cube") @@ -101,7 +101,7 @@ CGAL_add_cdt3_from_off_test("cheese-selection-2") CGAL_add_cdt3_from_off_test("non_manifold_face_graph") function(CGAL_add_cdt3_from_local_off_test data_name) - CGAL_add_cdt3_from_off_test_aux(${data_name} ${CMAKE_CURRENT_SOURCE_DIR}/data) + CGAL_add_cdt3_from_off_test_aux(${data_name} ${CMAKE_CURRENT_SOURCE_DIR}/data ${ARGN}) endfunction() CGAL_add_cdt3_from_local_off_test(cheese18) @@ -134,6 +134,10 @@ if (CGAL_CDT_TEST_USE_THINGI) CGAL_add_cdt3_from_local_off_test(1514904-min8) CGAL_add_cdt3_from_local_off_test(1147177-min1) CGAL_add_cdt3_from_local_off_test(1452672-min1) + CGAL_add_cdt3_from_local_off_test(135777-min3) + CGAL_add_cdt3_from_local_off_test(196123-min3) + CGAL_add_cdt3_from_local_off_test(200695-min3) + CGAL_add_cdt3_from_local_off_test(285604-min8) CGAL_add_cdt3_from_local_off_test(error_mesh-p_not_equal_0-min2) include(./Thingi10k-CDT.cmake) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/135777-min3.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/135777-min3.off new file mode 100644 index 00000000000..9607e930452 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/135777-min3.off @@ -0,0 +1,69 @@ +OFF +42 23 0 + +9.4220000000000006 6.265949 1.2926169999999999 +9.4220000000000006 6.4378760000000002 0.4304 +9.4299999999999997 6.3669419999999999 1.3120000000000001 +9.4220000000000006 6.522742 1.2616000000000001 +9.4299999999999997 1.737943 2.867 +9.4299999999999997 1.310943 1.46 +9.4299999999999997 1.167238 0 +9.4299999999999997 5.7969429999999997 6.9269999999999996 +9.4299999999999997 16.167649999999998 0 +9.4299999999999997 4.499943 6.2329999999999997 +9.4299999999999997 3.3639420000000002 5.3010000000000002 +9.4299999999999997 2.4309430000000001 4.1639999999999997 +12.430999999999999 1.167238 0 +12.430999999999999 1.310943 1.46 +-7.2230889999999999 -5.1901419999999998 1.2724260000000001 +-7.2389999999999999 -5.1100570000000003 1.3959999999999999 +3.7330000000000001 -6.7590570000000003 0.79200000000000004 +-7.255185 -4.6623049999999999 0.88247529999999996 +-7.2552779999999997 -4.6682399999999999 0.88692839999999995 +3.7330000000000001 -6.2710569999999999 0.42699999999999999 +-7.262251 -4.5335140000000003 0.78721229999999998 +-7.7199999999999998 -4.2110570000000003 1.929 +-7.6440000000000001 -4.1590579999999999 1.6100000000000001 +-7.5739999999999998 -4.3940570000000001 1.726 +-7.5469999999999997 -3.6850580000000002 1.224 +-7.5369999999999999 -3.8630580000000001 1.3169999999999999 +-7.4050000000000002 -4.4780569999999997 1.4410000000000001 +-7.3220000000000001 -4.3380580000000002 1.022 +-7.4009999999999998 -4.689057 1.677 +-7.2534720000000004 -4.6835990000000001 0.89814380000000005 +9.4299999999999997 10.06494 1.929 +-7.2789999999999999 -4.0440569999999996 0.34699999999999998 +9.4299999999999997 6.3669419999999999 1.3120000000000001 +-7.2230889999999999 -5.1901419999999998 1.2724260000000001 +3.68716 -6.0934090000000003 0 +3.7330000000000001 -6.2710569999999999 0.42699999999999999 +3.7330000000000001 -6.2710569999999999 0.42699999999999999 +-7.262251 -4.5335140000000003 0.78721229999999998 +-7.6029999999999998 -4.5610569999999999 2.0139999999999998 +-7.4050000000000002 -4.4780569999999997 1.4410000000000001 +-7.3220000000000001 -4.3380580000000002 1.022 +-7.2599 -4.5207920000000001 0.75421059999999995 +3 0 1 2 +3 1 3 2 +3 4 32 5 +3 6 5 32 +3 8 32 30 +3 9 7 32 +3 32 7 30 +3 10 32 11 +3 4 11 32 +3 10 9 32 +3 6 32 8 +3 13 5 12 +3 38 23 21 +3 26 25 22 +3 24 25 26 +3 27 39 28 +3 16 19 14 +3 35 34 20 +3 18 29 36 +3 18 28 29 +3 15 33 29 +3 31 40 41 +3 40 17 37 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/196123-min3.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/196123-min3.off new file mode 100644 index 00000000000..7b16adc0de4 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/196123-min3.off @@ -0,0 +1,75 @@ +OFF +37 34 0 + +172.45960998535156 327.00732421875 10.000033378601074 +173.35765075683594 327.18472290039062 10.000033378601074 +172.45960998535156 327.00732421875 10 +174.25531005859375 327.36398315429688 10.000033378601074 +175.282470703125 327.571533203125 10 +170.58482360839844 325.87066650390625 10 +170.58482360839844 325.87066650390625 10.000033378601074 +170.58476257324219 326.1373291015625 10 +170.58476257324219 326.1373291015625 10.000033378601074 +170.58488464355469 326.67068481445312 10.000033378601074 +170.58488464355469 326.67068481445312 10 +170.58477783203125 326.40402221679688 10.000033378601074 +170.58477783203125 326.40402221679688 10 +171.21096801757812 326.77630615234375 10 +171.21096801757812 326.77630615234375 10.000033378601074 +171.83589172363281 326.88851928710938 10.000033378601074 +171.83589172363281 326.88851928710938 10 +198.59626770019531 331.45306396484375 10 +171.3594970703125 325.91452026367188 10 +171.3594970703125 325.91452026367188 10.000033378601074 +171.10134887695312 325.89859008789062 10 +171.10134887695312 325.89859008789062 10.000033378601074 +170.84312438964844 325.88397216796875 10.000033378601074 +170.84312438964844 325.88397216796875 10 +173.88740539550781 326.08248901367188 10.000033378601074 +174.88548278808594 326.13330078125 10 +173.12164306640625 326.03756713867188 10 +172.62298583984375 326.00552368164062 10.000033378601074 +176.65058898925781 326.20169067382812 10 +179.54045104980469 326.31793212890625 10 +182.43142700195312 326.40179443359375 10 +192.267333984375 331.10971069335938 10 +197.96424865722656 332.17782592773438 10 +175.15260314941406 328.56558227539062 10.000033378601074 +175.15260314941406 325.1444091796875 10.000033378601074 +161.37049865722656 320.09548950195312 10.000033378601074 +161.37049865722656 332.70773315429688 10.000033378601074 +3 1 3 2 +3 2 3 4 +3 18 26 27 +3 27 26 24 +3 4 30 2 +3 31 32 17 +3 30 29 2 +3 2 29 16 +3 25 10 28 +3 10 25 12 +3 18 7 26 +3 18 20 7 +3 20 23 7 +3 23 5 7 +3 7 12 26 +3 26 12 25 +3 10 13 28 +3 13 16 28 +3 21 19 34 +3 21 34 22 +3 19 27 34 +3 36 11 35 +3 3 1 33 +3 1 0 33 +3 0 15 33 +3 15 14 33 +3 14 9 33 +3 33 9 36 +3 11 8 35 +3 8 6 35 +3 35 6 34 +3 6 22 34 +3 34 27 24 +3 11 36 9 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/200695-min3.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/200695-min3.off new file mode 100644 index 00000000000..c6eeab86882 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/200695-min3.off @@ -0,0 +1,46 @@ +OFF +25 17 0 + +137.05000305175781 47.498001098632812 132.02400207519531 +137.05000305175781 42.498001098632812 132.65000915527344 +137.05000305175781 20 131.40000915527344 +137.05000305175781 14.998001098632812 90.316001892089844 +137.05000305175781 0 133.90000915527344 +137.05000305175781 5 131.40000915527344 +120.80000305175781 0 133.90000915527344 +134.05000305175781 2.9980001449584961 133.90000915527344 +136.67401123046875 15.874000549316406 133.90000915527344 +137.12800598144531 5 132.33399963378906 +137.12800598144531 20 132.33399963378906 +137.36601257324219 5 133.24000549316406 +137.36601257324219 20 133.24000549316406 +137.75401306152344 5 134.09400939941406 +137.05000305175781 31.248001098632812 90.316001892089844 +137.05000305175781 16.248001098632812 133.90000915527344 +118.55000305175781 2.25 133.90000915527344 +104.55000305175781 0 133.90000915527344 +137.05000305175781 32.498001098632812 133.90000915527344 +55.800003051757812 0 133.90000915527344 +54.674003601074219 1.124000072479248 133.90000915527344 +55.050003051757812 0.74800002574920654 133.90000915527344 +136.30000305175781 31.750001907348633 133.90000915527344 +39.550003051757812 0 133.90000915527344 +137.05000305175781 20 118.12000274658203 +3 0 1 2 +3 4 5 2 +3 7 4 8 +3 10 9 11 +3 10 11 12 +3 12 11 13 +3 14 24 3 +3 2 15 4 +3 15 8 4 +3 17 7 16 +3 18 2 1 +3 7 17 6 +3 2 18 15 +3 7 8 22 +3 15 22 8 +3 20 23 21 +3 19 21 23 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/285604-min8.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/285604-min8.off new file mode 100644 index 00000000000..b389df129c7 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/285604-min8.off @@ -0,0 +1,119 @@ +OFF +67 48 0 + +25.003999710083008 4.125999927520752 5.3249998092651367 +24.648000717163086 4.1110000610351562 5.3020000457763672 +27.875 4.124000072479248 5.3159999847412109 +28.104999542236328 4.0980000495910645 5.3020000457763672 +27.547000885009766 -0.76599997282028198 5.3020000457763672 +24.26300048828125 4.1999998092651367 5.3020000457763672 +24.350000381469727 -1.2070000171661377 5.3020000457763672 +22.61199951171875 -1.2680000066757202 5.3020000457763672 +18.076999664306641 -0.76700001955032349 5.3020000457763672 +20.534172058105469 6.6471333503723145 5.3020000457763672 +21.565000534057617 6.2769999504089355 5.3020000457763672 +21.576999664306641 6.2220001220703125 5.3020000457763672 +24.184999465942383 4.2010002136230469 5.3020000457763672 +24.132999420166016 4.2109999656677246 5.3020000457763672 +23.76099967956543 4.3359999656677246 5.3020000457763672 +23.552000045776367 4.3480663299560547 5.3020000457763672 +23.822000503540039 4.2779998779296875 5.3020000457763672 +23.259000778198242 4.4369997978210449 5.3029999732971191 +22.599832534790039 5.119999885559082 5.3020000457763672 +21.814010620117188 5.7782330513000488 5.3020000457763672 +22.527999877929688 4.9455761909484863 5.3020000457763672 +21.797874450683594 5.8303966522216797 5.3020968437194824 +22.527999877929688 4.6591053009033203 5.3020000457763672 +21.822000503540039 5.8159999847412109 6.0100002288818359 +21.772134780883789 5.687347412109375 5.3039073944091797 +21.657405853271484 5.9652857780456543 5.3020000457763672 +21.655532836914062 5.9393019676208496 5.3023014068603516 +21.794061660766602 5.8427157402038574 5.3020000457763672 +21.660037994384766 5.9280643463134766 5.3023800849914551 +21.660087585449219 5.9279398918151855 5.3023805618286133 +21.820028305053711 5.6319999694824219 5.3020000457763672 +21.659999847412109 5.9279999732971191 5.3020000457763672 +19.494876861572266 4.9111647605895996 5.3020000457763672 +21.641000747680664 5.9800000190734863 5.3020000457763672 +21.624000549316406 6.0019998550415039 5.309999942779541 +-2.1689999103546143 -0.46799999475479126 5.3020000457763672 +20.37629508972168 6.2305684089660645 5.3020000457763672 +-2.2790000438690186 -1.3259999752044678 6.0859999656677246 +-2.1040000915527344 0.082999996840953827 5.320000171661377 +22.329999923706055 5.0479998588562012 5.3020000457763672 +19.238409042358398 4.5272407531738281 5.3020000457763672 +-1.9989999532699585 0.26600000262260437 6.7109999656677246 +18.052036285400391 3.3704285621643066 5.3020000457763672 +20.326999664306641 6.2470002174377441 5.0159997940063477 +19.670000076293945 6.4640002250671387 3.7780001163482666 +18.603000640869141 4.8670001029968262 3.7780001163482666 +-0.6029999852180481 6.8179998397827148 2.6359999179840088 +0.33100000023841858 3.0639998912811279 5.0159997940063477 +-1.143372654914856 4.4346461296081543 5.3020000457763672 +-1.1940000057220459 4.5510001182556152 5.0159997940063477 +0.83399999141693115 3.4670000076293945 3.7780001163482666 +-3.2599999904632568 10 5.0159997940063477 +-2.5490000247955322 10 3.7780001163482666 +-2.3269999027252197 8.1979999542236328 3.7780001163482666 +-1.6699999570846558 6.4640002250671387 3.7780001163482666 +-2.1198277473449707 5.846644401550293 5.3020000457763672 +-3.0250000953674316 8.0860004425048828 5.0159997940063477 +-1.9939999580383301 3.0099999904632568 5.3020000457763672 +-1.2384099960327148 4.5272407531738281 5.3020000457763672 +-2.2780001163482666 3.7369999885559082 5.3020000457763672 +-2.0220000743865967 0.74299997091293335 6.0689997673034668 +-1.9220000505447388 1.1330000162124634 5.3020000457763672 +-1.9739999771118164 0.92799997329711914 5.314000129699707 +-2.1050000190734863 -0.041999999433755875 6.1490001678466797 +-2.3269999027252197 6.2470002174377441 5.0159997940063477 +-0.6029999852180481 4.8670001029968262 3.7780001163482666 +21.659999847412109 5.9279999732971191 5.3020000457763672 +3 2 3 0 +3 5 1 6 +3 3 4 1 +3 4 6 1 +3 12 5 6 +3 13 12 7 +3 25 19 30 +3 19 25 27 +3 24 29 23 +3 31 32 25 +3 25 30 31 +3 34 66 26 +3 33 25 32 +3 66 34 28 +3 10 11 36 +3 11 33 36 +3 27 18 19 +3 17 18 21 +3 39 22 32 +3 20 19 18 +3 40 32 22 +3 8 40 22 +3 16 8 14 +3 6 7 12 +3 15 14 8 +3 8 16 7 +3 36 9 10 +3 30 39 32 +3 31 30 32 +3 36 33 32 +3 43 45 44 +3 22 15 8 +3 8 42 40 +3 16 13 7 +3 49 58 55 +3 57 59 48 +3 51 52 56 +3 64 56 53 +3 65 54 46 +3 65 49 54 +3 49 64 54 +3 47 65 50 +3 47 49 65 +3 38 62 35 +3 62 61 35 +3 38 37 63 +3 41 60 63 +3 63 37 41 + From 28d6ac5e49dde55c1e7a415a2aff656aa8c776e9 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 21 Oct 2025 16:17:39 +0200 Subject: [PATCH 23/51] missing include --- ...Conforming_constrained_Delaunay_triangulation_vertex_data_3.h | 1 + 1 file changed, 1 insertion(+) 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 21336b05806..c052949659c 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 @@ -16,6 +16,7 @@ #include #include +#include #include From e02c1495bf5d2568be2929afd74143ac8b9757f9 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 21 Oct 2025 16:18:30 +0200 Subject: [PATCH 24/51] add a declarative RAII macro --- .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 7 +++++-- 1 file changed, 5 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 b4c64afa4b1..c3cdea6d089 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 @@ -283,6 +283,9 @@ CDT_options::CDT_options(int argc, char* argv[]) { # define CGAL_CDT_3_TASK_END(task_handle) \ std::cerr << "-STOP " << #task_handle << '\n'; \ __itt_task_end(cdt_3_domain); +# define CGAL_CDT_3_TASK_SCOPE(task_handle) \ + CGAL_CDT_3_TASK_BEGIN(task_handle) \ + auto scope_exit = CGAL::make_scope_exit([&]() { CGAL_CDT_3_TASK_END(task_handle); }); auto cdt_3_domain = __itt_domain_create("org.cgal.CDT_3"); auto read_input_task_handle = __itt_string_handle_create("CDT_3: read input file"); @@ -297,6 +300,7 @@ CDT_options::CDT_options(int argc, char* argv[]) { #else // no ITT # define CGAL_CDT_3_TASK_BEGIN(task_handle) # define CGAL_CDT_3_TASK_END(task_handle) +# define CGAL_CDT_3_TASK_SCOPE(task_handle) #endif // no ITT void configure_cdt_debug_options(CDT& cdt, const CDT_options& options) { @@ -339,7 +343,7 @@ auto compute_bounding_box(const Mesh& mesh, const CDT_options& options) { std::function create_output_finalizer(const CDT& cdt, const CDT_options& options) { return [&cdt, &options]() { - CGAL_CDT_3_TASK_BEGIN(output_task_handle); + CGAL_CDT_3_TASK_SCOPE(output_task_handle); { auto dump_tets_to_medit = [](std::string fname, const std::vector &points, @@ -423,7 +427,6 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& #endif } - CGAL_CDT_3_TASK_END(output_task_handle); }; } From 15a155ec3068337e4aa09ed6d2dd132b566b69e1 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 22 Oct 2025 15:08:29 +0200 Subject: [PATCH 25/51] rename to `insert_vertices_range` and use `spatial_sort` --- ...ing_constrained_Delaunay_triangulation_3.h | 96 +++++++++++-------- .../cdt_3_from_off.cpp | 4 +- 2 files changed, 56 insertions(+), 44 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 973985619d8..1ace19259af 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 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -79,16 +80,11 @@ #include #include #include +#include #include #if CGAL_CXX20 && __has_include() # include #endif -#if CGAL_CXX20 && __has_include() -# include -# include -#elif CGAL_DEBUG_CDT_3 -# error "Compiler needs " -#endif namespace CGAL { @@ -651,7 +647,9 @@ public: auto mesh_vp_map = choose_parameter(get_parameter(np, internal_np::vertex_point), get_const_property_map(vertex_point, mesh)); - using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + using graph_traits = boost::graph_traits; + using vertex_descriptor = typename graph_traits::vertex_descriptor; + using halfedge_descriptor = typename graph_traits::halfedge_descriptor; std::vector>> patch_edges; auto tr_vertex_pmap = get(CGAL::dynamic_vertex_property_t(), mesh); @@ -682,13 +680,12 @@ public: put(v_selected_map, vb, true); } } - - cdt_impl.insert_mesh_vertices(mesh, mesh_vp_map, tr_vertex_pmap, p::vertex_is_constrained_map(v_selected_map)); + cdt_impl.insert_vertices_range(vertices(mesh), mesh_vp_map, tr_vertex_pmap, p::vertex_is_constrained_map(v_selected_map)); for(auto&& edges : patch_edges) { cdt_impl.insert_constrained_face_defined_by_its_border_edges(edges, mesh_vp_map, tr_vertex_pmap); } } else { - cdt_impl.insert_mesh_vertices(mesh, mesh_vp_map, tr_vertex_pmap); + cdt_impl.insert_vertices_range(vertices(mesh), mesh_vp_map, tr_vertex_pmap); for(auto f : faces(mesh)) { auto face_vertices = vertices_around_face(halfedge(f, mesh), mesh); auto range_of_vertices = CGAL::make_transform_range_from_property_map(face_vertices, tr_vertex_pmap); @@ -731,24 +728,30 @@ public: using std::cend; if constexpr (false == has_plc_face_id) { - using Vertex_handle = typename Triangulation::Vertex_handle; - using Cell_handle = typename Triangulation::Cell_handle; - using Vec_vertex_handle = std::vector; - Vec_vertex_handle vertices(points.size()); - Cell_handle hint_ch{}; - auto i = 0u; - for(auto p_descr : points) { - auto p = get(point_map, p_descr); - auto vh = cdt_impl.insert(p, hint_ch, false); - hint_ch = vh->cell(); - vertices[i++] = vh; - } + // One cannot use the input range directly because it may be a non-random-access range. + auto points_vec = std::vector(cbegin(points), cend(points)); - cdt_impl.add_bbox_points_if_not_dimension_3(); + // Create a property map: index -> point + auto index_to_point_pmap = boost::make_function_property_map( + [&points_vec, point_map](std::size_t i) { + return get(point_map, points_vec[i]); + }); - struct - { - Vec_vertex_handle* vertices; + // 2. Create a property map to store vertex handles: index -> vertex handle + std::vector vertices(points.size()); + auto index_to_vh_pmap = boost::make_iterator_property_map( + vertices.begin(), + boost::typed_identity_property_map()); + + // Insert vertices using a range of indices + using counting_iterator = boost::counting_iterator; + cdt_impl.insert_vertices_range( + CGAL::make_range(counting_iterator(0),counting_iterator(points.size())), + index_to_point_pmap, + index_to_vh_pmap); + + struct { // this is not a lambda so that the capture is copy-assignable + std::vector* vertices; const Vertex_handle& operator()(std::size_t i) const { return (*vertices)[i]; } } transform_function{&vertices}; for(auto polygon : polygons) { @@ -789,9 +792,9 @@ public: np.polygon_to_face_output_iterator(polygon_to_face_output_iterator)); Conforming_constrained_Delaunay_triangulation_3 ccdt{std::move(surface_mesh), - CGAL::parameters::plc_face_id(plc_face_id_pmap) - .do_self_intersection_tests(false) - .geom_traits(cdt_impl.geom_traits())}; + CGAL::parameters::plc_face_id(plc_face_id_pmap) + .do_self_intersection_tests(false) + .geom_traits(cdt_impl.geom_traits())}; *this = std::move(ccdt); } } @@ -975,6 +978,10 @@ public: 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()(); } @@ -1338,26 +1345,26 @@ public: } /** - * Insert vertices from a polygon mesh into the triangulation. + * Insert vertices from a range into the triangulation. * - * @tparam PolygonMesh A model of FaceGraph. + * @tparam VertexRange A range of vertex descriptors. * @tparam VertexPointMap A readable property map from vertex descriptor to Point_3. * @tparam VertexHandleMap A writable property map from vertex descriptor to Vertex_handle. * @tparam NamedParameters A sequence of named parameters. * - * @param mesh The polygon mesh. - * @param mesh_vp_map Property map to access vertex coordinates. - * @param tr_vertex_pmap Property map to store the mapping from mesh vertices to triangulation vertices. + * @param vertices The range of vertices to insert. + * @param mesh_vpmap Property map to access vertex coordinates. + * @param tr_vertex_pmap Property map to store the mapping from vertices to triangulation vertices. * @param np Optional named parameters: * - `vertex_is_constrained_map`: A readable property map from vertex descriptor to bool. * If provided, only vertices where the map returns true will be inserted. */ - template - void insert_mesh_vertices(const PolygonMesh& mesh, - VertexPointMap mesh_vp_map, - VertexHandleMap tr_vertex_pmap, - const NamedParameters& np = parameters::default_values()) + void insert_vertices_range(VertexRange&& vertices, + VertexPointMap vertex_point_map, + VertexHandleMap tr_vertex_pmap, + const NamedParameters& np = parameters::default_values()) { using parameters::get_parameter; using parameters::is_default_parameter; @@ -1365,13 +1372,18 @@ public: constexpr bool has_v_filter = !is_default_parameter::value; auto v_filter_map = get_parameter(np, internal_np::vertex_is_constrained); + using std::begin; using std::end; + std::vector vertices_vec(begin(vertices), end(vertices)); + + using Search_traits = Spatial_sort_traits_adapter_3; + spatial_sort(vertices_vec.begin(), vertices_vec.end(), Search_traits(vertex_point_map)); + Cell_handle hint_ch{}; - for(auto v : vertices(mesh)) { + for(auto v : vertices_vec) { if constexpr(has_v_filter) { if(false == get(v_filter_map, v)) continue; } - auto p = get(mesh_vp_map, v); - auto vh = this->insert(p, hint_ch, false); + auto vh = this->insert(get(vertex_point_map, v), hint_ch, false); vh->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::CORNER); hint_ch = vh->cell(); put(tr_vertex_pmap, v, vh); 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 c3cdea6d089..2b2b86d821d 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 @@ -822,10 +822,10 @@ int go(Mesh mesh, CDT_options options) { { auto start_time = std::chrono::high_resolution_clock::now(); if(options.merge_facets) { - cdt.insert_mesh_vertices(mesh, pmaps.mesh_vertex_point_map, mesh_descriptor_to_vertex_handle_pmap, + cdt.insert_vertices_range(vertices(mesh), pmaps.mesh_vertex_point_map, mesh_descriptor_to_vertex_handle_pmap, CGAL::parameters::vertex_is_constrained_map(pmaps.v_selected_map)); } else { - cdt.insert_mesh_vertices(mesh, pmaps.mesh_vertex_point_map, mesh_descriptor_to_vertex_handle_pmap); + cdt.insert_vertices_range(vertices(mesh), pmaps.mesh_vertex_point_map, mesh_descriptor_to_vertex_handle_pmap); } CDT::Cell_handle hint{}; for(auto v: vertices(mesh)) { From d34201ed383dd6694bda43e5a4da40a1feb2d33a Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 22 Oct 2025 15:09:07 +0200 Subject: [PATCH 26/51] fix a conversion warning (-1 converted to `std::size_t`) --- .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 4 +++- 1 file changed, 3 insertions(+), 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 2b2b86d821d..ac650de2225 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 @@ -651,7 +651,9 @@ int merge_facets_region_growing(Mesh& mesh, } } if(!dump_surface_mesh_after_merge_filename.empty()) { - auto [corner_id_map, corner_id_map_ok] = mesh.add_property_map("v:corner_id", -1); + static constexpr auto max_size_t = std::numeric_limits::max(); + auto [corner_id_map, corner_id_map_ok] = + mesh.add_property_map("v:corner_id", max_size_t); assert(corner_id_map_ok); CGAL_USE(corner_id_map_ok); const auto nb_corners = CGAL::Polygon_mesh_processing::detect_corners_of_regions( From c48b4cb6c29b506dd938b29aa70ed44e581d7940 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 23 Oct 2025 12:22:03 +0200 Subject: [PATCH 27/51] handle isolated non-manifold vertices into constrained faces --- ...ing_constrained_Delaunay_triangulation_3.h | 210 +++++++++++++++--- ...ned_Delaunay_triangulation_vertex_data_3.h | 2 +- 2 files changed, 181 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 1ace19259af..26e6444e0e5 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 @@ -36,8 +36,9 @@ #include #include #include -#include #include +#include +#include #include #include @@ -680,9 +681,113 @@ public: put(v_selected_map, vb, true); } } - cdt_impl.insert_vertices_range(vertices(mesh), mesh_vp_map, tr_vertex_pmap, p::vertex_is_constrained_map(v_selected_map)); + + auto extra_isolated_mesh_vertices = std::invoke([&]() + { + std::vector> extra_isolated_vertices(number_of_patches); + using Point = CGAL::cpp20::remove_cvref_t()))>; + struct Point_info { + halfedge_descriptor halfedge; + int count; + }; + CGAL::unordered_flat_map points_to_halfedge; + auto visited_halfedges = get(CGAL::dynamic_halfedge_property_t(), mesh); + + for(const auto h : halfedges(mesh)) { + // If 'h' is not visited yet, we walk around the target of 'h' and mark these + // halfedges as visited. Thus, if we are here and the target is already marked as visited, + // it means that the vertex is non manifold. + if(get(visited_halfedges, h)) + continue; + + const auto v = target(h, mesh); + const auto& p = get(mesh_vp_map, v); + + bool is_vertex_non_manifold = false; + + put(visited_halfedges, h, true); + + const auto [it, inserted_new_point] = points_to_halfedge.emplace(p, Point_info{h, 1}); + if(!inserted_new_point) { // unsuccessful insertion: already seen this point, but not from this star + + auto& [first_halfedge, nb_of_occurrences] = it->second; + is_vertex_non_manifold = true; + + // If this is the second time we visit that vertex and the first star was manifold, we have + // not yet marked the vertex as non-manifold from the first star, but must do so now. + if(nb_of_occurrences == 1) { + if(is_border(first_halfedge, mesh)) { + first_halfedge = opposite(first_halfedge, mesh); + } + auto first_patch_id = get(mesh_plc_face_id, face(first_halfedge, mesh)); + if(!is_border_edge(first_halfedge, mesh)) { + // isolated non-manifold vertex on the patch `first_patch_id` + extra_isolated_vertices[first_patch_id].insert(v); + } + } + ++nb_of_occurrences; + } + + // While walking the star of this halfedge, if we meet a border halfedge more than once, + // it means the mesh is pinched and we are also in the case of a non-manifold situation. + const auto [border_halfedge_counter, is_on_patch_border, patch_id_of_v] = std::invoke([&]() { + int border_halfedge_counter = 0; + bool is_on_patch_border = false; + std::optional opt_patch_id_of_v = std::nullopt; + auto halfedge_around_v = h; + do { + const auto opposite_he = opposite(halfedge_around_v, mesh); + put(visited_halfedges, halfedge_around_v, true); + if(is_border(halfedge_around_v, mesh)) { + ++border_halfedge_counter; + } else { + auto halfedge_patch_id = get(mesh_plc_face_id, face(halfedge_around_v, mesh)); + opt_patch_id_of_v = opt_patch_id_of_v.value_or(halfedge_patch_id); + if(opt_patch_id_of_v != halfedge_patch_id) { + is_on_patch_border = true; + put(v_selected_map, v, true); + } + } + + halfedge_around_v = prev(opposite_he, mesh); + } while(halfedge_around_v != h); + + CGAL_assertion(opt_patch_id_of_v.has_value()); + return std::make_tuple(border_halfedge_counter, is_on_patch_border, opt_patch_id_of_v.value()); + }); + + if(border_halfedge_counter > 1) { + is_vertex_non_manifold = true; + } + + if(is_vertex_non_manifold) { + if(is_on_patch_border == false) { + // isolated non-manifold vertex on the patch `patch_id_of_v` + extra_isolated_vertices[patch_id_of_v].insert(v); + } + } + } + return extra_isolated_vertices; + }); + + std::for_each(extra_isolated_mesh_vertices.begin(), extra_isolated_mesh_vertices.end(), + [&](const auto& isolated_vertices_in_patch) { + for(const auto vd : isolated_vertices_in_patch) { + put(v_selected_map, vd, true); + } + }); + + cdt_impl.insert_vertices_range(vertices(mesh), mesh_vp_map, tr_vertex_pmap, + p::vertex_is_constrained_map(v_selected_map)); for(auto&& edges : patch_edges) { - cdt_impl.insert_constrained_face_defined_by_its_border_edges(edges, mesh_vp_map, tr_vertex_pmap); + auto index = &edges - &patch_edges[0]; + auto face_index_opt = + cdt_impl.insert_constrained_face_defined_by_its_border_edges(edges, mesh_vp_map, tr_vertex_pmap); + for(auto vd : extra_isolated_mesh_vertices[index]) { + auto vh = get(tr_vertex_pmap, vd); + cdt_impl.mark_vertex_as_isolated_in_a_constrained_face(vh, face_index_opt); + } + } } else { cdt_impl.insert_vertices_range(vertices(mesh), mesh_vp_map, tr_vertex_pmap); @@ -1411,14 +1516,21 @@ public: * for bounding box computation (to identify the longest polyline). * @param mesh_vd_to_vh_pmap Property map used to convert mesh vertex descriptors * to triangulation Vertex_handle objects for insertion. + * + * @return An optional Face_index identifying the newly registered constrained face + * (polygon constraint id). If multiple closed polylines are reconstructed (e.g., + * holes), the same Face_index is returned for the whole set. Returns std::nullopt + * if no face can be created (for example, when the input edge set is empty). */ template - void insert_constrained_face_defined_by_its_border_edges(Edges edges, - VertexPointMap mesh_vd_to_point_pmap, - VertexHandleMap mesh_vd_to_vh_pmap) + std::optional + insert_constrained_face_defined_by_its_border_edges(Edges edges, + VertexPointMap mesh_vd_to_point_pmap, + VertexHandleMap mesh_vd_to_vh_pmap) { + std::optional face_index = std::nullopt; if(edges.empty()) - return; + return std::nullopt; auto polylines = segment_soup_to_polylines(std::move(edges)); while(true) { const auto non_closed_polylines_begin = @@ -1467,13 +1579,13 @@ public: } } - std::optional face_index; for(auto& polyline : polylines) { CGAL_assertion(!polyline.empty() && polyline.front() == polyline.back()); polyline.pop_back(); auto range_of_vertices = CGAL::make_transform_range_from_property_map(polyline, mesh_vd_to_vh_pmap); - face_index = insert_constrained_face(range_of_vertices, false, face_index.value_or(-1)); + face_index = insert_constrained_face(range_of_vertices, false, face_index); } + return face_index; } bool is_facet_constrained(Facet f) const { @@ -1525,13 +1637,13 @@ public: template ) Polygon> std::optional - insert_constrained_polygon(const Polygon& polygon, bool restore_Delaunay = true, Face_index face_index = -1) + insert_constrained_polygon(const Polygon& polygon, bool restore_Delaunay = true, std::optional face_index = std::nullopt) { std::vector handles; handles.reserve(polygon.size()); - std::optional hint; + Cell_handle hint{}; for(const auto& p : polygon) { - const auto v = this->insert(p, hint.value_or(Cell_handle{}), restore_Delaunay); + const auto v = this->insert(p, hint, restore_Delaunay); handles.push_back(v); hint = v->cell(); } @@ -1543,8 +1655,14 @@ public: (std::is_convertible_v, Vertex_handle>)) std::optional insert_constrained_face(Vertex_handles&& vertex_handles, bool restore_Delaunay = true, - Face_index face_index = -1) + std::optional face_index = std::nullopt) { + auto may_restore_Delaunay = make_scope_exit([this, do_it=restore_Delaunay]() { + if(do_it) this->restore_Delaunay(); + }); + + restore_Delaunay = false; + if(this->debug_input_faces()) { std::cerr << "insert_constrained_face (" << std::size(vertex_handles) << " vertices):\n"; for(auto v: vertex_handles) { @@ -1556,24 +1674,25 @@ public: const auto first_it = begin(vertex_handles); const auto vend = end(vertex_handles); const auto size = std::distance(first_it, vend); - if(size < 2) return {}; - if(size == 2) { - this->insert_constrained_edge(*first_it, *std::next(first_it)); + if(size < 2 && !face_index.has_value()) return {}; + if(size == 2 && !face_index.has_value()) { + this->insert_constrained_edge(*first_it, *std::next(first_it), restore_Delaunay); return {}; } CGAL::Circulator_from_container> circ{&vertex_handles}; const auto circ_end{circ}; - auto& borders = face_index < 0 ? this->face_borders.emplace_back() : this->face_borders[face_index]; + auto& borders = face_index.has_value() ? this->face_borders[face_index.value()] : this->face_borders.emplace_back(); auto& border = borders.emplace_back(); - const auto polygon_contraint_id = - face_index < 0 ? static_cast(this->face_borders.size() - 1) : face_index; + border.reserve(std::size(vertex_handles)); + const auto polygon_contraint_id = face_index.value_or(this->face_borders.size() - 1); do { const auto va = *circ; ++circ; const auto vb = *circ; const auto c_id = this->constraint_from_extremities(va, vb); if(c_id != Constrained_polyline_id{}) { - const bool constraint_c_id_is_reversed = va != (*this->constraint_hierarchy.vertices_in_constraint_begin(c_id)); + const bool constraint_c_id_is_reversed = + va != (*this->constraint_hierarchy.vertices_in_constraint_begin(c_id)); border.push_back(Face_edge{c_id, constraint_c_id_is_reversed}); constraint_to_faces.emplace(c_id, polygon_contraint_id); } else { @@ -1596,7 +1715,7 @@ public: os << this->point(*first_it) << std::endl; } - if(face_index < 0) { + if(!face_index.has_value()) { const auto accumulated_normal = std::invoke([&] { const auto last_it = std::next(first_it, size - 1); const auto &last_point = tr().point(*last_it); @@ -1627,6 +1746,7 @@ public: face_cdt_2.emplace_back(CDT_2_traits{accumulated_normal}); face_constraint_misses_subfaces.resize(face_cdt_2.size()); + extra_isolated_vertices.resize(face_cdt_2.size()); } if(this->debug_input_faces()) { std::cerr << "insert_constrained_face return the polygon_contraint_id: " << polygon_contraint_id << '\n'; @@ -1672,6 +1792,31 @@ public: return {std::move(steiner_vertices)}; } + /** + * \brief Marks a vertex as isolated within a constrained face. + * + * This function is used to track non-manifold vertices that are isolated + * within a specific patch/face constraint. These vertices don't lie on + * the border of the face but are contained within it. + * + * \param vh The vertex handle to mark as isolated + * \param face_index_opt Optional face index identifying which constrained face contains the isolated vertex + */ + void mark_vertex_as_isolated_in_a_constrained_face(Vertex_handle vh, std::optional face_index_opt) + { + if(!face_index_opt.has_value()) { + return; + } + + vh->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::INPUT_VERTEX); + + const auto face_index = static_cast(face_index_opt.value()); + + extra_isolated_vertices.resize((std::max)(face_index + 1, extra_isolated_vertices.size())); + + extra_isolated_vertices[face_index].insert(vh); + } + private: void set_mark(Vertex_handle v, Vertex_marker m) { @@ -1782,10 +1927,15 @@ private: auto cstr_id = cdt_2.insert_constraint(va, vb); return cstr_id; }; + 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; + return vh_2d; + }; + for(const auto& handles : vec_of_handles) { - const auto first_2d = cdt_2.insert(tr().point(handles.front())); - first_2d->info().vertex_handle_3d = handles.front(); + const auto first_2d = insert_vertex_in_cdt_2(handles.front()); auto previous_2d = first_2d; for(auto it = handles.begin(), end = std::prev(handles.end()); it != end; /* incremented in the loop */) @@ -1812,8 +1962,7 @@ private: --v_end; }; while(vit != v_end) { - auto vh_2d = cdt_2.insert(tr().point(*vit)); - vh_2d->info().vertex_handle_3d = *vit; + auto vh_2d = insert_vertex_in_cdt_2(*vit); insert_constraint_in_cdt_2(previous_2d, vh_2d); previous_2d = vh_2d; if(constraint_c_id_is_reversed) { @@ -1826,10 +1975,8 @@ private: } } - auto vh_2d = it == end ? first_2d : cdt_2.insert(tr().point(vb)); - if(it != end) { - vh_2d->info().vertex_handle_3d = vb; - } + auto vh_2d = it == end ? first_2d : insert_vertex_in_cdt_2(vb); + (void)vh_2d; try { insert_constraint_in_cdt_2(previous_2d, vh_2d); } catch(typename CDT_2::Intersection_of_constraints_exception&) { @@ -1839,6 +1986,9 @@ private: previous_2d = vh_2d; } } + for(auto vh_3d : extra_isolated_vertices[polygon_contraint_id]) { + insert_vertex_in_cdt_2(vh_3d); + } { // mark all the faces outside/inside, starting from one infinite face const bool simple_case = std::all_of(cdt_2.subconstraints_and_contexts_begin(), cdt_2.subconstraints_and_contexts_end(), @@ -4222,7 +4372,7 @@ protected: }; std::vector>> face_borders; boost::container::multimap constraint_to_faces; - + std::vector> extra_isolated_vertices; // boost::dynamic_bitset<> face_constraint_misses_subfaces; // std::size_t face_constraint_misses_subfaces_find_first() const { // return face_constraint_misses_subfaces.find_first(); 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 c052949659c..e1e716be8c8 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, STEINER_ON_EDGE, STEINER_IN_FACE }; +enum class CDT_3_vertex_type { FREE, CORNER, INPUT_VERTEX = CORNER, STEINER_ON_EDGE, STEINER_IN_FACE }; enum class CDT_3_vertex_marker { CLEAR = 0, From b85035ff87d6a8993bf256061d7d936c4963e00b Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 24 Oct 2025 10:26:09 +0200 Subject: [PATCH 28/51] fix warnings --- ...onstrained_Delaunay_triangulation_cell_data_3.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 f687b715ff1..91d9b73044a 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 @@ -52,31 +52,33 @@ class Conforming_constrained_Delaunay_triangulation_cell_data_3 { void clear_mark(CDT_3_cell_marker m) { markers.reset(static_cast(m)); } void clear_marks() { markers.reset(); } + static unsigned int uint(int i) { return static_cast(i); } + template void set_facet_constraint(int i, CDT_3_signed_index face_id, Facet_handle facet_2d) { - this->face_id[unsigned(i)] = face_id; - this->facet_2d[unsigned(i)] = static_cast(facet_2d == Facet_handle{} ? nullptr : std::addressof(*facet_2d)); + this->face_id[uint(i)] = face_id; + this->facet_2d[uint(i)] = static_cast(facet_2d == Facet_handle{} ? nullptr : std::addressof(*facet_2d)); } template auto face_2 (const CDT_2& cdt, int i) const { using Face = typename CDT_2::Face; - auto ptr = static_cast(facet_2d[unsigned(i)]); + auto ptr = static_cast(facet_2d[uint(i)]); return cdt.tds().faces().iterator_to(*ptr); } public: /// @{ // @cond SKIP_IN_MANUAL - bool is_facet_constrained(int i) const { return face_id[unsigned(i)] >= 0; } + bool is_facet_constrained(int i) const { return face_id[uint(i)] >= 0; } CDT_3_signed_index face_constraint_index(int i) const { - return face_id[unsigned(i)]; + return face_id[uint(i)]; } void set_face_constraint_index(int i, CDT_3_signed_index index) { - face_id[unsigned(i)] = index; + face_id[uint(i)] = index; } /// @endcond }; From b068e62ffbe1ffd0d61b8f15d1dad3dc5707053c Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 24 Oct 2025 14:11:17 +0200 Subject: [PATCH 29/51] cleanup of cdt_3_from_off.cpp, and move ITT code to CDT_3 --- .../Conforming_Delaunay_triangulation_3.h | 105 ++++++++++++ .../CMakeLists.txt | 13 -- .../cdt_3_from_off.cpp | 158 +++++++----------- ..._insert_constrained_edge_from_EDG_file.cpp | 3 - ..._insert_constrained_edge_from_OFF_file.cpp | 3 - 5 files changed, 161 insertions(+), 121 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 4133f2844c6..f16b04ce729 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -34,8 +35,112 @@ #ifndef DOXYGEN_RUNNING +#if CGAL_USE_ITT +# include +#endif + namespace CGAL { +namespace CDT_3::internal { + +auto& tasks_manager() { + struct Tasks_manager { + enum { + READ_INPUT = 0, + MERGE_FACETS, + INSERT_VERTICES, + COMPUTE_DISTANCES, + CONFORMING, + CDT, + OUTPUT, + VALIDATION, + NB_TASKS + }; + + #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 = { + __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"), + __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: outputs"), + __itt_string_handle_create("CDT_3: validation") + }; + #endif + std::array timers{}; + struct Scope_guard { + Tasks_manager *instance = nullptr; + int task_id; + Scope_guard(Tasks_manager *instance, int task_id) : instance(instance), task_id(task_id) { + instance->timers[task_id].start(); +#if CGAL_USE_ITT + __itt_task_begin(instance->cdt_3_domain, __itt_null, __itt_null, instance->task_handles[task_id]); +#endif + } + ~Scope_guard() { + instance->timers[task_id].stop(); +#if CGAL_USE_ITT + __itt_task_end(instance->cdt_3_domain); +#endif + } + }; + + Scope_guard make_task_scope_guard(int task_id) { + return Scope_guard(this, 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 OUTPUT_TASK_guard() { return make_task_scope_guard(OUTPUT); } + Scope_guard VALIDATION_TASK_guard() { return make_task_scope_guard(VALIDATION); } + + }; // end struct Intel_OneAPI_ITT_API + + static Tasks_manager instance; + return instance; +} // end auto& tasks_manager() + +} // end namespace CDT_3::internal + +inline auto CDT_3_READ_INPUT_TASK_guard() { + return CDT_3::internal::tasks_manager().READ_INPUT_TASK_guard(); +} + +inline auto CDT_3_MERGE_FACETS_TASK_guard() { + return CDT_3::internal::tasks_manager().MERGE_FACETS_TASK_guard(); +} + +inline auto CDT_3_INSERT_VERTICES_TASK_guard() { + return CDT_3::internal::tasks_manager().INSERT_VERTICES_TASK_guard(); +} + +inline auto CDT_3_COMPUTE_DISTANCES_TASK_guard() { + return CDT_3::internal::tasks_manager().COMPUTE_DISTANCES_TASK_guard(); +} + +inline auto CDT_3_CONFORMING_TASK_guard() { + return CDT_3::internal::tasks_manager().CONFORMING_TASK_guard(); +} + +inline auto CDT_3_CDT_TASK_guard() { + return CDT_3::internal::tasks_manager().CDT_TASK_guard(); +} + +inline auto CDT_3_OUTPUT_TASK_guard() { + return CDT_3::internal::tasks_manager().OUTPUT_TASK_guard(); +} + +inline auto CDT_3_VALIDATION_TASK_guard() { + return CDT_3::internal::tasks_manager().VALIDATION_TASK_guard(); +} + template class Conforming_Delaunay_triangulation_3 : public T_3 { public: diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index b11f6c5d1d9..ac5089404b2 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -29,13 +29,6 @@ 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) create_single_source_cgal_program( "snap_and_cdt3.cpp") -if(cxx_std_20 IN_LIST CMAKE_CXX_COMPILE_FEATURES) - add_executable(cdt_3_from_off_CGAL_DEBUG_CDT_3 cdt_3_from_off) - target_compile_definitions(cdt_3_from_off_CGAL_DEBUG_CDT_3 PRIVATE CGAL_DEBUG_CDT_3=255) - target_link_libraries(cdt_3_from_off_CGAL_DEBUG_CDT_3 PRIVATE CDT_3_dependencies) - cgal_add_test(cdt_3_from_off_CGAL_DEBUG_CDT_3) -endif() - 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_compile_definitions(test_CDT_3_insert_constrained_edge_from_EDG_file PUBLIC CGAL_TEST_CDT_3_USE_CDT) @@ -143,12 +136,6 @@ if (CGAL_CDT_TEST_USE_THINGI) include(./Thingi10k-CDT.cmake) endif() -if(cxx_std_20 IN_LIST CMAKE_CXX_COMPILE_FEATURES) - add_test(NAME "execution of cdt_3_from_off_CGAL_DEBUG_CDT_3 3torus" COMMAND cdt_3_from_off_CGAL_DEBUG_CDT_3 ${CGAL_DATA_DIR}/meshes/3torus.off) - cgal_add_compilation_test(cdt_3_from_off_CGAL_DEBUG_CDT_3) - cgal_setup_test_properties("execution of cdt_3_from_off_CGAL_DEBUG_CDT_3 3torus" cdt_3_from_off_CGAL_DEBUG_CDT_3) -endif() - get_directory_property(all_tests TESTS) foreach(test ${all_tests}) if(test MATCHES cdt|CDT) 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 ac650de2225..5359a58e5ff 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 @@ -2,13 +2,7 @@ #pragma warning(disable: 4455) #endif -#if defined(CGAL_DEBUG_CDT_3) && !__has_include() -#undef CGAL_DEBUG_CDT_3 -#endif - // #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1 -#define NO_TRY_CATCH 1 -// #define CGAL_DEBUG_CDT_3 1 #include #include #include @@ -275,34 +269,6 @@ CDT_options::CDT_options(int argc, char* argv[]) { # define CDT_3_throw_exception_again throw #endif -#if CGAL_USE_ITT -# include -# define CGAL_CDT_3_TASK_BEGIN(task_handle) \ - std::cerr << "START " << #task_handle << '\n'; \ - __itt_task_begin(cdt_3_domain, __itt_null, __itt_null, task_handle); -# define CGAL_CDT_3_TASK_END(task_handle) \ - std::cerr << "-STOP " << #task_handle << '\n'; \ - __itt_task_end(cdt_3_domain); -# define CGAL_CDT_3_TASK_SCOPE(task_handle) \ - CGAL_CDT_3_TASK_BEGIN(task_handle) \ - auto scope_exit = CGAL::make_scope_exit([&]() { CGAL_CDT_3_TASK_END(task_handle); }); - - auto cdt_3_domain = __itt_domain_create("org.cgal.CDT_3"); - auto read_input_task_handle = __itt_string_handle_create("CDT_3: read input file"); - auto merge_facets_task_handle = __itt_string_handle_create("CDT_3: merge facets"); - auto insert_vertices_task_handle = __itt_string_handle_create("CDT_3: insert vertices"); - auto compute_distances_task_handle = __itt_string_handle_create("CDT_3: compute distances"); - auto conforming_task_handle = __itt_string_handle_create("CDT_3: conforming"); - auto cdt_task_handle = __itt_string_handle_create("CDT_3: cdt"); - auto output_task_handle = __itt_string_handle_create("CDT_3: outputs"); - auto validation_task_handle = __itt_string_handle_create("CDT_3: validation"); - -#else // no ITT -# define CGAL_CDT_3_TASK_BEGIN(task_handle) -# define CGAL_CDT_3_TASK_END(task_handle) -# define CGAL_CDT_3_TASK_SCOPE(task_handle) -#endif // no ITT - void configure_cdt_debug_options(CDT& cdt, const CDT_options& options) { cdt.debug_Steiner_points(options.verbose_level > 0); cdt.debug_input_faces(options.debug_input_faces); @@ -343,7 +309,7 @@ auto compute_bounding_box(const Mesh& mesh, const CDT_options& options) { std::function create_output_finalizer(const CDT& cdt, const CDT_options& options) { return [&cdt, &options]() { - CGAL_CDT_3_TASK_SCOPE(output_task_handle); + auto _ = CGAL::CDT_3_OUTPUT_TASK_guard(); { auto dump_tets_to_medit = [](std::string fname, const std::vector &points, @@ -725,8 +691,8 @@ Borders_of_patches maybe_merge_facets( if(!options.merge_facets) return patch_edges; - CGAL_CDT_3_TASK_BEGIN(merge_facets_task_handle); { + auto _ = CGAL::CDT_3_MERGE_FACETS_TASK_guard(); auto start_time = std::chrono::high_resolution_clock::now(); if(options.merge_facets_old_method) { @@ -743,16 +709,14 @@ Borders_of_patches maybe_merge_facets( << std::chrono::duration_cast(timing).count() << " ms\n"; } } - CGAL_CDT_3_TASK_END(merge_facets_task_handle); if(options.dump_patches_after_merge_filename.empty()) return patch_edges; - CGAL_CDT_3_TASK_BEGIN(output_task_handle); { + auto _ = CGAL::CDT_3_OUTPUT_TASK_guard(); std::ofstream out(options.dump_patches_after_merge_filename); CGAL::IO::write_PLY(out, mesh, CGAL::parameters::stream_precision(17)); } - CGAL_CDT_3_TASK_END(output_task_handle); return patch_edges; } @@ -813,15 +777,14 @@ int go(Mesh mesh, CDT_options options) { auto patch_edges = maybe_merge_facets(mesh, options, pmaps, bbox_max_span); if(!options.dump_patches_borders_prefix.empty()) { - CGAL_CDT_3_TASK_BEGIN(output_task_handle); + auto _ = CGAL::CDT_3_OUTPUT_TASK_guard(); dump_patches_borders(patch_edges, pmaps, options.dump_patches_borders_prefix); - CGAL_CDT_3_TASK_END(output_task_handle); } auto mesh_descriptor_to_vertex_handle_pmap = get(CGAL::dynamic_vertex_property_t(), mesh); - CGAL_CDT_3_TASK_BEGIN(insert_vertices_task_handle); { + auto _ = CGAL::CDT_3_INSERT_VERTICES_TASK_guard(); auto start_time = std::chrono::high_resolution_clock::now(); if(options.merge_facets) { cdt.insert_vertices_range(vertices(mesh), pmaps.mesh_vertex_point_map, mesh_descriptor_to_vertex_handle_pmap, @@ -844,10 +807,9 @@ int go(Mesh mesh, CDT_options options) { } cdt.add_bbox_points_if_not_dimension_3(); } - CGAL_CDT_3_TASK_END(insert_vertices_task_handle); - CGAL_CDT_3_TASK_BEGIN(compute_distances_task_handle); { + auto _ = CGAL::CDT_3_COMPUTE_DISTANCES_TASK_guard(); auto start_time = std::chrono::high_resolution_clock::now(); auto exit_code = validate_minimum_vertex_distances(cdt, options.vertex_vertex_epsilon * bbox_max_span, options); @@ -863,10 +825,9 @@ int go(Mesh mesh, CDT_options options) { << std::chrono::duration_cast(timing).count() << " ms\n"; } } - CGAL_CDT_3_TASK_END(compute_distances_task_handle); - CGAL_CDT_3_TASK_BEGIN(conforming_task_handle); { + auto _ = CGAL::CDT_3_CONFORMING_TASK_guard(); int poly_id = 0; auto output_on_exit_scope_guard = CGAL::make_scope_exit(create_output_finalizer(cdt, options)); auto start_time = std::chrono::high_resolution_clock::now(); @@ -904,10 +865,9 @@ int go(Mesh mesh, CDT_options options) { std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; } } - CGAL_CDT_3_TASK_END(conforming_task_handle); if(!options.dump_after_conforming_filename.empty()) { - CGAL_CDT_3_TASK_BEGIN(output_task_handle); + auto _ = CGAL::CDT_3_OUTPUT_TASK_guard(); using Vertex_index = Mesh::Vertex_index; [[maybe_unused]] std::size_t time_stamp_counter = 0u; for(auto v: cdt.finite_vertex_handles()) { @@ -929,23 +889,21 @@ int go(Mesh mesh, CDT_options options) { out_mesh.precision(17); out_mesh << mesh; out_mesh.close(); - CGAL_CDT_3_TASK_END(output_task_handle); } if(!options.quiet) { std::cout << "Number of vertices after conforming: " << cdt.number_of_vertices() << "\n\n"; } - CGAL_CDT_3_TASK_BEGIN(validation_task_handle); { + auto _ = CGAL::CDT_3_VALIDATION_TASK_guard(); CGAL_assertion(!options.call_is_valid || cdt.Base_triantulation::is_valid(true)); CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); } - CGAL_CDT_3_TASK_END(validation_task_handle); - CGAL_CDT_3_TASK_BEGIN(cdt_task_handle); { + auto _ = CGAL::CDT_3_CDT_TASK_guard(); auto start_time = std::chrono::high_resolution_clock::now(); cdt.restore_constrained_Delaunay(); if(!options.quiet) { @@ -954,14 +912,12 @@ int go(Mesh mesh, CDT_options options) { std::cout << "Number of vertices after CDT: " << cdt.number_of_vertices() << "\n\n"; } } - CGAL_CDT_3_TASK_END(cdt_task_handle); - CGAL_CDT_3_TASK_BEGIN(validation_task_handle); { + auto _ = CGAL::CDT_3_VALIDATION_TASK_guard(); CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); } - CGAL_CDT_3_TASK_END(validation_task_handle); return EXIT_SUCCESS; } @@ -1064,56 +1020,54 @@ int main(int argc, char* argv[]) { return 0; } - CGAL_CDT_3_TASK_BEGIN(read_input_task_handle); - auto start_time = std::chrono::high_resolution_clock::now(); - CGAL::CDT_3_read_polygon_mesh_output read_mesh_result; - if(options.read_mesh_with_operator) { - std::ifstream in(options.input_filename); - if(!in) { - std::cerr << "Cannot open file: " << options.input_filename << std::endl; - return EXIT_FAILURE; - } - Mesh mesh; - in >> mesh; - if(!in) { - std::cerr << "Error reading mesh with operator>>" << std::endl; - return EXIT_FAILURE; - } - read_mesh_result.polygon_mesh = std::move(mesh); - } else { - auto read_options = CGAL::parameters::repair_polygon_soup(options.repair_mesh).verbose(options.verbose_level); - read_mesh_result = CGAL::read_polygon_mesh_for_cdt_3(options.input_filename, read_options); - } - - if (!read_mesh_result.polygon_mesh) + auto start_time = std::chrono::high_resolution_clock::now(); { - std::cerr << "Not a valid input file." << std::endl; - std::cerr << "Details:\n" << read_mesh_result.polygon_mesh.error() << std::endl; - return EXIT_FAILURE; - } - Mesh mesh = std::move(*read_mesh_result.polygon_mesh); - if(!options.quiet) { - std::cout << "[timings] read mesh in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - std::cout << "Number of vertices: " << mesh.number_of_vertices() << '\n'; - std::cout << "Number of edges: " << mesh.number_of_edges() << '\n'; - std::cout << "Number of faces: " << mesh.number_of_faces() << "\n\n"; + auto _ = CGAL::CDT_3_READ_INPUT_TASK_guard(); - if(!options.read_mesh_with_operator) { - std::cout << "Processing was successful.\n"; - std::cout << " Number of duplicated points: " << read_mesh_result.nb_of_duplicated_points << '\n'; - std::cout << " Number of simplified polygons: " << read_mesh_result.nb_of_simplified_polygons << '\n'; - std::cout << " Number of new polygons: " << read_mesh_result.nb_of_new_polygons << '\n'; - std::cout << " Number of removed invalid polygons: " << read_mesh_result.nb_of_removed_invalid_polygons << '\n'; - std::cout << " Number of removed duplicated polygons: " << read_mesh_result.nb_of_removed_duplicated_polygons << '\n'; - std::cout << " Number of removed isolated points: " << read_mesh_result.nb_of_removed_isolated_points << '\n'; - std::cout << " Polygon soup self-intersects: " << (read_mesh_result.polygon_soup_self_intersects ? "YES" : "no") << '\n'; - std::cout << " Polygon mesh is manifold: " << (read_mesh_result.polygon_mesh_is_manifold ? "yes" : "NO") << '\n'; - std::cout << std::endl; + if(options.read_mesh_with_operator) { + std::ifstream in(options.input_filename); + if(!in) { + std::cerr << "Cannot open file: " << options.input_filename << std::endl; + return EXIT_FAILURE; + } + in >> *read_mesh_result.polygon_mesh; + if(!in) { + std::cerr << "Error reading mesh with operator>>" << std::endl; + return EXIT_FAILURE; + } + } else { + auto read_options = CGAL::parameters::repair_polygon_soup(options.repair_mesh).verbose(options.verbose_level); + read_mesh_result = CGAL::read_polygon_mesh_for_cdt_3(options.input_filename, read_options); + } + + if (!read_mesh_result.polygon_mesh) + { + std::cerr << "Not a valid input file." << std::endl; + std::cerr << "Details:\n" << read_mesh_result.polygon_mesh.error() << std::endl; + 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 << "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"; + + if(!options.read_mesh_with_operator) { + std::cout << "Processing was successful.\n"; + std::cout << " Number of duplicated points: " << read_mesh_result.nb_of_duplicated_points << '\n'; + std::cout << " Number of simplified polygons: " << read_mesh_result.nb_of_simplified_polygons << '\n'; + std::cout << " Number of new polygons: " << read_mesh_result.nb_of_new_polygons << '\n'; + std::cout << " Number of removed invalid polygons: " << read_mesh_result.nb_of_removed_invalid_polygons << '\n'; + std::cout << " Number of removed duplicated polygons: " << read_mesh_result.nb_of_removed_duplicated_polygons << '\n'; + std::cout << " Number of removed isolated points: " << read_mesh_result.nb_of_removed_isolated_points << '\n'; + std::cout << " Polygon soup self-intersects: " << (read_mesh_result.polygon_soup_self_intersects ? "YES" : "no") << '\n'; + std::cout << " Polygon mesh is manifold: " << (read_mesh_result.polygon_mesh_is_manifold ? "yes" : "NO") << '\n'; + std::cout << std::endl; + } } } - CGAL_CDT_3_TASK_END(read_input_task_handle); if(options.reject_self_intersections && read_mesh_result.polygon_soup_self_intersects) { std::cerr << "ERROR: input mesh self-intersects\n"; @@ -1121,10 +1075,10 @@ int main(int argc, char* argv[]) { } if(!options.failure_assertion_expression.empty()) { - return bisect_errors(std::move(mesh), options); + return bisect_errors(std::move(*read_mesh_result.polygon_mesh), options); } - auto exit_code = go(std::move(mesh), options); + 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"; diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_test_insert_constrained_edge_from_EDG_file.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_test_insert_constrained_edge_from_EDG_file.cpp index 56e38ba2979..fefdaf4749b 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_test_insert_constrained_edge_from_EDG_file.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_test_insert_constrained_edge_from_EDG_file.cpp @@ -1,6 +1,3 @@ -#if __has_include() -#define CGAL_DEBUG_CDT_3 1 -#endif #define CGAL_TRIANGULATION_CHECK_EXPENSIVE 1 #include #include diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_test_insert_constrained_edge_from_OFF_file.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_test_insert_constrained_edge_from_OFF_file.cpp index 7a58a1f5667..3510cf0ea15 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_test_insert_constrained_edge_from_OFF_file.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_test_insert_constrained_edge_from_OFF_file.cpp @@ -1,6 +1,3 @@ -#if __has_include() -#define CGAL_DEBUG_CDT_3 1 -#endif #define CGAL_TRIANGULATION_CHECK_EXPENSIVE 1 #include #include From edbc32959d2476e3925fa9f6d163634dd9b11c28 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Mon, 27 Oct 2025 17:03:34 +0100 Subject: [PATCH 30/51] refactor debug API so that cdt_3_from_off can use the official API --- .clang-format | 82 ++-- .../Conforming_Delaunay_triangulation_3.h | 324 ++++++-------- ...ing_constrained_Delaunay_triangulation_3.h | 423 ++++++++++++++---- .../cdt_3_from_off.cpp | 346 ++------------ 4 files changed, 593 insertions(+), 582 deletions(-) diff --git a/.clang-format b/.clang-format index 369aadbb15f..856d54b4fbc 100644 --- a/.clang-format +++ b/.clang-format @@ -1,35 +1,63 @@ --- +# CGAL clang-format configuration +# This file defines the code formatting style for C++ files in the CGAL project. + Language: Cpp BasedOnStyle: LLVM -AccessModifierOffset: -2 -AllowShortFunctionsOnASingleLine: true -BinPackParameters: false -BreakConstructorInitializers: BeforeComma + +# Indentation +AccessModifierOffset: -2 # Indent public:/private:/protected: 2 spaces to the left + +# Function formatting +AllowShortFunctionsOnASingleLine: Inline # Allow short inline/member functions on one line, but not free functions +AlwaysBreakAfterReturnType: None # Don't force return type on separate line + +# Parameter and argument formatting +BinPackParameters: false # Put each parameter on its own line for better readability + +# Constructor formatting +BreakConstructorInitializers: BeforeComma # Put comma before each initializer: `MyClass() \n , member1(val1) \n , member2(val2)` + +# Brace wrapping configuration BreakBeforeBraces: Custom BraceWrapping: - AfterCaseLabel: false - AfterClass: true - AfterControlStatement: MultiLine - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: true - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyRecord: false - SplitEmptyNamespace: false -ColumnLimit: 120 -# Force pointers to the type for C++. + AfterCaseLabel: false # Don't put brace on new line after case labels + AfterClass: true # Put opening brace on new line after class definition + AfterControlStatement: MultiLine # Only break before braces if the control statement spans multiple lines + AfterEnum: false # Don't break after enum + AfterFunction: false # Don't break after function declaration (keep on same line) + AfterNamespace: false # Don't break after namespace + AfterObjCDeclaration: false # Objective-C related (not used in CGAL) + AfterStruct: true # Put opening brace on new line after struct definition + AfterUnion: false # Don't break after union + AfterExternBlock: false # Don't break after extern "C" blocks + BeforeCatch: false # Don't put catch on new line + BeforeElse: false # Don't put else on new line + BeforeLambdaBody: false # Don't break before lambda body + BeforeWhile: false # Don't put while on new line (do-while loops) + IndentBraces: false # Don't indent the braces themselves + SplitEmptyFunction: false # Don't split empty functions across lines + SplitEmptyRecord: false # Don't split empty classes/structs across lines + SplitEmptyNamespace: false # Don't split empty namespaces across lines + +# Line length +ColumnLimit: 120 # Maximum line length of 120 characters + +# Pointer and reference alignment +# Force pointers and references to align with the type (e.g., `int* ptr` not `int *ptr`) DerivePointerAlignment: false PointerAlignment: Left -# Control the spaces around conditionals -SpacesInConditionalStatement: false -SpaceBeforeParens: false + +# Spacing in control statements +SpacesInConditionalStatement: false # No extra spaces inside conditionals: `if(condition)` not `if( condition )` +SpaceBeforeParens: false # No space before parentheses: `if(` not `if (` + +# Include directive handling +SortIncludes: Never # Preserve the original order of #include statements (don't sort them) + +# Preprocessor directive formatting +IndentPPDirectives: None # Don't indent preprocessor directives (#ifdef, #include, etc.) + +# Blank line handling +MaxEmptyLinesToKeep: 2 # Keep up to 2 consecutive blank lines ... 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 f16b04ce729..f047cff574d 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -41,7 +41,96 @@ namespace CGAL { -namespace CDT_3::internal { +namespace CDT_3 { + +struct Debug_options { + enum class Flags { + Steiner_points = 0, + conforming, + input_faces, + missing_region, + regions, + copy_triangulation_into_hole, + validity, + use_older_cavity_algorithm, + debug_finite_edges_map, + use_finite_edges_map, + debug_subconstraints_to_conform, + verbose_special_cases, + debug_encroaching_vertices, + debug_conforming_validation, + debug_constraint_hierarchy, + debug_geometric_errors, + debug_polygon_insertion, + display_statistics, + nb_of_flags + }; + + bool Steiner_points() const { return flags[static_cast(Flags::Steiner_points)]; } + void Steiner_points(bool b) { flags.set(static_cast(Flags::Steiner_points), b); } + + 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 missing_region() const { return flags[static_cast(Flags::missing_region)]; } + void missing_region(bool b) { flags.set(static_cast(Flags::missing_region), b); } + + bool regions() const { return flags[static_cast(Flags::regions)]; } + void regions(bool b) { flags.set(static_cast(Flags::regions), 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 validity() const { return flags[static_cast(Flags::validity)]; } + void validity(bool b) { flags.set(static_cast(Flags::validity), b); } + + bool use_older_cavity_algorithm() const { return flags[static_cast(Flags::use_older_cavity_algorithm)]; } + bool use_newer_cavity_algorithm() const { return !flags[static_cast(Flags::use_older_cavity_algorithm)]; } + void use_older_cavity_algorithm(bool b) { flags.set(static_cast(Flags::use_older_cavity_algorithm), 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 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 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 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 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 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 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 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 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 display_statistics() const { return flags[static_cast(Flags::display_statistics)]; } + void display_statistics(bool b) { flags.set(static_cast(Flags::display_statistics), b); } + + double segment_vertex_epsilon() const { return segment_vertex_epsilon_; } + void set_segment_vertex_epsilon(double eps) { segment_vertex_epsilon_ = eps; } + + double vertex_vertex_epsilon() const { return vertex_vertex_epsilon_; } + void set_vertex_vertex_epsilon(double eps) { vertex_vertex_epsilon_ = eps; } + +private: + std::bitset(Flags::nb_of_flags)> flags{}; + double segment_vertex_epsilon_ = 0.0; + double vertex_vertex_epsilon_ = 0.0; +}; // end struct Debug_options + +namespace internal { auto& tasks_manager() { struct Tasks_manager { @@ -80,6 +169,12 @@ auto& tasks_manager() { __itt_task_begin(instance->cdt_3_domain, __itt_null, __itt_null, instance->task_handles[task_id]); #endif } + auto time() const { + return instance->timers[task_id].time(); + } + auto time_ms() const { + return instance->timers[task_id].time() / 1000.; + } ~Scope_guard() { instance->timers[task_id].stop(); #if CGAL_USE_ITT @@ -107,7 +202,9 @@ auto& tasks_manager() { return instance; } // end auto& tasks_manager() -} // end namespace CDT_3::internal +} // end namespace internal + +} // end namespace CDT_3 inline auto CDT_3_READ_INPUT_TASK_guard() { return CDT_3::internal::tasks_manager().READ_INPUT_TASK_guard(); @@ -155,9 +252,9 @@ public: using Line = typename T_3::Geom_traits::Line_3; using Locate_type = typename T_3::Locate_type; - inline static With_offset_tag with_offset{}; - inline static With_point_tag with_point{}; - inline static With_point_and_info_tag with_point_and_info{}; + 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}; Conforming_Delaunay_triangulation_3(const Geom_traits& gt = Geom_traits()) : T_3(gt) @@ -215,7 +312,7 @@ protected: if(v1 > v2) std::swap(v1, v2); auto v1_index = v1->time_stamp(); [[maybe_unused]] auto nb_erased = self->all_finite_edges[v1_index].erase(v2); - if constexpr (cdt_3_can_use_cxx20_format()) if(self->debug_finite_edges_map() && nb_erased > 0) { + if constexpr (cdt_3_can_use_cxx20_format()) if(self->debug().finite_edges_map() && nb_erased > 0) { std::cerr << cdt_3_format("erasing edge {} {}\n", self->display_vert((std::min)(v1, v2)), self->display_vert((std::max)(v1, v2))); } @@ -274,7 +371,7 @@ protected: void add_to_subconstraints_to_conform(Vertex_handle va, Vertex_handle vb, Constrained_polyline_id id) { const auto pair = make_subconstraint(va, vb); - if(debug_subconstraints_to_conform()) { + if(debug().subconstraints_to_conform()) { std::cerr << "tr().subconstraints_to_conform.push(" << display_subcstr(pair) << ")\n"; } @@ -321,7 +418,7 @@ protected: if(tr().is_infinite(v1) || tr().is_infinite(v2)) return; [[maybe_unused]] auto [_, inserted] = all_finite_edges[v1->time_stamp()].insert(v2); - if constexpr (cdt_3_can_use_cxx20_format()) if (debug_finite_edges_map() && inserted) { + if constexpr (cdt_3_can_use_cxx20_format()) if (debug().finite_edges_map() && inserted) { if(v2 < v1) std::swap(v1, v2); std::cerr << cdt_3_format("new_edge({}, {})\n", display_vert(v1), display_vert(v2)); } @@ -372,7 +469,7 @@ protected: if(use_finite_edges_map()) { new_vertex(v); all_finite_edges.clear(); - if (debug_finite_edges_map()) std::cerr << "all_finite_edges.clear()\n"; + if (debug().finite_edges_map()) std::cerr << "all_finite_edges.clear()\n"; for(auto e: tr().all_edges()) { new_edge(e); } @@ -408,7 +505,7 @@ protected: } } - void update_max_bbox_edge_length() { + void update_max_bbox_edge_length() const { double d_x = bbox.xmax() - bbox.xmin(); double d_y = bbox.ymax() - bbox.ymin(); double d_z = bbox.zmax() - bbox.zmin(); @@ -421,137 +518,15 @@ public: segment_vertex_epsilon = epsilon; } - bool debug_Steiner_points() const { - return debug_flags[static_cast(Debug_flags::Steiner_points)]; - } + CDT_3::Debug_options& debug() { return debug_options_; } + const CDT_3::Debug_options& debug() const { return debug_options_; } - void debug_Steiner_points(bool b) { - debug_flags.set(static_cast(Debug_flags::Steiner_points), b); - } - - bool debug_input_faces() const { - return debug_flags[static_cast(Debug_flags::input_faces)]; - } - - void debug_input_faces(bool b) { - debug_flags.set(static_cast(Debug_flags::input_faces), b); - } - - bool debug_missing_region() const { - return debug_flags[static_cast(Debug_flags::missing_region)]; - } - - void debug_missing_region(bool b) { - debug_flags.set(static_cast(Debug_flags::missing_region), b); - } - - bool debug_regions() const { - return debug_flags[static_cast(Debug_flags::regions)]; - } - - void debug_regions(bool b) { - debug_flags.set(static_cast(Debug_flags::regions), b); - } - - bool debug_copy_triangulation_into_hole() const { - return debug_flags[static_cast(Debug_flags::copy_triangulation_into_hole)]; - } - - void debug_copy_triangulation_into_hole(bool b) { - debug_flags.set(static_cast(Debug_flags::copy_triangulation_into_hole), b); - } - - bool debug_validity() const { - return debug_flags[static_cast(Debug_flags::validity)]; - } - - void debug_validity(bool b) { - debug_flags.set(static_cast(Debug_flags::validity), b); - } - - bool use_older_cavity_algorithm() const { - return debug_flags[static_cast(Debug_flags::use_older_cavity_algorithm)]; - } - - bool use_newer_cavity_algorithm() const { - return !debug_flags[static_cast(Debug_flags::use_older_cavity_algorithm)]; - } - - void use_older_cavity_algorithm(bool b) { - debug_flags.set(static_cast(Debug_flags::use_older_cavity_algorithm), b); - } - - bool debug_finite_edges_map() const { - return debug_flags[static_cast(Debug_flags::debug_finite_edges_map)]; - } - - void debug_finite_edges_map(bool b) { - debug_flags.set(static_cast(Debug_flags::debug_finite_edges_map), b); - } - - bool debug_subconstraints_to_conform() const { - return debug_flags[static_cast(Debug_flags::debug_subconstraints_to_conform)]; - } - - void debug_subconstraints_to_conform(bool b) { - debug_flags.set(static_cast(Debug_flags::debug_subconstraints_to_conform), b); - } - - bool use_finite_edges_map() const { - return update_all_finite_edges_ && debug_flags[static_cast(Debug_flags::use_finite_edges_map)]; - } - - void use_finite_edges_map(bool b) { - debug_flags.set(static_cast(Debug_flags::use_finite_edges_map), b); - } - - bool debug_verbose_special_cases() const { - return debug_flags[static_cast(Debug_flags::verbose_special_cases)]; - } - - void debug_verbose_special_cases(bool b) { - debug_flags.set(static_cast(Debug_flags::verbose_special_cases), b); - } - - bool debug_encroaching_vertices() const { - return debug_flags[static_cast(Debug_flags::debug_encroaching_vertices)]; - } - - void debug_encroaching_vertices(bool b) { - debug_flags.set(static_cast(Debug_flags::debug_encroaching_vertices), b); - } - - bool debug_conforming_validation() const { - return debug_flags[static_cast(Debug_flags::debug_conforming_validation)]; - } - - void debug_conforming_validation(bool b) { - debug_flags.set(static_cast(Debug_flags::debug_conforming_validation), b); - } - - bool debug_constraint_hierarchy() const { - return debug_flags[static_cast(Debug_flags::debug_constraint_hierarchy)]; - } - - void debug_constraint_hierarchy(bool b) { - debug_flags.set(static_cast(Debug_flags::debug_constraint_hierarchy), b); - } - - bool debug_geometric_errors() const { - return debug_flags[static_cast(Debug_flags::debug_geometric_errors)]; - } - - void debug_geometric_errors(bool b) { - debug_flags.set(static_cast(Debug_flags::debug_geometric_errors), b); - } - - bool debug_polygon_insertion() const { - return debug_flags[static_cast(Debug_flags::debug_polygon_insertion)]; - } - - void debug_polygon_insertion(bool b) { - debug_flags.set(static_cast(Debug_flags::debug_polygon_insertion), b); - } + // Backward compatibility wrappers (deprecated, use debug().method() instead) + bool use_older_cavity_algorithm() const { return debug_options_.use_older_cavity_algorithm(); } + bool use_newer_cavity_algorithm() const { return debug_options_.use_newer_cavity_algorithm(); } + void use_older_cavity_algorithm(bool b) { debug_options_.use_older_cavity_algorithm(b); } + bool use_finite_edges_map() const { return update_all_finite_edges_ && debug_options_.use_finite_edges_map_flag(); } + void use_finite_edges_map(bool b) { debug_options_.use_finite_edges_map(b); } Vertex_handle insert(const Point &p, Locate_type lt, Cell_handle c, int li, int lj) @@ -579,14 +554,14 @@ public: bool is_edge(Vertex_handle va, Vertex_handle vb) const { const bool is_edge_v1 = - ((debug_finite_edges_map() && use_finite_edges_map()) || !use_finite_edges_map()) && tr().tds().is_edge(va, vb); + ((debug().finite_edges_map() && use_finite_edges_map()) || !use_finite_edges_map()) && tr().tds().is_edge(va, vb); if(use_finite_edges_map() && va > vb) std::swap(va, vb); const auto va_index = va->time_stamp(); const bool is_edge_v2 = use_finite_edges_map() && all_finite_edges[va_index].find(vb) != all_finite_edges[va_index].end(); - if(debug_finite_edges_map() && use_finite_edges_map() && is_edge_v1 != is_edge_v2) { + if(debug().finite_edges_map() && use_finite_edges_map() && is_edge_v1 != is_edge_v2) { std::cerr << "!! Inconsistent edge status\n"; std::cerr << " -> constraint " << display_vert(va) << " " << display_vert(vb) << '\n'; std::cerr << " -> edge " << (is_edge_v1 ? "is" : "is not") << " in the triangulation\n"; @@ -606,7 +581,7 @@ public: [this](const auto &sc) { const auto [va, vb] = sc; const auto is_edge = this->is_edge(va, vb); - if constexpr (cdt_3_can_use_cxx20_format()) if(debug_conforming_validation()) { + 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), @@ -622,7 +597,7 @@ public: Vertex_handle vb, Vertex_handle min_vertex, double min_dist, - Check_distance distance_type) + Check_distance distance_type = Check_distance::NON_SQUARED_DISTANCE) const { if(!max_bbox_edge_length) { update_max_bbox_edge_length(); @@ -643,6 +618,26 @@ public: } } + void check_vertex_vertex_distance_or_throw(Vertex_handle va, + Vertex_handle vb, + double min_dist) const + { + if(!max_bbox_edge_length) { + update_max_bbox_edge_length(); + } + if(min_dist < debug_options_.vertex_vertex_epsilon() * *max_bbox_edge_length) + { + std::stringstream ss; + ss.precision(std::cerr.precision()); + ss << "Two vertices are too close to each other.\n"; + ss << " -> vertex " << display_vert(va) << '\n'; + ss << " -> vertex " << display_vert(vb) << '\n'; + ss << " -> distance = " << min_dist << '\n'; + ss << " -> max_bbox_edge_length = " << *max_bbox_edge_length << '\n'; + CGAL_error_msg(ss.str().c_str()); + } + } + auto ancestors_of_Steiner_vertex_on_edge(Vertex_handle v) const { std::pair result; CGAL_precondition(v->ccdt_3_data().is_Steiner_vertex_on_edge()); @@ -748,7 +743,7 @@ protected: if(!constraint_hierarchy.is_subconstraint(va, vb)) { continue; } - if(debug_subconstraints_to_conform()) { + if(debug().subconstraints_to_conform()) { std::cerr << "tr().subconstraints_to_conform.pop()=" << display_subcstr(subconstraint) << "\n"; } @@ -794,7 +789,7 @@ protected: 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()) { + 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 << ": [ " << display_vert(c_start) << " - " << display_vert(c_end) << " ] "; @@ -830,7 +825,7 @@ protected: 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)); - if(debug_constraint_hierarchy()) { + if(debug().constraint_hierarchy()) { std::cerr << "constraint " << static_cast(c_id.vl_ptr()) << " has " << c_id.vl_ptr()->skip_size() << " vertices\n"; } @@ -891,7 +886,7 @@ protected: encroaching_vertices.insert(v); }; auto fill_encroaching_vertices = [&](const auto simplex) { - if(debug_encroaching_vertices()) { + if(debug().encroaching_vertices()) { std::cerr << " - " << IO::oformat(simplex, With_point_tag{}) << '\n'; } auto visit_cell = [&](Cell_handle cell) { @@ -937,7 +932,7 @@ protected: std::cerr << "!! The constraint passes through a vertex!\n"; std::cerr << " -> constraint " << display_vert(va) << " " << display_vert(vb) << '\n'; std::cerr << " -> vertex " << display_vert(v) << '\n'; - if(debug_geometric_errors()) { + if(debug().geometric_errors()) { debug_dump("bug-through-vertex"); } CGAL_error(); @@ -949,7 +944,7 @@ protected: std::for_each(tr().segment_traverser_simplices_begin(va, vb), tr().segment_traverser_simplices_end(), fill_encroaching_vertices); auto vector_of_encroaching_vertices = encroaching_vertices.extract_sequence(); - if(debug_encroaching_vertices()) { + if(debug().encroaching_vertices()) { std::cerr << " -> vector_of_encroaching_vertices (before filter):\n"; std::for_each(vector_of_encroaching_vertices.begin(), vector_of_encroaching_vertices.end(), @@ -965,7 +960,7 @@ protected: this->tr().point(v), pb) == ACUTE; }); - if(debug_encroaching_vertices()) { + if(debug().encroaching_vertices()) { std::cerr << " -> vector_of_encroaching_vertices (after filter):\n"; std::for_each(vector_of_encroaching_vertices.begin(), end, [&](Vertex_handle v) { std::cerr << " " << this->display_vert(v) << " angle " << approximate_angle(pa, this->tr().point(v), pb) @@ -1001,7 +996,7 @@ protected: return {midpoint_functor(pa, pb), va->cell(), va}; } - if(debug_encroaching_vertices()) { + if(debug().encroaching_vertices()) { std::cerr << "construct_Steiner_point( " << display_vert(va) << " , " << display_vert(vb) << " )\n"; } @@ -1102,7 +1097,7 @@ protected: static_assert(CGAL::cdt_3_msvc_2019_or_older() || CGAL::is_nothrow_movable_v); Bbox_3 bbox{}; double segment_vertex_epsilon = 1e-8; - std::optional max_bbox_edge_length; + mutable std::optional max_bbox_edge_length; using Pair_of_vertex_handles = std::pair; boost::container::map pair_of_vertices_to_cid; Insert_in_conflict_visitor insert_in_conflict_visitor = {this}; @@ -1134,27 +1129,7 @@ protected: } } - enum class Debug_flags { - Steiner_points = 0, - conforming, - input_faces, - missing_region, - regions, - copy_triangulation_into_hole, - validity, - use_older_cavity_algorithm, - debug_finite_edges_map, - use_finite_edges_map, - debug_subconstraints_to_conform, - verbose_special_cases, - debug_encroaching_vertices, - debug_conforming_validation, - debug_constraint_hierarchy, - debug_geometric_errors, - debug_polygon_insertion, - nb_of_flags - }; - std::bitset(Debug_flags::nb_of_flags)> debug_flags{}; + CDT_3::Debug_options debug_options_{}; bool is_Delaunay = true; }; @@ -1162,6 +1137,5 @@ protected: #endif // not DOXYGEN_RUNNING -# #endif // CGAL_CONFORMING_DELAUNAY_TRIANGULATION_3_H 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 26e6444e0e5..fea4d208113 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 @@ -9,8 +9,8 @@ // // Author(s) : Laurent Rineau -#ifndef CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H -#define CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H +#ifndef CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H +#define CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H #include @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -578,6 +580,9 @@ public: #endif // not DOXYGEN_RUNNING using size_type = typename Triangulation::size_type; + /// \cond SKIP_IN_MANUAL + CDT_3_impl& impl() { return cdt_impl; } + /// /endcond public: /** \name Constructors @{ @@ -647,6 +652,7 @@ public: auto mesh_vp_map = choose_parameter(get_parameter(np, internal_np::vertex_point), get_const_property_map(vertex_point, mesh)); + this->debug() = choose_parameter(get_parameter(np, internal_np::debug), CDT_3::Debug_options{}); using graph_traits = boost::graph_traits; using vertex_descriptor = typename graph_traits::vertex_descriptor; @@ -779,27 +785,57 @@ public: cdt_impl.insert_vertices_range(vertices(mesh), mesh_vp_map, tr_vertex_pmap, p::vertex_is_constrained_map(v_selected_map)); - for(auto&& edges : patch_edges) { - auto index = &edges - &patch_edges[0]; - auto face_index_opt = - cdt_impl.insert_constrained_face_defined_by_its_border_edges(edges, mesh_vp_map, tr_vertex_pmap); - for(auto vd : extra_isolated_mesh_vertices[index]) { - auto vh = get(tr_vertex_pmap, vd); - cdt_impl.mark_vertex_as_isolated_in_a_constrained_face(vh, face_index_opt); - } + cdt_impl.template validate_distances_and_report(mesh, patch_edges, tr_vertex_pmap); + + { + auto task_guard = CGAL::CDT_3_CONFORMING_TASK_guard(); + for(auto&& edges : patch_edges) { + auto index = &edges - &patch_edges[0]; + auto face_index_opt = + cdt_impl.insert_constrained_face_defined_by_its_border_edges(edges, mesh_vp_map, tr_vertex_pmap); + for(auto vd : extra_isolated_mesh_vertices[index]) { + auto vh = get(tr_vertex_pmap, vd); + cdt_impl.mark_vertex_as_isolated_in_a_constrained_face(vh, face_index_opt); + } + } } } else { cdt_impl.insert_vertices_range(vertices(mesh), mesh_vp_map, tr_vertex_pmap); - for(auto f : faces(mesh)) { - auto face_vertices = vertices_around_face(halfedge(f, mesh), mesh); - auto range_of_vertices = CGAL::make_transform_range_from_property_map(face_vertices, tr_vertex_pmap); - cdt_impl.insert_constrained_face(range_of_vertices, false); + cdt_impl.template validate_distances_and_report(mesh, patch_edges, tr_vertex_pmap); + { + auto task_guard = CGAL::CDT_3_CONFORMING_TASK_guard(); + + for(auto f : faces(mesh)) { + auto face_vertices = vertices_around_face(halfedge(f, mesh), mesh); + auto range_of_vertices = CGAL::make_transform_range_from_property_map(face_vertices, tr_vertex_pmap); + cdt_impl.insert_constrained_face(range_of_vertices, false); + } + } + } + + { + auto task_guard = CGAL::CDT_3_CONFORMING_TASK_guard(); + cdt_impl.restore_Delaunay(); + if(cdt_impl.debug().display_statistics()) { + std::cout << "[timings] restored Delaunay in " << task_guard.time_ms() << " ms\n"; + std::cout << "Number of vertices after Delaunay: " << cdt_impl.number_of_vertices() << "\n"; + } + } + + const auto& visitor = parameters::get_parameter(np, internal_np::visitor); + if constexpr (!std::is_same_v, internal_np::Param_not_found>) { + visitor(*this); + } + { + auto task_guard = CGAL::CDT_3_CDT_TASK_guard(); + cdt_impl.restore_constrained_Delaunay(); + if(cdt_impl.debug().display_statistics()) { + std::cout << "[timings] restored constrained Delaunay in " << task_guard.time_ms() << " ms\n"; + std::cout << "Number of vertices after CDT: " << cdt_impl.number_of_vertices() << "\n\n"; } } - cdt_impl.restore_Delaunay(); - cdt_impl.restore_constrained_Delaunay(); // std::cerr << cdt_3_format("cdt_impl: {} vertices, {} cells\n", cdt_impl.number_of_vertices(), // cdt_impl.number_of_cells()); } @@ -1042,15 +1078,58 @@ public: /// \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); } - /// @} + + // ----------------------- + // Debug and advanced control + // ----------------------- +public: + /** + * \brief Access debug options. + * + * Returns a reference to the debug options object that controls various debugging + * and algorithmic behaviors. Use this to configure debug flags: + * + * \code + * ccdt.debug().Steiner_points(true); + * ccdt.debug().input_faces(true); + * \endcode + */ + CDT_3::Debug_options& debug() { return cdt_impl.debug(); } + const CDT_3::Debug_options& debug() const { return cdt_impl.debug(); } + + // Algorithmic switches and tolerances + void use_older_cavity_algorithm(bool b) { cdt_impl.use_older_cavity_algorithm(b); } + void use_finite_edges_map(bool b) { cdt_impl.use_finite_edges_map(b); } + void set_segment_vertex_epsilon(double eps) { cdt_impl.set_segment_vertex_epsilon(eps); } + + // 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); } + void write_all_segments_file(std::ostream& out) { cdt_impl.write_all_segments_file(out); } + + // 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(); } + + // Ranges and utilities for post-processing (e.g., dumping Steiner vertices) + auto finite_vertex_handles() const { return cdt_impl.finite_vertex_handles(); } + auto finite_cell_handles() const { return triangulation().finite_cell_handles(); } + auto all_cell_handles() const { return triangulation().all_cell_handles(); } + auto finite_facets() const { return triangulation().finite_facets(); } + typename Triangulation::Cell_handle infinite_cell() const { return triangulation().infinite_cell(); } + + auto ancestors_of_Steiner_vertex_on_edge(Vertex_handle v) const + { return cdt_impl.ancestors_of_Steiner_vertex_on_edge(v); } + + // Write facets (for debugging/output) + template + void write_facets(std::ostream& out, const CDT& cdt, FacetRange&& facets) const { + cdt_impl.write_facets(out, cdt, std::forward(facets)); + } }; #ifndef DOXYGEN_RUNNING @@ -1423,7 +1502,7 @@ public: this->new_vertex(p_vh); - CGAL_assume(!this->debug_validity() || this->is_valid(true)); + CGAL_assume(!this->debug().validity() || this->is_valid(true)); return p_vh; } @@ -1471,6 +1550,8 @@ public: VertexHandleMap tr_vertex_pmap, const NamedParameters& np = parameters::default_values()) { + auto task_guard = CGAL::CDT_3_INSERT_VERTICES_TASK_guard(); + using parameters::get_parameter; using parameters::is_default_parameter; @@ -1495,6 +1576,13 @@ public: } add_bbox_points_if_not_dimension_3(); + + if(this->debug().display_statistics()) { + + std::cout << "[timings] inserted vertices in " + << task_guard.time_ms() << " ms\n"; + std::cout << "Number of vertices: " << this->number_of_vertices() << "\n\n"; + } } /** @@ -1663,7 +1751,7 @@ public: restore_Delaunay = false; - if(this->debug_input_faces()) { + if(this->debug().input_faces()) { std::cerr << "insert_constrained_face (" << std::size(vertex_handles) << " vertices):\n"; for(auto v: vertex_handles) { std::cerr << " - " << this->display_vert(v) << '\n'; @@ -1703,7 +1791,7 @@ public: } } while(circ != circ_end); - if(this->debug_input_faces()) { + if(this->debug().input_faces()) { std::stringstream filename; filename << "dump-input-face-" << polygon_contraint_id << "_polygon_" << borders.size() - 1 << ".polylines.txt"; std::ofstream os(filename.str()); @@ -1748,7 +1836,7 @@ public: face_constraint_misses_subfaces.resize(face_cdt_2.size()); extra_isolated_vertices.resize(face_cdt_2.size()); } - if(this->debug_input_faces()) { + if(this->debug().input_faces()) { std::cerr << "insert_constrained_face return the polygon_contraint_id: " << polygon_contraint_id << '\n'; } return polygon_contraint_id; @@ -1817,10 +1905,181 @@ public: extra_isolated_vertices[face_index].insert(vh); } + + struct Min_distance_result { + double min_distance; + std::array vertices_of_min_edge; + }; + + Min_distance_result compute_minimum_vertex_distance() const { + const auto& tr = *this; +#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L + auto [min_sq_distance, min_edge] = + (std::ranges::min)(tr.finite_edges() | std::views::transform([&](auto edge) { + return std::make_pair(tr.segment(edge).squared_length(), edge); + })); +#else + auto transform_fct = [&](auto edge) { return std::make_pair(tr.segment(edge).squared_length(), edge); }; + auto min_p = transform_fct(*tr.finite_edges_begin()); + for (auto ite=tr.finite_edges_begin(); ite!=tr.finite_edges_end(); ++ite) + { + auto p = transform_fct(*ite); + if (p < min_p) + p = min_p; + } + auto [min_sq_distance, min_edge] = min_p; +#endif + auto min_distance = CGAL::to_double(CGAL::approximate_sqrt(min_sq_distance)); + auto vertices_of_min_edge = this->vertices(min_edge); + + return {min_distance, vertices_of_min_edge}; + } + + 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"; + } + + struct Constraint_distance_result { + double min_distance; + Vertex_handle min_va, min_vb, min_vertex; + }; + + template + Constraint_distance_result + compute_constraint_vertex_distances_from_patches_borders(const BordersOfPatches& patch_edges, + const VertexPointMap& tr_vertex_pmap) const + { + +#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L + auto edge_results = patch_edges + | std::views::join + | std::views::transform([&](const auto& edge_pair) { + auto [vda, vdb] = edge_pair; + auto va = get(tr_vertex_pmap, vda); + auto vb = get(tr_vertex_pmap, vdb); + auto [min_dist, min_v] = this->min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); + return std::make_tuple(CGAL::to_double(min_dist), va, vb, min_v); + }); + + auto min_result = std::ranges::min_element(edge_results, {}, [](const auto& tuple) { + return std::get<0>(tuple); + }); + + auto [min_distance, min_va, min_vb, min_vertex] = *min_result; +#else + double min_distance = (std::numeric_limits::max)(); + Vertex_handle min_va, min_vb, min_vertex; + + for(const auto& edges : patch_edges) { + for(auto [vda, vdb]: edges) { + auto va = get(tr_vertex_pmap, vda); + auto vb = get(tr_vertex_pmap, vdb); + auto [min_dist, min_v] = this->min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); + if(min_dist < min_distance) { + min_distance = CGAL::to_double(min_dist); + min_va = va; + min_vb = vb; + min_vertex = min_v; + } + } + } +#endif + + return {min_distance, min_va, min_vb, min_vertex}; + } + + template + Constraint_distance_result + compute_constraint_vertex_distances_from_faces(const Mesh& mesh, const VertexPointMap& tr_vertex_pmap) const { + double min_distance = (std::numeric_limits::max)(); + Vertex_handle min_va, min_vb, min_vertex; + + for(auto face_descriptor : faces(mesh)) { + auto he = halfedge(face_descriptor, mesh); + const auto end = he; + do { + auto va = get(tr_vertex_pmap, source(he, mesh)); + auto vb = get(tr_vertex_pmap, target(he, mesh)); + auto [min_dist, min_v] = this->min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); + if(min_dist < min_distance) { + min_distance = CGAL::to_double(min_dist); + min_va = va; + min_vb = vb; + min_vertex = min_v; + } + he = next(he, mesh); + } while((he = next(he, mesh)) != end); + } + + return {min_distance, min_va, min_vb, min_vertex}; + } + + template + void validate_constraint_vertex_distances_or_throw(const Mesh& mesh, + const BordersOfPatches& patch_edges, + const VertexPointMap& tr_vertex_pmap) + { + + auto [min_distance, min_va, min_vb, min_vertex] = + merged_facets ? compute_constraint_vertex_distances_from_patches_borders(patch_edges, tr_vertex_pmap) + : compute_constraint_vertex_distances_from_faces(mesh, tr_vertex_pmap); + + 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' + << " and vertex : " + << CGAL::IO::oformat(min_vertex, CGAL::With_point_tag{}) << "\n\n"; + } + this->check_segment_vertex_distance_or_throw(min_va, min_vb, min_vertex, min_distance); + } + + template + void validate_vertex_vertex_distances_or_throw([[maybe_unused]] const Mesh& mesh, + [[maybe_unused]] const BordersOfPatches& patch_edges, + [[maybe_unused]] const VertexPointMap& tr_vertex_pmap) + { + + auto result = this->compute_minimum_vertex_distance(); + + 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"; + } + this->check_vertex_vertex_distance_or_throw(result.vertices_of_min_edge[0], + result.vertices_of_min_edge[1], + result.min_distance); + } + + template + void validate_distances_and_report(const Mesh& mesh, + const BordersOfPatches& patch_edges, + const VertexPointMap& tr_vertex_pmap) + { + auto task_guard = CGAL::CDT_3_COMPUTE_DISTANCES_TASK_guard(); + if(this->debug().segment_vertex_epsilon() > 0) { + this->validate_constraint_vertex_distances_or_throw(mesh, patch_edges, tr_vertex_pmap); + } + if(this->debug().vertex_vertex_epsilon() > 0) { + this->validate_vertex_vertex_distances_or_throw(mesh, patch_edges, tr_vertex_pmap); + } + if(this->debug().display_statistics()) { + std::cout << "[timings] compute distances in " + << task_guard.time_ms() << " ms\n"; + } + } + + private: void set_mark(Vertex_handle v, Vertex_marker m) { - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << " set_mark(" << this->display_vert(v) << ", "; switch(m) { case Vertex_marker::CLEAR: std::cerr << "CLEAR"; break; @@ -1900,7 +2159,7 @@ private: return vec_of_handles; }); - if(this->debug_input_faces()) { + if(this->debug().input_faces()) { std::cerr << "Polygon #" << polygon_contraint_id << " normal is: " << cdt_2.geom_traits().normal() << '\n'; auto filename = "dump_cdt_2_polygons_" + std::to_string(polygon_contraint_id) + ".polylines.txt"; std::cerr << " dumping it to \"" << filename << "\".\n"; @@ -1917,7 +2176,7 @@ private: // create and fill the 2D triangulation { auto insert_constraint_in_cdt_2 = [&](const auto& va, const auto& vb) { - if(this->debug_input_faces()) { + if(this->debug().input_faces()) { std::cerr << "cdt_2.insert_constraint (" << tr().point(va->info().vertex_handle_3d) << " , " @@ -2032,7 +2291,7 @@ private: } } } // end of marking inside/outside - if(this->debug_input_faces()) { + 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; @@ -2066,7 +2325,7 @@ private: if(this->is_edge(vb_3d, vd_3d)) { // let's insert the diagonal [bd] in the CDT_2 cdt_2.insert_constraint(vb, vd); - if(this->debug_verbose_special_cases()) { + if(this->debug().verbose_special_cases()) { std::cerr << "NOTE: CDT_2 has 4 vertices. Flip the diagonal\n"; } } @@ -2146,7 +2405,7 @@ private: border_edges.emplace_back(c, i, j); } } - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << "region size is: " << fh_region.size() << "\n"; std::cerr << "region border size is: " << border_edges.size() << "\n"; } @@ -2292,7 +2551,7 @@ private: std::set> non_intersecting_edges_set; - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { expensive_debug_dump_tetrahedra_intersect_region(face_index, region_index, cdt_2, fh_region); } @@ -2334,7 +2593,7 @@ private: } } - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << "...use edges of the border facets to unify sets\n"; } for(auto facet: facets_of_border) { @@ -2350,7 +2609,7 @@ private: } if(vertices_of_cavity_union_find.number_of_sets() > 2) { - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << "...use non-intersecting edges to unify sets, until we have at most 2 sets\n"; } for(auto c : cr_intersecting_cells) { @@ -2400,13 +2659,13 @@ private: return std::make_pair(Vertex_handle{}, Facet{}); }); CGAL_assume(vertex_above != Vertex_handle{}); - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << "The vertex above the region is " << IO::oformat(vertex_above, with_point_and_info) << "\n"; } // if there are still more than 2 sets, we need to propagate the information // using a DFS on the border facets if(vertices_of_cavity_union_find.number_of_sets() > 2) { - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << "...propagate the information using a DFS on the border facets\n"; } const auto border_edges_set = std::invoke([&] { @@ -2561,7 +2820,7 @@ private: // classify the facets of the border of the cavity for(auto facet: facets_of_border) { - if(this->debug_regions()) { + if(this->debug().regions()) { debug_output_facet_vertices({facet}); } for(auto v: tr().vertices(facet)) { @@ -2579,7 +2838,7 @@ private: clear_marks(vertices_of_lower_cavity, Vertex_marker::CAVITY_BELOW); } // new algorithm - if(this->debug_regions()) { + if(this->debug().regions()) { debug_dump_cavity_outputs(face_index, region_index, intersecting_edges, facets_of_border, facets_of_upper_cavity, facets_of_lower_cavity); for(auto edge : intersecting_edges) { auto [v1, v2] = tr().vertices(edge); @@ -2617,7 +2876,7 @@ private: void 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()) { + if(this->debug().regions()) { std::cerr << "restore_subface_region face index: " << face_index << ", region #" << region_index << "\n"; } const auto& cdt_2 = non_const_cdt_2; @@ -2640,7 +2899,7 @@ private: } return vertices; }); - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { + 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)); @@ -2685,7 +2944,7 @@ private: if(cdt_2.orientation(v0->point(), v1->point(), v3->point()) == CGAL::POSITIVE && cdt_2.orientation(v0->point(), v3->point(), v2->point()) == CGAL::POSITIVE) { - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << "NOTE: the other diagonal is in the 3D triangulation: flip the edge\n"; } non_const_cdt_2.flip(non_const_fh_region[0], diagonal_index); @@ -2705,7 +2964,7 @@ private: fh->info().missing_subface = false; } return true; - } else if(this->debug_regions()) { + } else if(this->debug().regions()) { std::cerr << "NOTE: the other diagonal is in the 3D triangulation BUT the edge is not flippable!\n"; std::cerr << " The region " << region_index << " of face #F" << face_index << " has four points:\n"; std::cerr << " v0: " << v0->point() << '\n'; @@ -2715,7 +2974,7 @@ private: } } - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { + 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), @@ -2786,7 +3045,7 @@ private: // std::to_string(region_index) + ".binary.cgal"); // CGAL::IO::save_binary_file(dump, *this); // } - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << "ERROR: No first segment found intersecting region " << region_index << " of face #" << face_index << "\n"; dump_region(face_index, region_index, cdt_2); @@ -2832,7 +3091,7 @@ private: return true; }; - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format("Cavity has {} piercing cells and {} piercing edges, " "{} vertices in upper cavity and {} in lower, " "{} facets in upper cavity and {} in lower\n", @@ -2845,7 +3104,7 @@ private: } auto register_internal_constrained_facet = [this](Facet f) { this->register_facet_to_be_constrained(f); }; - if(this->debug_copy_triangulation_into_hole()) { + if(this->debug().copy_triangulation_into_hole()) { std::cerr << "# upper cavity\n"; } [[maybe_unused]] const auto [upper_cavity_triangulation, vertices_of_upper_cavity, @@ -2855,7 +3114,7 @@ private: const auto& upper_cavity_triangulation_ = upper_cavity_triangulation; std::for_each(interior_constrained_faces_upper.begin(), interior_constrained_faces_upper.end(), register_internal_constrained_facet); - if(this->debug_copy_triangulation_into_hole()) { + if(this->debug().copy_triangulation_into_hole()) { std::cerr << "# lower cavity\n"; } [[maybe_unused]] const auto [lower_cavity_triangulation, vertices_of_lower_cavity, @@ -2888,7 +3147,7 @@ private: << " )"; return s.str(); }; - if(this->debug_missing_region()) { + if(this->debug().missing_region()) { if(fail_upper) { std::cerr << "NOTE: Face " << display_face() << " is not a facet of the upper cavity\n"; } @@ -2900,7 +3159,7 @@ private: } return false; })) { - if(this->debug_missing_region()) { + if(this->debug().missing_region()) { // debug_region_size_4(); dump_region(face_index, region_index, cdt_2); std::for_each(fh_region.begin(), fh_region.end(), [](auto fh) { fh->info().is_in_region = 3; }); @@ -2937,7 +3196,7 @@ private: insert_in_conflict_visitor.process_cells_in_conflict(cells_of_upper_cavity.begin(), cells_of_upper_cavity.end()); insert_in_conflict_visitor.process_cells_in_conflict(cells_of_lower_cavity.begin(), cells_of_lower_cavity.end()); - if(this->debug_copy_triangulation_into_hole()) { + if(this->debug().copy_triangulation_into_hole()) { std::cerr << "# glu the upper triangulation of the cavity\n"; if(cells_of_lower_cavity.size() > original_intersecting_cells.size() || cells_of_upper_cavity.size() > original_intersecting_cells.size()) @@ -2958,7 +3217,7 @@ private: outer_map[vt] = f; CGAL_USE(this); #if CGAL_CDT_3_CAN_USE_CXX20_FORMAT - if(this->debug_copy_triangulation_into_hole()) { + if(this->debug().copy_triangulation_into_hole()) { CGAL_assertion(vt[0] != vt[1]); CGAL_assertion(vt[0] != vt[2]); CGAL_assertion(vt[1] != vt[2]); @@ -2995,7 +3254,7 @@ private: if(!is_facet) 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); + 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]], @@ -3010,7 +3269,7 @@ private: CGAL_assertion(static_cast(facet_is_facet_of_cdt_2(*this, {new_cell, 3}, cdt_2))); add_to_outer_map(vt, {new_cell, 3}, "extra "); } - if(this->debug_regions()) { + if(this->debug().regions()) { std::ofstream out(cdt_3_format("dump_{}_pseudo_cells_region_{}_{}.off", is_upper_cavity ? "upper" : "lower", face_index, region_index)); out.precision(17); @@ -3025,7 +3284,7 @@ private: const auto upper_inner_map = tr().create_triangulation_inner_map( upper_cavity_triangulation, map_upper_cavity_vertices_to_ambient_vertices, false); - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_copy_triangulation_into_hole()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().copy_triangulation_into_hole()) { std::cerr << "upper_inner_map:\n"; for(auto [vt, _] : upper_inner_map) { std::cerr << cdt_3_format(" {:.6}, {:.6}, {:.6})\n", @@ -3034,7 +3293,7 @@ private: IO::oformat(vt[2], with_point)); } } - if(this->debug_copy_triangulation_into_hole()) { + if(this->debug().copy_triangulation_into_hole()) { std::cerr << "# glu the lower triangulation of the cavity\n"; } this->copy_triangulation_into_hole(map_upper_cavity_vertices_to_ambient_vertices, @@ -3042,7 +3301,7 @@ private: upper_inner_map, this->new_cells_output_iterator()); } - if(this->debug_copy_triangulation_into_hole()) { + if(this->debug().copy_triangulation_into_hole()) { std::cerr << "# glu the lower triangulation of the cavity\n"; } @@ -3063,7 +3322,7 @@ private: const auto lower_inner_map = tr().create_triangulation_inner_map( lower_cavity_triangulation, map_lower_cavity_vertices_to_ambient_vertices, false); #if CGAL_CDT_3_CAN_USE_CXX20_FORMAT - if(this->debug_copy_triangulation_into_hole()) { + 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", @@ -3104,7 +3363,7 @@ private: set_facet_constrained(f, face_index, f2d); f2d->info().missing_subface = false; } - CGAL_assume(!this->debug_validity() || this->is_valid(true)); + CGAL_assume(!this->debug().validity() || this->is_valid(true)); }; // ------------------------- @@ -3237,7 +3496,7 @@ private: tr().is_infinite(v) ? cavity_triangulation.infinite_vertex() : cavity_triangulation.insert(this->point(v)); map_ambient_vertices_to_cavity_vertices[v] = cavity_v; map_cavity_vertices_to_ambient_vertices[cavity_v] = v; - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { + 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), @@ -3337,7 +3596,7 @@ private: { const auto& cdt_2 = non_const_cdt_2; auto steiner_pt = CGAL::centroid(cdt_2.triangle(fh_2d)); - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_verbose_special_cases()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().verbose_special_cases()) { std::cerr << cdt_3_format("Trying to insert Steiner (centroid) point {} in non-coplanar face {}.\n", IO::oformat(steiner_pt), IO::oformat(cdt_2.triangle(fh_2d))); } @@ -3345,7 +3604,7 @@ private: if(encroached_edge_opt) { return encroached_edge_opt; } - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_Steiner_points()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().Steiner_points()) { std::cerr << cdt_3_format("Inserting Steiner (centroid) point {} in non-coplanar face {}: {}.\n", IO::oformat(steiner_pt), face_index, IO::oformat(cdt_2.triangle(fh_2d))); } @@ -3415,7 +3674,7 @@ private: 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()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().Steiner_points()) { std::cerr << " -> " << IO::oformat(v, with_offset) << '\n'; } v->ccdt_3_data().set_Steiner_vertex_in_face(face_index); @@ -3438,13 +3697,13 @@ private: const auto a = this->point(va_3d); const auto b = this->point(vb_3d); const auto mid = CGAL::midpoint(a, b); - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_Steiner_points()) { + 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)); } auto&& contexts = this->constraint_hierarchy.contexts(va_3d, vb_3d); - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_verbose_special_cases()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().verbose_special_cases()) { if(std::next(contexts.begin()) != contexts.end()) { std::cerr << "ERROR: Edge is constrained by more than one constraint\n"; for(const auto& c : contexts) { @@ -3472,7 +3731,7 @@ private: [[maybe_unused]] auto v = 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()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().Steiner_points()) { std::cerr << " -> " << IO::oformat(v, with_offset) << '\n'; } // this->study_bug = false; @@ -3483,7 +3742,7 @@ private: CDT_2& non_const_cdt_2 = face_cdt_2[face_index]; const CDT_2& cdt_2 = non_const_cdt_2; if constexpr (cdt_3_can_use_cxx20_format()) - if(this->debug_copy_triangulation_into_hole() || this->debug_verbose_special_cases()) { + if(this->debug().copy_triangulation_into_hole() || this->debug().verbose_special_cases()) { std::cerr << cdt_3_format("restore_face({}): CDT_2 has {} vertices\n", face_index, cdt_2.number_of_vertices()); } for(const auto& edge : cdt_2.finite_edges()) { @@ -3493,7 +3752,7 @@ private: const auto vb_3d = fh->vertex(cdt_2.ccw(i))->info().vertex_handle_3d; 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()) { + 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)); } @@ -3529,11 +3788,11 @@ private: processed_faces.insert(fh_region.begin(), fh_region.end()); auto handle_error_with_region = [&](const char* what, CDT_2_face_handle fh_2d) { - if(this->debug_regions() || this->debug_verbose_special_cases()) { + if(this->debug().regions() || this->debug().verbose_special_cases()) { std::cerr << "NOTE: " << what << " in sub-region " << (region_index - 1) << " of face #F" << face_index << '\n'; } - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_verbose_special_cases()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().verbose_special_cases()) { std::cerr << " constrained edges are:\n"; for(auto [c, index]: cdt_2.constrained_edges()) { const auto va = c->vertex(cdt_2.cw(index)); @@ -3713,7 +3972,7 @@ public: fill_cdt_2(cdt_2, i); search_for_missing_subfaces(i); } - if(this->debug_input_faces()) { + if(this->debug().input_faces()) { for(CDT_3_signed_index i = 0, end = static_cast (face_constraint_misses_subfaces.size()); i < end; ++i) { dump_face(i); } @@ -3727,7 +3986,7 @@ public: if(restore_face(static_cast (i))) { face_constraint_misses_subfaces_reset(i); } else { - if(this->debug_missing_region() || this->debug_Steiner_points()) { + if(this->debug().missing_region() || this->debug().Steiner_points()) { std::cerr << "restore_face(" << i << ") incomplete, back to conforming...\n"; } Conforming_Dt::restore_Delaunay(insert_in_conflict_visitor); @@ -3739,7 +3998,7 @@ public: i = face_constraint_misses_subfaces_find_next(i); if(i == npos) { std::cerr << "ERROR: No more missing face to restore after a PLC error\n"; - if(this->debug_regions() || this->debug_verbose_special_cases()) { + if(this->debug().regions() || this->debug().verbose_special_cases()) { dump_region(e.face_index, e.region_index); } throw; @@ -3883,7 +4142,7 @@ public: for(std::size_t i = 0; i < intersecting_edges.size(); ++i) { const auto intersecting_edge = intersecting_edges[i]; const auto [v_above, v_below] = tr().vertices(intersecting_edge); - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { + if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { debug_dump_edge_region_intersection(face_index, region_index, fh_region, i, v_above, v_below, intersecting_edge); } @@ -3891,7 +4150,7 @@ public: int expected) { auto value_returned = [this, v0, v1](bool b, bool not_visited) { - if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug_regions()) { + 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), @@ -3952,7 +4211,7 @@ public: !test_edge(cell, v_below, index_v_below, vc, index_vc, -1) && this->use_older_cavity_algorithm()) { - if(this->debug_regions()) { + if(this->debug().regions()) { dump_triangulation(); dump_region(face_index, region_index, cdt_2); std::ofstream out(std::string("dump_two_edges_") + std::to_string(face_index) + ".polylines.txt"); @@ -3966,7 +4225,7 @@ public: } while(++facet_circ != facet_circ_end); if(this->use_newer_cavity_algorithm() && i + 1 == intersecting_edges.size()) { for(auto ch: intersecting_cells) { - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << "tetrahedron #" << ch->time_stamp() << " intersects the region\n"; } for(int i = 0; i < 4; ++i) { @@ -3989,10 +4248,10 @@ public: })) { intersecting_cells.insert(n_ch); - if(this->debug_regions()) { + if(this->debug().regions()) { std::cerr << "new tetrahedron #" << n_ch->time_stamp() << " intersects the region\n"; } - } else if(this->debug_regions()) { + } else if(this->debug().regions()) { std::cerr << "NO, new tetrahedron #" << n_ch->time_stamp() << " does not intersect the region\n"; } for(int i = 0; i < 4; ++i) { @@ -4570,4 +4829,4 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 } // end CGAL -#endif // CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H +#endif // CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_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 5359a58e5ff..86f864937a4 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 @@ -5,6 +5,7 @@ // #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1 #include #include +#include #include #include #include @@ -45,12 +46,8 @@ using K = CGAL::Exact_predicates_inexact_constructions_kernel; #endif // use Epick -struct Vb : public CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3 {}; -struct Cb : public CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3 {}; -struct Tds: public CGAL::Triangulation_data_structure_3 {}; -using Base_triantulation = CGAL::Delaunay_triangulation_3; -using CDT = CGAL::Conforming_constrained_Delaunay_triangulation_3_impl; -using Point = Base_triantulation::Point; +using CDT = CGAL::Conforming_constrained_Delaunay_triangulation_3; +using Point = K::Point_3; using Point_3 = K::Point_3; using Mesh = CGAL::Surface_mesh; @@ -258,35 +255,29 @@ CDT_options::CDT_options(int argc, char* argv[]) { } } -#if NO_TRY_CATCH -# define CDT_3_try if (true) -# define CDT_3_catch(X) if (false) -# define CDT_3_throw_exception_again -#else -// Else proceed normally. -# define CDT_3_try try -# define CDT_3_catch(X) catch(X) -# define CDT_3_throw_exception_again throw -#endif +CGAL::CDT_3::Debug_options cdt_debug_options(const CDT_options& options) { + CGAL::CDT_3::Debug_options cdt_debug; + cdt_debug.Steiner_points(options.verbose_level > 0); + cdt_debug.input_faces(options.debug_input_faces); + cdt_debug.missing_region(options.verbose_level > 1 || options.debug_missing_regions); + cdt_debug.regions(options.debug_regions); + cdt_debug.validity(options.debug_validity); + cdt_debug.finite_edges_map(options.debug_finite_edges_map); + cdt_debug.subconstraints_to_conform(options.debug_subconstraints_to_conform); + 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.constraint_hierarchy(options.debug_constraint_hierarchy); + cdt_debug.geometric_errors(options.debug_geometric_errors); + cdt_debug.polygon_insertion(options.debug_polygon_insertion); + cdt_debug.copy_triangulation_into_hole(options.debug_copy_triangulation_into_hole); + cdt_debug.use_older_cavity_algorithm(!options.use_new_cavity_algorithm); + cdt_debug.use_finite_edges_map(options.use_finite_edges_map); + cdt_debug.display_statistics(!options.quiet); + cdt_debug.set_segment_vertex_epsilon(options.segment_vertex_epsilon); + cdt_debug.set_vertex_vertex_epsilon(options.vertex_vertex_epsilon); -void configure_cdt_debug_options(CDT& cdt, const CDT_options& options) { - cdt.debug_Steiner_points(options.verbose_level > 0); - cdt.debug_input_faces(options.debug_input_faces); - cdt.debug_missing_region(options.verbose_level > 1 || options.debug_missing_regions); - cdt.debug_regions(options.debug_regions); - cdt.debug_validity(options.debug_validity); - cdt.debug_finite_edges_map(options.debug_finite_edges_map); - cdt.debug_subconstraints_to_conform(options.debug_subconstraints_to_conform); - 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_constraint_hierarchy(options.debug_constraint_hierarchy); - cdt.debug_geometric_errors(options.debug_geometric_errors); - cdt.debug_polygon_insertion(options.debug_polygon_insertion); - cdt.debug_copy_triangulation_into_hole(options.debug_copy_triangulation_into_hole); - cdt.use_older_cavity_algorithm(!options.use_new_cavity_algorithm); - cdt.use_finite_edges_map(options.use_finite_edges_map); - cdt.set_segment_vertex_epsilon(options.segment_vertex_epsilon); + return cdt_debug; } auto compute_bounding_box(const Mesh& mesh, const CDT_options& options) { @@ -334,7 +325,7 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& auto& tr = cdt; - std::unordered_map cells_map; + std::unordered_map cells_map; for(auto ch : tr.all_cell_handles()) { cells_map[ch] = 1; @@ -380,13 +371,13 @@ 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 - cdt.write_facets(dump, cdt, std::views::filter(cdt.finite_facets(), [&](auto f) { + cdt.write_facets(dump, cdt.triangulation(), std::views::filter(cdt.finite_facets(), [&](auto f) { return cdt.is_facet_constrained(f); })); #else auto is_facet_constrained = [&](auto f) { return cdt.is_facet_constrained(f); }; auto it_end = cdt.finite_facets_end(); - cdt.write_facets(dump, cdt, + cdt.write_facets(dump, cdt.triangulation(), CGAL::make_range( boost::make_filter_iterator(is_facet_constrained,cdt.finite_facets_begin(), it_end), boost::make_filter_iterator(is_facet_constrained,it_end, it_end))); @@ -396,155 +387,6 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& }; } -struct Min_distance_result { - double min_distance; - std::array vertices_of_min_edge; -}; - -Min_distance_result compute_minimum_vertex_distance(const CDT& cdt) { -#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L - auto [min_sq_distance, min_edge] = - (std::ranges::min)(cdt.finite_edges() | std::views::transform([&](auto edge) { - return std::make_pair(cdt.segment(edge).squared_length(), edge); - })); -#else - auto transform_fct = [&](auto edge) { return std::make_pair(cdt.segment(edge).squared_length(), edge); }; - auto min_p = transform_fct(*cdt.finite_edges_begin()); - for (auto ite=cdt.finite_edges_begin(); ite!=cdt.finite_edges_end(); ++ite) - { - auto p = transform_fct(*ite); - if (p < min_p) - p = min_p; - } - auto [min_sq_distance, min_edge] = min_p; -#endif - auto min_distance = CGAL::to_double(CGAL::approximate_sqrt(min_sq_distance)); - auto vertices_of_min_edge = cdt.vertices(min_edge); - - return {min_distance, vertices_of_min_edge}; -} - -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"; -} - -int validate_minimum_vertex_distances(const CDT& cdt, double vertex_vertex_min_distance, const CDT_options& options) { - auto result = compute_minimum_vertex_distance(cdt); - - if(!options.quiet) { - print_minimum_distance_info(result); - } - if(result.min_distance < vertex_vertex_min_distance) { - std::cerr << "ERROR: min distance between vertices is too small\n"; - return EXIT_FAILURE; - } - return EXIT_SUCCESS; -} - -struct Constraint_distance_result { - double min_distance; - CDT::Vertex_handle min_va, min_vb, min_vertex; -}; - -template -Constraint_distance_result compute_constraint_vertex_distances_from_patches_borders( - CDT& cdt, - const BordersOfPatches& patch_edges, - const VertexPointMap& tr_vertex_pmap) { - -#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L - auto edge_results = patch_edges - | std::views::join - | std::views::transform([&](const auto& edge_pair) { - auto [vda, vdb] = edge_pair; - auto va = get(tr_vertex_pmap, vda); - auto vb = get(tr_vertex_pmap, vdb); - auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); - return std::make_tuple(CGAL::to_double(min_dist), va, vb, min_v); - }); - - auto min_result = std::ranges::min_element(edge_results, {}, [](const auto& tuple) { - return std::get<0>(tuple); - }); - - auto [min_distance, min_va, min_vb, min_vertex] = *min_result; -#else - double min_distance = (std::numeric_limits::max)(); - CDT::Vertex_handle min_va, min_vb, min_vertex; - - for(const auto& edges : patch_edges) { - for(auto [vda, vdb]: edges) { - auto va = get(tr_vertex_pmap, vda); - auto vb = get(tr_vertex_pmap, vdb); - auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); - if(min_dist < min_distance) { - min_distance = CGAL::to_double(min_dist); - min_va = va; - min_vb = vb; - min_vertex = min_v; - } - } - } -#endif - - return {min_distance, min_va, min_vb, min_vertex}; -} - -template -Constraint_distance_result compute_constraint_vertex_distances_from_faces( - CDT& cdt, - const Mesh& mesh, - const VertexPointMap& tr_vertex_pmap) { - - double min_distance = (std::numeric_limits::max)(); - CDT::Vertex_handle min_va, min_vb, min_vertex; - - for(auto face_descriptor : faces(mesh)) { - auto he = halfedge(face_descriptor, mesh); - const auto end = he; - do { - auto va = get(tr_vertex_pmap, source(he, mesh)); - auto vb = get(tr_vertex_pmap, target(he, mesh)); - auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); - if(min_dist < min_distance) { - min_distance = CGAL::to_double(min_dist); - min_va = va; - min_vb = vb; - min_vertex = min_v; - } - he = next(he, mesh); - } while((he = next(he, mesh)) != end); - } - - return {min_distance, min_va, min_vb, min_vertex}; -} - -template -void validate_constraint_vertex_distances_or_throw( - CDT& cdt, - const Mesh& mesh, - const CDT_options& options, - const BordersOfPatches& patch_edges, - const VertexPointMap& tr_vertex_pmap) { - - auto [min_distance, min_va, min_vb, min_vertex] = - options.merge_facets ? compute_constraint_vertex_distances_from_patches_borders(cdt, patch_edges, tr_vertex_pmap) - : compute_constraint_vertex_distances_from_faces(cdt, mesh, tr_vertex_pmap); - - if(!options.quiet) { - std::cout << "Min distance between constraint segment and vertex: " << min_distance << '\n' - << " between segment : " - << CGAL::IO::oformat(min_va, CDT::Conforming_Dt::with_point) << " " - << CGAL::IO::oformat(min_vb, CDT::Conforming_Dt::with_point) << '\n' - << " and vertex : " - << CGAL::IO::oformat(min_vertex, CDT::Conforming_Dt::with_point) << "\n\n"; - } - cdt.check_segment_vertex_distance_or_throw(min_va, min_vb, min_vertex, min_distance, - CDT::Check_distance::NON_SQUARED_DISTANCE); -} template struct Mesh_property_maps { @@ -766,9 +608,9 @@ void dump_patches_borders(const BordersOfPatches& patch_edges, } int go(Mesh mesh, CDT_options options) { - CDT cdt; - configure_cdt_debug_options(cdt, options); + CGAL::CDT_3::Debug_options cdt_debug = cdt_debug_options(options); + // Compute bbox (used for distance checks scaling) auto bbox = compute_bounding_box(mesh, options); auto bbox_max_span = (std::max)(bbox.x_span(), (std::max)(bbox.y_span(), bbox.z_span())); @@ -781,99 +623,18 @@ int go(Mesh mesh, CDT_options options) { dump_patches_borders(patch_edges, pmaps, options.dump_patches_borders_prefix); } - auto mesh_descriptor_to_vertex_handle_pmap = get(CGAL::dynamic_vertex_property_t(), mesh); - - { - auto _ = CGAL::CDT_3_INSERT_VERTICES_TASK_guard(); - auto start_time = std::chrono::high_resolution_clock::now(); - if(options.merge_facets) { - cdt.insert_vertices_range(vertices(mesh), pmaps.mesh_vertex_point_map, mesh_descriptor_to_vertex_handle_pmap, - CGAL::parameters::vertex_is_constrained_map(pmaps.v_selected_map)); - } else { - cdt.insert_vertices_range(vertices(mesh), pmaps.mesh_vertex_point_map, mesh_descriptor_to_vertex_handle_pmap); + auto dump_mesh_with_steiner_points = [&](const auto& cdt) { + if(options.dump_after_conforming_filename.empty()) { + return; } - CDT::Cell_handle hint{}; - for(auto v: vertices(mesh)) { - if(options.merge_facets && false == get(pmaps.v_selected_map, v)) continue; - auto vh = cdt.insert(get(pmaps.mesh_vertex_point_map, v), hint, false); - hint = vh->cell(); - put(mesh_descriptor_to_vertex_handle_pmap, v, vh); - } - if(!options.quiet) { - auto timing = std::chrono::high_resolution_clock::now() - start_time; - std::cout << "[timings] inserted vertices in " - << std::chrono::duration_cast(timing).count() << " ms\n"; - std::cout << "Number of vertices: " << cdt.number_of_vertices() << "\n\n"; - } - cdt.add_bbox_points_if_not_dimension_3(); - } - - { - auto _ = CGAL::CDT_3_COMPUTE_DISTANCES_TASK_guard(); - auto start_time = std::chrono::high_resolution_clock::now(); - - auto exit_code = validate_minimum_vertex_distances(cdt, options.vertex_vertex_epsilon * bbox_max_span, options); - if(exit_code != EXIT_SUCCESS) { - return exit_code; - } - - validate_constraint_vertex_distances_or_throw(cdt, mesh, options, patch_edges, mesh_descriptor_to_vertex_handle_pmap); - - if(!options.quiet) { - auto timing = std::chrono::high_resolution_clock::now() - start_time; - std::cout << "[timings] compute distances on " - << std::chrono::duration_cast(timing).count() << " ms\n"; - } - } - - { - auto _ = CGAL::CDT_3_CONFORMING_TASK_guard(); - int poly_id = 0; - auto output_on_exit_scope_guard = CGAL::make_scope_exit(create_output_finalizer(cdt, options)); - auto start_time = std::chrono::high_resolution_clock::now(); - if(options.merge_facets) { - for(auto& edges: patch_edges) { - cdt.insert_constrained_face_defined_by_its_border_edges(std::move(edges), pmaps.mesh_vertex_point_map, - mesh_descriptor_to_vertex_handle_pmap); - } - } else { - for(auto face_descriptor : faces(mesh)) { - std::vector polygon; - const auto he = halfedge(face_descriptor, mesh); - for(auto vertex_it : CGAL::vertices_around_face(he, mesh)) { - polygon.push_back(get(pmaps.mesh_vertex_point_map, vertex_it)); - } - if(cdt.debug_polygon_insertion()) { - std::cerr << "NEW POLYGON #" << poly_id << '\n'; - } - [[maybe_unused]] auto id = cdt.insert_constrained_polygon(polygon, false); - assert(id == poly_id); - ++poly_id; - // std::ofstream dump("dump.binary.cgal"); - // CGAL::Mesh_3::save_binary_file(dump, cdt); - } - } // not merge_facets - if(!options.quiet) { - auto timing = std::chrono::high_resolution_clock::now() - start_time; - std::cout << "[timings] registered facets in " - << std::chrono::duration_cast(timing).count() << " ms\n"; - } - start_time = std::chrono::high_resolution_clock::now(); - cdt.restore_Delaunay(); - if(!options.quiet) { - std::cout << "[timings] restored Delaunay (conforming of facets borders) in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - } - } - - if(!options.dump_after_conforming_filename.empty()) { auto _ = CGAL::CDT_3_OUTPUT_TASK_guard(); using Vertex_index = Mesh::Vertex_index; [[maybe_unused]] std::size_t time_stamp_counter = 0u; - for(auto v: cdt.finite_vertex_handles()) { + for(auto v : cdt.finite_vertex_handles()) { [[maybe_unused]] const auto time_stamp = v->time_stamp(); assert(++time_stamp_counter == time_stamp); - if(!v->ccdt_3_data().is_Steiner_vertex_on_edge()) continue; + if(!v->ccdt_3_data().is_Steiner_vertex_on_edge()) + continue; const auto [va, vb] = cdt.ancestors_of_Steiner_vertex_on_edge(v); const auto index_va = Vertex_index{static_cast(va->time_stamp() - 1)}; const auto index_vb = Vertex_index{static_cast(vb->time_stamp() - 1)}; @@ -889,36 +650,28 @@ int go(Mesh mesh, CDT_options options) { out_mesh.precision(17); out_mesh << mesh; out_mesh.close(); - } + }; + // Build CDT directly from the polygon mesh using the official API + auto build_start = std::chrono::high_resolution_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) { - std::cout << "Number of vertices after conforming: " << cdt.number_of_vertices() << "\n\n"; + 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"; } + // Validation: check conforming status and validity { auto _ = CGAL::CDT_3_VALIDATION_TASK_guard(); - CGAL_assertion(!options.call_is_valid || cdt.Base_triantulation::is_valid(true)); CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); } - { - auto _ = CGAL::CDT_3_CDT_TASK_guard(); - auto start_time = std::chrono::high_resolution_clock::now(); - cdt.restore_constrained_Delaunay(); - if(!options.quiet) { - std::cout << "[timings] restored constrained Delaunay in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; - std::cout << "Number of vertices after CDT: " << cdt.number_of_vertices() << "\n\n"; - } - } - - { - auto _ = CGAL::CDT_3_VALIDATION_TASK_guard(); - CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); - CGAL_assertion(!options.call_is_valid || cdt.is_valid(true)); - } - return EXIT_SUCCESS; } @@ -1008,9 +761,6 @@ int bisect_errors(Mesh mesh, CDT_options options) { } int main(int argc, char* argv[]) { - CDT::Conforming_Dt::with_offset.offset = -1; - CDT::Conforming_Dt::with_point.offset = -1; - CDT::Conforming_Dt::with_point_and_info.offset = -1; std::cerr.precision(17); std::cout.precision(17); From 07e0ea785d5dc34ea1bc0f0fffe247bf7b463d94 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 28 Oct 2025 11:22:48 +0100 Subject: [PATCH 31/51] CDT_3: fix for Epeck --- ...ing_constrained_Delaunay_triangulation_3.h | 8 +- .../include/CGAL/Kernel/hash_functions.h | 217 +++++++++--------- .../test/Kernel_23/test_hash_functions.cpp | 9 +- .../include/CGAL/unordered_flat_map.h | 16 ++ .../test/STL_Extension/CMakeLists.txt | 1 + .../test/STL_Extension/test_is_hashable.cpp | 86 +++++++ 6 files changed, 228 insertions(+), 109 deletions(-) create mode 100644 STL_Extension/test/STL_Extension/test_is_hashable.cpp 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 fea4d208113..a918c1db316 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 @@ -696,7 +696,13 @@ public: halfedge_descriptor halfedge; int count; }; - CGAL::unordered_flat_map points_to_halfedge; + + using Point_to_point_info_map = std::conditional_t< + CGAL::is_hashable_v, + CGAL::unordered_flat_map, + std::map>; + + Point_to_point_info_map points_to_halfedge; auto visited_halfedges = get(CGAL::dynamic_halfedge_property_t(), mesh); for(const auto h : halfedges(mesh)) { diff --git a/Kernel_23/include/CGAL/Kernel/hash_functions.h b/Kernel_23/include/CGAL/Kernel/hash_functions.h index a68197941d8..224483c492b 100644 --- a/Kernel_23/include/CGAL/Kernel/hash_functions.h +++ b/Kernel_23/include/CGAL/Kernel/hash_functions.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019 +// Copyright (c) 2019,2025 // GeometryFactory (France) // // This file is part of CGAL (www.cgal.org) @@ -9,6 +9,8 @@ // // // Author(s) : Simon Giraudot +// +// Test file: test/Kernel_23/test_hash_functions.cpp #ifndef CGAL_KERNEL_HASH_FUNCTIONS_H #define CGAL_KERNEL_HASH_FUNCTIONS_H @@ -16,20 +18,73 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + namespace CGAL { using boost::hash_value; +template +inline constexpr bool has_rep_tag_v = false; + template -inline std::enable_if_t::value, std::size_t> +inline constexpr bool has_rep_tag_v> = true; + +template +struct Rep_tag { + using type = void; +}; + +template +struct Rep_tag>> { + using type = typename K::Rep_tag; +}; + +template +using Rep_tag_t = typename Rep_tag::type; + +template +inline constexpr bool is_Cartesian_v = std::is_same, Cartesian_tag>::value; + +template +struct Is_kernel_hashable : public std::false_type {}; + +template +struct Is_kernel_hashable()))>> : public std::true_type {}; + +template +inline constexpr bool is_kernel_hashable_v = Is_kernel_hashable::value; + +template +using enable_if_Cartesian_and_hashable_t = + std::enable_if_t && is_kernel_hashable_v, T>; + +template +inline enable_if_Cartesian_and_hashable_t hash_value (const Aff_transformation_2& transform) { std::size_t result = hash_value(transform.cartesian(0,0)); for(int i=0; i < 3; ++i) - for(int j = 0; j < 3; ++j) - if (!(i == 0 && j == 0)) - boost::hash_combine(result, hash_value(transform.cartesian(i,j))); + for(int j = (i == 0 ? 1 : 0); j < 3; ++j) + boost::hash_combine(result, hash_value(transform.cartesian(i,j))); return result; } @@ -44,7 +99,7 @@ hash_value (const Bbox_2& bbox) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Circle_2& circle) { std::size_t result = hash_value(circle.center()); @@ -54,7 +109,7 @@ hash_value (const Circle_2& circle) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Iso_rectangle_2& iso_rectangle) { std::size_t result = hash_value((iso_rectangle.min)()); @@ -63,7 +118,7 @@ hash_value (const Iso_rectangle_2& iso_rectangle) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Point_2& point) { std::size_t result = hash_value(point.x()); @@ -72,7 +127,7 @@ hash_value (const Point_2& point) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Segment_2& segment) { std::size_t result = hash_value(segment.source()); @@ -81,7 +136,7 @@ hash_value (const Segment_2& segment) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Vector_2& vector) { std::size_t result = hash_value(vector.x()); @@ -90,7 +145,7 @@ hash_value (const Vector_2& vector) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Weighted_point_2& weighed_point) { std::size_t result = hash_value(weighed_point.point()); @@ -99,14 +154,13 @@ hash_value (const Weighted_point_2& weighed_point) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Aff_transformation_3& transform) { std::size_t result = hash_value(transform.cartesian(0,0)); for(int i = 0; i < 3; ++i) - for(int j = 0; j < 4; ++j) - if (!(i == 0 && j == 0)) - boost::hash_combine(result, hash_value(transform.cartesian(i,j))); + for(int j = (i == 0 ? 1 : 0); j < 4; ++j) + boost::hash_combine(result, hash_value(transform.cartesian(i,j))); return result; } @@ -123,7 +177,7 @@ hash_value (const Bbox_3& bbox) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Iso_cuboid_3& iso_cuboid) { std::size_t result = hash_value((iso_cuboid.min)()); @@ -132,7 +186,7 @@ hash_value (const Iso_cuboid_3& iso_cuboid) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Point_3& point) { std::size_t result = hash_value(point.x()); @@ -142,7 +196,7 @@ hash_value (const Point_3& point) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Segment_3& segment) { std::size_t result = hash_value(segment.source()); @@ -151,7 +205,7 @@ hash_value (const Segment_3& segment) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Sphere_3& sphere) { std::size_t result = hash_value(sphere.center()); @@ -161,7 +215,7 @@ hash_value (const Sphere_3& sphere) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Vector_3& vector) { std::size_t result = hash_value(vector.x()); @@ -171,7 +225,7 @@ hash_value (const Vector_3& vector) } template -inline std::enable_if_t::value, std::size_t> +inline enable_if_Cartesian_and_hashable_t hash_value (const Weighted_point_3& weighed_point) { std::size_t result = hash_value(weighed_point.point()); @@ -179,93 +233,46 @@ hash_value (const Weighted_point_3& weighed_point) return result; } +struct Forward_to_hash_value { + template + std::size_t operator()(T&& t) const { + using boost::hash_value; + return hash_value(std::forward(t)); + } +}; + +template +struct Maybe_forward_to_hash_value { + Maybe_forward_to_hash_value() = delete; + Maybe_forward_to_hash_value(const Maybe_forward_to_hash_value&) = delete; +}; + +template +struct Maybe_forward_to_hash_value>> + : public Forward_to_hash_value {}; + + } //namespace CGAL // overloads of std::hash used for using std::unordered_[set/map] on CGAL Kernel objects -namespace std -{ +namespace std { +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template <> struct hash : CGAL::Forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template struct hash> : CGAL::Maybe_forward_to_hash_value {}; +template <> struct hash : CGAL::Forward_to_hash_value {}; +} // namespace std -template struct hash > { - std::size_t operator() (const CGAL::Aff_transformation_2& transform) const { - return CGAL::hash_value (transform); - } -}; -template <> struct hash { - std::size_t operator() (const CGAL::Bbox_2& bbox) const { - return CGAL::hash_value (bbox); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Circle_2& circle) const { - return CGAL::hash_value (circle); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Iso_rectangle_2& iso_rectangle) const { - return CGAL::hash_value (iso_rectangle); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Point_2& point) const { - return CGAL::hash_value (point); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Segment_2& segment) const { - return CGAL::hash_value (segment); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Vector_2& vector) const { - return CGAL::hash_value (vector); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Weighted_point_2& weighted_point) const { - return CGAL::hash_value (weighted_point); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Aff_transformation_3& transform) const { - return CGAL::hash_value (transform); - } -}; -template <> struct hash { - std::size_t operator() (const CGAL::Bbox_3& bbox) const { - return CGAL::hash_value (bbox); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Iso_cuboid_3& iso_cuboid) const { - return CGAL::hash_value (iso_cuboid); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Point_3& point) const { - return CGAL::hash_value (point); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Segment_3& segment) const { - return CGAL::hash_value (segment); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Sphere_3& sphere) const { - return CGAL::hash_value (sphere); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Vector_3& vector) const { - return CGAL::hash_value (vector); - } -}; -template struct hash > { - std::size_t operator() (const CGAL::Weighted_point_3& weighted_point) const { - return CGAL::hash_value (weighted_point); - } -}; - -} #endif // CGAL_KERNEL_HASH_FUNCTIONS_H diff --git a/Kernel_23/test/Kernel_23/test_hash_functions.cpp b/Kernel_23/test/Kernel_23/test_hash_functions.cpp index 43a5cb8ead6..4ccf96acdb0 100644 --- a/Kernel_23/test/Kernel_23/test_hash_functions.cpp +++ b/Kernel_23/test/Kernel_23/test_hash_functions.cpp @@ -1,13 +1,18 @@ +// test partially generated by Github Copilot + #include #include #include - #include +#include typedef CGAL::Simple_cartesian SC; typedef CGAL::Exact_predicates_inexact_constructions_kernel Epick; +static_assert(CGAL::is_kernel_hashable_v); +static_assert(CGAL::is_kernel_hashable_v); + template void test (const Object& obj) { @@ -67,5 +72,3 @@ int main() return 0; } - - diff --git a/STL_Extension/include/CGAL/unordered_flat_map.h b/STL_Extension/include/CGAL/unordered_flat_map.h index 5354e7d5db3..af55f9975bf 100644 --- a/STL_Extension/include/CGAL/unordered_flat_map.h +++ b/STL_Extension/include/CGAL/unordered_flat_map.h @@ -38,6 +38,22 @@ namespace CGAL { + namespace internal_is_hashable { + using boost::hash_value; + + template + struct Has_hash_value : std::false_type {}; + + template + struct Has_hash_value()))>> + : std::is_convertible())), std::size_t> + {}; + } + + template + inline constexpr bool is_hashable_v = + internal_is_hashable::Has_hash_value::value && std::is_default_constructible_v>; + template < typename Key, typename T, diff --git a/STL_Extension/test/STL_Extension/CMakeLists.txt b/STL_Extension/test/STL_Extension/CMakeLists.txt index b1007acfcb5..0639a015ca4 100644 --- a/STL_Extension/test/STL_Extension/CMakeLists.txt +++ b/STL_Extension/test/STL_Extension/CMakeLists.txt @@ -28,6 +28,7 @@ create_single_source_cgal_program("test_dispatch_output.cpp") create_single_source_cgal_program("test_Flattening_iterator.cpp") create_single_source_cgal_program("test_Handle_with_policy.cpp") create_single_source_cgal_program("test_In_place_list.cpp") +create_single_source_cgal_program("test_is_hashable.cpp") create_single_source_cgal_program("test_is_iterator.cpp") create_single_source_cgal_program("test_is_streamable.cpp") create_single_source_cgal_program("test_lexcompare_outputrange.cpp") diff --git a/STL_Extension/test/STL_Extension/test_is_hashable.cpp b/STL_Extension/test/STL_Extension/test_is_hashable.cpp new file mode 100644 index 00000000000..043d5839e4e --- /dev/null +++ b/STL_Extension/test/STL_Extension/test_is_hashable.cpp @@ -0,0 +1,86 @@ +// test partially generated by Github Copilot + +#include +#include +#include +#include +#include +#include +#include +#include +#include // std::pair + +// Test types without hash support +struct No_hash {}; +struct No_default_constructible_hash {}; + +// Provide boost::hash_value for No_default_constructible_hash +namespace boost { + std::size_t hash_value(const No_default_constructible_hash&) { return 42; } +} + +// But make std::hash not default constructible +namespace std { + template <> + struct hash { + hash() = delete; // Not default constructible + std::size_t operator()(const No_default_constructible_hash&) const { return 42; } + }; +} + +int main() { + using CGAL::is_hashable_v; + using SC = CGAL::Simple_cartesian; + using Epick = CGAL::Exact_predicates_inexact_constructions_kernel; + using Epeck = CGAL::Exact_predicates_exact_constructions_kernel; + + // Built-in types should be hashable + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + + // Types without hash support should not be hashable + static_assert(!is_hashable_v); + + // Types with hash_value but non-default-constructible std::hash should not be hashable + static_assert(!is_hashable_v); + + // CGAL kernel objects should be hashable (with Cartesian kernels) + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + + // Test with Epick kernel + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + static_assert(is_hashable_v); + + // Test with Epeck kernel + static_assert(!is_hashable_v); + static_assert(!is_hashable_v); + static_assert(!is_hashable_v); + static_assert(!is_hashable_v); + + // Bbox types should be hashable + static_assert(is_hashable_v); + static_assert(is_hashable_v); + + // std::pair should not be hashable (no std::hash specialization in standard) + static_assert(!is_hashable_v>); + + return 0; +} From bc42fb4a40d1198b658c30510c2d907f0776efec Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 28 Oct 2025 12:13:46 +0100 Subject: [PATCH 32/51] set the default random --- .../Thingi10k-CDT.cmake | 89 +++++++++++++++++++ .../cdt_3_from_off.cpp | 2 + 2 files changed, 91 insertions(+) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake b/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake index fc072a5f1d7..f37a6708c88 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake @@ -118,6 +118,92 @@ set(thingi10k_FAILED_WITH_SEGFAULT_CTest_20251002 822697.stl ) +set(thingi10k_FAILED_WITH_MERGE_FACETS_CTest_20251028 +139765.stl +1452677.stl +1452678.stl +1452679.stl +145329.stl +145330.stl +145331.stl +1505036.stl +1514900.stl +153100.stl +1652975.stl +1652976.stl +1706457.stl +186546.stl +196121.stl +196122.stl +196123.stl +196126.stl +196127.stl +196194.stl +199814.stl +199818.stl +206318.stl +215991.stl +230152.stl +230153.stl +237632.stl +239188.stl +255657.stl +255658.stl +276937.stl +285603.stl +286161.stl +288352.stl +288446.stl +360073.stl +362398.stl +37743.stl +383022.stl +39182.stl +39245.stl +39495.stl +39499.stl +40841.stl +41521.stl +42040.stl +44025.stl +44064.stl +44901.stl +472050.stl +50659.stl +51797.stl +57811.stl +61418.stl +61431.stl +622000.stl +62592.stl +62593.stl +65144.stl +65395.stl +65402.stl +669962.stl +68255.stl +702204.stl +70381.stl +71461.stl +723893.stl +72419.stl +726665.stl +77343.stl +84624.stl +90225.stl +906183.stl +91147.stl +91474.stl +93702.stl +93703.stl +95796.stl +95797.stl +97515.stl +97590.stl +97593.stl +99895.stl +) + function(CGAL_add_cdt3_test_from_Thingi10k data_name data_filename) set(options "ONLY_MERGE_FACETS") set(oneValueArgs TIMEOUT) @@ -157,6 +243,9 @@ foreach(thingi_file_name ${thingi10k_max_10k_solid}) if(thingi_file_name IN_LIST thingi10k_FAILED_WITH_SEGFAULT_CTest_20251002) list(APPEND LABELS "CTest_20251002_failed_segfault") endif() + if(thingi_file_name IN_LIST thingi10k_FAILED_WITH_MERGE_FACETS_CTest_20251028) + list(APPEND LABELS "CTest_20251028_failed_merge_facets") + endif() get_filename_component(thingi_ID "${thingi_file_name}" NAME_WE) CGAL_add_cdt3_test_from_Thingi10k(Thingi10K_${thingi_ID} ${thingi_file_name} TIMEOUT 600 LABELS ${LABELS} ${MY_ONLY_MERGE_FACETS}) 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 86f864937a4..8b5b1679628 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 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -763,6 +764,7 @@ int bisect_errors(Mesh mesh, CDT_options options) { int main(int argc, char* argv[]) { std::cerr.precision(17); std::cout.precision(17); + CGAL::get_default_random() = CGAL::Random(42); CDT_options options(argc, argv); if(options.need_help) { From fd21dfb67da67c445cc2088fcfae7f05493b451d Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 28 Oct 2025 17:30:22 +0100 Subject: [PATCH 33/51] fix clang warning --- .../include/CGAL/Conforming_Delaunay_triangulation_3.h | 6 +++--- 1 file changed, 3 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 f047cff574d..805726cfb4e 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -252,9 +252,9 @@ public: 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{ {-1} }; + inline static With_point_tag with_point{ {-1} }; + inline static With_point_and_info_tag with_point_and_info{ {-1} }; Conforming_Delaunay_triangulation_3(const Geom_traits& gt = Geom_traits()) : T_3(gt) From 6706e0468e3d27398265ffb6d0e048d4612665f5 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Tue, 28 Oct 2025 17:31:18 +0100 Subject: [PATCH 34/51] set epsilons to 0 by default --- .../Conforming_Delaunay_triangulation_3.h | 13 ++--- .../Thingi10k-CDT.cmake | 54 +++++++++++++++++++ .../cdt_3_from_off.cpp | 4 +- 3 files changed, 60 insertions(+), 11 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 805726cfb4e..936273cb2fa 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -381,7 +381,7 @@ protected: template Constrained_polyline_id insert_constrained_edge_impl(Vertex_handle va, Vertex_handle vb, Visitor&) { if(va != vb) { - if(segment_vertex_epsilon != 0.) { + if(debug().segment_vertex_epsilon() != 0.) { auto [min_dist, min_vertex] = min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb); check_segment_vertex_distance_or_throw(va, vb, min_vertex, CGAL::to_double(min_dist), Check_distance::NON_SQUARED_DISTANCE); @@ -514,10 +514,6 @@ protected: } public: - void set_segment_vertex_epsilon(double epsilon) { - segment_vertex_epsilon = epsilon; - } - CDT_3::Debug_options& debug() { return debug_options_; } const CDT_3::Debug_options& debug() const { return debug_options_; } @@ -603,9 +599,9 @@ public: update_max_bbox_edge_length(); } if((distance_type == Check_distance::NON_SQUARED_DISTANCE && - min_dist < segment_vertex_epsilon * *max_bbox_edge_length) || + min_dist < debug().segment_vertex_epsilon() * *max_bbox_edge_length) || (distance_type == Check_distance::SQUARED_DISTANCE && - min_dist < CGAL::square(segment_vertex_epsilon * *max_bbox_edge_length))) + min_dist < CGAL::square(debug().segment_vertex_epsilon() * *max_bbox_edge_length))) { std::stringstream ss; ss.precision(std::cerr.precision()); @@ -1065,7 +1061,7 @@ protected: return return_orig_result_point(lambda, orig_pb, orig_pa); } } else { - if(segment_vertex_epsilon > 0) { + if(debug().segment_vertex_epsilon() > 0) { if(!max_bbox_edge_length) { update_max_bbox_edge_length(); } @@ -1096,7 +1092,6 @@ protected: Constraint_hierarchy constraint_hierarchy = {comp}; static_assert(CGAL::cdt_3_msvc_2019_or_older() || CGAL::is_nothrow_movable_v); Bbox_3 bbox{}; - double segment_vertex_epsilon = 1e-8; mutable std::optional max_bbox_edge_length; using Pair_of_vertex_handles = std::pair; boost::container::map pair_of_vertices_to_cid; diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake b/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake index f37a6708c88..5c83528f4df 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/Thingi10k-CDT.cmake @@ -118,6 +118,57 @@ set(thingi10k_FAILED_WITH_SEGFAULT_CTest_20251002 822697.stl ) +set(thingi10k_FAILED_CTest_20251002 +100606.stl +100644.stl +101955.stl +109130.stl +116873.stl +116876.stl +135777.stl +139737.stl +1439534.stl +145329.stl +145330.stl +1505036.stl +1514900.stl +196121.stl +196122.stl +196123.stl +196126.stl +196127.stl +199814.stl +199818.stl +200695.stl +215991.stl +230152.stl +230153.stl +239188.stl +276937.stl +285604.stl +285605.stl +288352.stl +288353.stl +288354.stl +288355.stl +39182.stl +39245.stl +472050.stl +55278.stl +61418.stl +622000.stl +669962.stl +67817.stl +702204.stl +723893.stl +822697.stl +904476.stl +91474.stl +95796.stl +95797.stl +97515.stl +) + set(thingi10k_FAILED_WITH_MERGE_FACETS_CTest_20251028 139765.stl 1452677.stl @@ -240,6 +291,9 @@ foreach(thingi_file_name ${thingi10k_max_10k_solid}) if(thingi_file_name IN_LIST thingi10k_FAILED_WITH_MERGE_FACETS_CTest_20240222_2201) list(APPEND LABELS "CTest_20240222_2201_failed_merge_facets") endif() + if(thingi_file_name IN_LIST thingi10k_FAILED_CTest_20251002) + list(APPEND LABELS "CTest_20251002_failed") + endif() if(thingi_file_name IN_LIST thingi10k_FAILED_WITH_SEGFAULT_CTest_20251002) list(APPEND LABELS "CTest_20251002_failed_segfault") endif() 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 8b5b1679628..88124d7254e 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 @@ -136,8 +136,8 @@ struct CDT_options bool use_finite_edges_map = false; bool call_is_valid = true; double ratio = 0.1; - double vertex_vertex_epsilon = 1e-14; - double segment_vertex_epsilon = 1e-14; + double vertex_vertex_epsilon = 0.; // 1e-14; + double segment_vertex_epsilon = 0.; // 1e-14; double coplanar_polygon_max_angle = 5.1; double coplanar_polygon_max_distance = 1e-6; std::string failure_assertion_expression {}; From ab9e0ebc8a34a19f71936b903edae95ad7c48fdd Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 29 Oct 2025 14:09:00 +0100 Subject: [PATCH 35/51] fix a few segfaults --- .../Conforming_constrained_Delaunay_triangulation_3.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 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 a918c1db316..539bfaf93a0 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 @@ -4199,11 +4199,12 @@ public: CGAL_assertion(false == this->is_infinite(*facet_circ)); const auto cell = facet_circ->first; const auto facet_index = facet_circ->second; - CGAL_assertion_msg(!cell->ccdt_3_data().is_facet_constrained(facet_index), - std::invoke([&]() { - this->dump_triangulation_to_off(); - return std::string("intersecting polygons!"); - }).c_str()); + if(cell->ccdt_3_data().is_facet_constrained(facet_index)) { + CGAL_error_msg(std::invoke([&]() { + if(this->debug().regions()) this->dump_triangulation_to_off(); + return std::string("intersecting polygons!"); + }).c_str()); + } if(new_cell(cell)) { intersecting_cells.insert(cell); } From b5c3b5f89541ec83bf9aba4197057d70e1f6938b Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 29 Oct 2025 15:32:11 +0100 Subject: [PATCH 36/51] fix typo --- .../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 539bfaf93a0..80116b99819 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 @@ -582,7 +582,7 @@ public: /// \cond SKIP_IN_MANUAL CDT_3_impl& impl() { return cdt_impl; } - /// /endcond + /// \endcond public: /** \name Constructors @{ From 5cc8ec7d714bf3e171c810215ce0aa69a4344806 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 31 Oct 2025 09:47:20 +0100 Subject: [PATCH 37/51] hide unwanted stats or warnings --- .../Conforming_constrained_Delaunay_triangulation_3.h | 10 ++++++---- 1 file 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 80116b99819..863d0b9a72d 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 @@ -2075,9 +2075,10 @@ public: if(this->debug().vertex_vertex_epsilon() > 0) { this->validate_vertex_vertex_distances_or_throw(mesh, patch_edges, tr_vertex_pmap); } - if(this->debug().display_statistics()) { - std::cout << "[timings] compute distances in " - << task_guard.time_ms() << " ms\n"; + if((this->debug().segment_vertex_epsilon() > 0 || this->debug().vertex_vertex_epsilon() > 0) && + this->debug().display_statistics()) + { + std::cout << "[timings] compute distances in " << task_guard.time_ms() << " ms\n"; } } @@ -2304,7 +2305,8 @@ private: } std::cerr << counter << " triangles(s) in the face\n"; } - if(Algebraic_structure_traits::Is_exact::value && + if(this->debug().input_faces() && + Algebraic_structure_traits::Is_exact::value && !std::all_of(cdt_2.finite_face_handles().begin(), cdt_2.finite_face_handles().end(), [=](const auto fh) { const auto p0 = cdt_2.point(fh->vertex(0)); const auto v1 = cdt_2.point(fh->vertex(1)) - p0; From e13da7cbd45ded8b4bd45529d422f96f42a7d73f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 31 Oct 2025 09:43:13 +0100 Subject: [PATCH 38/51] restore the output --- .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 4 +++- 1 file changed, 3 insertions(+), 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 88124d7254e..eea97494e07 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 @@ -654,8 +654,10 @@ 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(); - CDT cdt{std::invoke([&]() { + 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); })}; From 7f4703e248e0f0fcc416911609a2f6be388465a6 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 31 Oct 2025 09:43:13 +0100 Subject: [PATCH 39/51] fix a warning --- Number_types/include/CGAL/NT_wrapper.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Number_types/include/CGAL/NT_wrapper.h b/Number_types/include/CGAL/NT_wrapper.h index 97f66f1e55b..1ab57db8a9a 100644 --- a/Number_types/include/CGAL/NT_wrapper.h +++ b/Number_types/include/CGAL/NT_wrapper.h @@ -11,6 +11,11 @@ #ifndef CGAL_NT_WRAPPER_H #define CGAL_NT_WRAPPER_H + +#include + +#if __cpp_lib_source_location + #include #include #include @@ -184,4 +189,6 @@ struct Wrapped_epeck } // namespace CGAL +#endif // __cpp_lib_source_location + #endif // CGAL_NT_WRAPPER_H \ No newline at end of file From f508d6fe1dfc4a4223ee8cd44a4dac26caae1e70 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 31 Oct 2025 15:40:22 +0100 Subject: [PATCH 40/51] Update STL_Extension/include/CGAL/unordered_flat_map.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- STL_Extension/include/CGAL/unordered_flat_map.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/STL_Extension/include/CGAL/unordered_flat_map.h b/STL_Extension/include/CGAL/unordered_flat_map.h index af55f9975bf..74e09b967c6 100644 --- a/STL_Extension/include/CGAL/unordered_flat_map.h +++ b/STL_Extension/include/CGAL/unordered_flat_map.h @@ -45,8 +45,10 @@ namespace CGAL { struct Has_hash_value : std::false_type {}; template - struct Has_hash_value()))>> - : std::is_convertible())), std::size_t> + using hash_value_type = decltype(hash_value(std::declval())); + template + struct Has_hash_value>> + : std::is_convertible, std::size_t> {}; } From 2a5351cc0a0d378f3defa0ddb2b1e53d6debed23 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Fri, 31 Oct 2025 15:40:42 +0100 Subject: [PATCH 41/51] Update Kernel_23/include/CGAL/Kernel/hash_functions.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Kernel_23/include/CGAL/Kernel/hash_functions.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Kernel_23/include/CGAL/Kernel/hash_functions.h b/Kernel_23/include/CGAL/Kernel/hash_functions.h index 224483c492b..2a2e42ba710 100644 --- a/Kernel_23/include/CGAL/Kernel/hash_functions.h +++ b/Kernel_23/include/CGAL/Kernel/hash_functions.h @@ -83,8 +83,10 @@ hash_value (const Aff_transformation_2& transform) { std::size_t result = hash_value(transform.cartesian(0,0)); for(int i=0; i < 3; ++i) - for(int j = (i == 0 ? 1 : 0); j < 3; ++j) - boost::hash_combine(result, hash_value(transform.cartesian(i,j))); + for(int j=0; j < 3; ++j) + // Skip (0,0) as it was already used to initialize the hash + if (!(i == 0 && j == 0)) + boost::hash_combine(result, hash_value(transform.cartesian(i,j))); return result; } From c751ee6bc91de561ea2e549a419c4fbc0ed88b09 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 12 Nov 2025 12:16:35 +0100 Subject: [PATCH 42/51] fix for AppleClang --- .../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 863d0b9a72d..4ad22b9eed8 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 @@ -1959,7 +1959,8 @@ public: const VertexPointMap& tr_vertex_pmap) const { -#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L +#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L && \ + (!defined(_LIBCPP_STD_VER) || defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)) auto edge_results = patch_edges | std::views::join | std::views::transform([&](const auto& edge_pair) { From 1602be1348e099401756f870e78bdfa18350bb4b Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 13 Nov 2025 13:29:35 +0100 Subject: [PATCH 43/51] precompiled demos for CGAL-6.1 --- Maintenance/public_release/scripts/precompiled_demos_zips | 3 +++ .../PackageDescription.txt | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Maintenance/public_release/scripts/precompiled_demos_zips b/Maintenance/public_release/scripts/precompiled_demos_zips index 405e0e806b6..3e45f332dc5 100755 --- a/Maintenance/public_release/scripts/precompiled_demos_zips +++ b/Maintenance/public_release/scripts/precompiled_demos_zips @@ -78,6 +78,9 @@ pushd Triangulation_on_sphere_2_Demo_with_dlls; zip -r ../triangulation_on_spher pushd Lab_Demo_with_dlls; zip -r ../CGALlab.zip *; popd pushd Arrangement_on_surface_2_earth_Demo_with_dlls; zip -r ../arrangements_2_earth.zip *; popd +# CGAL-6.1 +pushd Triangulation_on_hyperbolic_surface_2_Demo_with_dlls; zip -r ../triangulation_on_hyperbolic_surface_2.zip *; popd + # check echo CHECK now. The following lines should be empty. for f in *zip; do unzip -qql $f; done | awk '{print $4}' >| done diff --git a/Triangulation_on_hyperbolic_surface_2/doc/Triangulation_on_hyperbolic_surface_2/PackageDescription.txt b/Triangulation_on_hyperbolic_surface_2/doc/Triangulation_on_hyperbolic_surface_2/PackageDescription.txt index 5f3b585103b..34cdcf2a70e 100644 --- a/Triangulation_on_hyperbolic_surface_2/doc/Triangulation_on_hyperbolic_surface_2/PackageDescription.txt +++ b/Triangulation_on_hyperbolic_surface_2/doc/Triangulation_on_hyperbolic_surface_2/PackageDescription.txt @@ -29,7 +29,7 @@ \cgalPkgDependsOn{\ref PkgCombinatorialMaps} \cgalPkgBib{cgal:ddpt-thss} \cgalPkgLicense{\ref licensesGPL "GPL"} -\cgalPkgDemo{2D Triangulations on Hyperbolic Surfaces,nofilefornow.zip} +\cgalPkgDemo{2D Triangulations on Hyperbolic Surfaces,triangulation_on_hyperbolic_surface_2.zip} \cgalPkgShortInfoEnd \cgalPkgDescriptionEnd From 3e1a8c110df684055b4da2b514ff7257381e3e7e Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 13 Nov 2025 07:26:05 +0000 Subject: [PATCH 44/51] (std::numeric_limits::max)() --- .../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 eea97494e07..838ea7ccc56 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 @@ -460,7 +460,7 @@ int merge_facets_region_growing(Mesh& mesh, } } if(!dump_surface_mesh_after_merge_filename.empty()) { - static constexpr auto max_size_t = std::numeric_limits::max(); + static constexpr auto max_size_t = (std::numeric_limits::max)(); auto [corner_id_map, corner_id_map_ok] = mesh.add_property_map("v:corner_id", max_size_t); assert(corner_id_map_ok); From 18e5836373e1a9449c878b755221434f880ef9aa Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 13 Nov 2025 09:44:12 +0100 Subject: [PATCH 45/51] fix usage of That C++20 header is already conditionally included bu ``. See https://github.com/lrineau/cgal/blob/e6b4d83cff944772ea5ed67bc414b61547d86432/Installation/include/CGAL/config.h#L322 --- Installation/include/CGAL/config.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Installation/include/CGAL/config.h b/Installation/include/CGAL/config.h index dfc36694740..474ee926a46 100644 --- a/Installation/include/CGAL/config.h +++ b/Installation/include/CGAL/config.h @@ -462,10 +462,6 @@ namespace CGAL { } // end of the temporary compatibility with CGAL-4.14 #endif // CGAL_NO_DEPRECATED_CODE -#if __has_include() -# include -#endif - namespace CGAL { // Typedef for the type of nullptr. From ef6f9d8c3cdf64edd77ebc5d96679f7c328efccf Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 13 Nov 2025 09:48:30 +0100 Subject: [PATCH 46/51] fix compilation error without C++20 --- .../Constrained_triangulation_3/cdt_3_from_off.cpp | 12 ++++++------ 1 file changed, 6 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 838ea7ccc56..9e89127563b 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 @@ -377,12 +377,12 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& })); #else auto is_facet_constrained = [&](auto f) { return cdt.is_facet_constrained(f); }; - auto it_end = cdt.finite_facets_end(); - cdt.write_facets(dump, cdt.triangulation(), - CGAL::make_range( - boost::make_filter_iterator(is_facet_constrained,cdt.finite_facets_begin(), it_end), - boost::make_filter_iterator(is_facet_constrained,it_end, it_end))); - + 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 } }; From c856e4066229320f2b522e244f274bd6f4dfe53b Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 13 Nov 2025 09:50:21 +0100 Subject: [PATCH 47/51] re-add to fix clangd warnings --- .../CGAL/Conforming_constrained_Delaunay_triangulation_3.h | 3 +++ .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 3 +++ 2 files changed, 6 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 4ad22b9eed8..6f3207a5852 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 @@ -85,6 +85,9 @@ #include #include #include +#if __has_include() +# include +#endif #if CGAL_CXX20 && __has_include() # include #endif 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 9e89127563b..329bac1c2ed 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 @@ -30,6 +30,9 @@ #include #include #include +#if __has_include() +# include +#endif #if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L #include From e0a720452d4080dd8e5841810f83ad77a0ab6127 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 13 Nov 2025 09:54:05 +0100 Subject: [PATCH 48/51] fix a warning --- .../include/CGAL/Conforming_Delaunay_triangulation_3.h | 2 +- 1 file changed, 1 insertion(+), 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 936273cb2fa..a8666d4b845 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -254,7 +254,7 @@ public: 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_point_and_info_tag with_point_and_info{ { {-1} } }; Conforming_Delaunay_triangulation_3(const Geom_traits& gt = Geom_traits()) : T_3(gt) From e98fa21cc9e003ab0cc2c5f59e6c6b5babcc31f1 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Fri, 14 Nov 2025 06:58:46 +0000 Subject: [PATCH 49/51] Fix warning in CGAL-6.2-Ic-41 --- .../test/Constrained_triangulation_3/cdt_3_from_off.cpp | 4 ++-- 1 file changed, 2 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 329bac1c2ed..72575e48a01 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 @@ -449,10 +449,10 @@ int merge_facets_region_growing(Mesh& mesh, double coplanar_polygon_max_angle, const std::string& dump_surface_mesh_after_merge_filename) { namespace np = CGAL::parameters; - int number_of_patches = CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces( + int number_of_patches = static_cast(CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces( mesh, pmaps.patch_id_map, np::maximum_distance(coplanar_polygon_max_distance) - .maximum_angle(coplanar_polygon_max_angle)); + .maximum_angle(coplanar_polygon_max_angle))); for(auto f: faces(mesh)) { if(get(pmaps.patch_id_map, f) < 0) { std::cerr << "warning: face " << f << " has no patch id! Reassign it to " << number_of_patches << '\n'; From cda931ec46a2257dc0de53c7d13d6d4ef326efd8 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Thu, 20 Nov 2025 15:23:40 +0100 Subject: [PATCH 50/51] fix a warning --- .../include/CGAL/Conforming_Delaunay_triangulation_3.h | 2 +- 1 file changed, 1 insertion(+), 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 a8666d4b845..8cafef40b20 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -252,7 +252,7 @@ public: 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_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} } }; From 7bf5687d5d4055d9f3355c12089457caedb829f2 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 12 Nov 2025 11:37:52 +0100 Subject: [PATCH 51/51] Remove -fexperimental-library --- .../test/Constrained_triangulation_3/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index ac5089404b2..ffd762b5c9c 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -15,9 +15,6 @@ include(CGAL_setup_tl-excepted) 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 tl::expected) -if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") - target_compile_options(CDT_3_dependencies INTERFACE -fexperimental-library) -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)