diff --git a/.gitignore b/.gitignore index 74e4fdc1f4a0..e6ecde661dcd 100644 --- a/.gitignore +++ b/.gitignore @@ -1077,6 +1077,12 @@ Makefile *.sln *.depends +# Output files of tetgen +*.edge +*.cell +*.node +*.smesh + # The rest: "various" things that I do not know how to classify # -- Laurent Rineau, 2013/01/11 *.ncb @@ -1163,10 +1169,11 @@ Data/data/meshes/*.*.vtk Data/data/meshes/*.log Data/data/meshes/*.off-cdt-output.off dump_*.off +dump_*.ply dump_*.txt -dump-*.binary.cgal -dump-*.polylines.txt -dump-*.xyz +dump*.binary.cgal +dump*.polylines.txt +dump*.xyz dump.off.mesh log.txt patches_after_merge.ply diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h index 06f383e0ff1d..fdbecdd3f23a 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_Delaunay_triangulation_3.h @@ -72,7 +72,10 @@ namespace CGAL { namespace CDT_3 { struct Debug_options { - enum class Flags { + enum class Flag; + bool get(Flag f) const { return flags[static_cast(f)]; } + auto get(Flag f) { return flags[static_cast(f)]; } + enum class Flag { Steiner_points = 0, Steiner_points_construction, conforming, @@ -91,71 +94,92 @@ struct Debug_options { debug_geometric_errors, debug_polygon_insertion, debug_restore_faces, + debug_move_Steiner_vertices_bit1, + debug_move_Steiner_vertices_bit2, display_statistics, use_epeck_for_normals, use_epeck_for_Steiner_points, + move_Steiner_vertices_allow_negative_tets, nb_of_flags }; - bool Steiner_points_construction() const { return flags[static_cast(Flags::Steiner_points_construction)]; } - void Steiner_points_construction(bool b) { flags.set(static_cast(Flags::Steiner_points_construction), b); } - bool Steiner_points() const { return flags[static_cast(Flags::Steiner_points)]; } - void Steiner_points(bool b) { flags.set(static_cast(Flags::Steiner_points), b); } + using Flags = std::bitset(Flag::nb_of_flags)>; - bool input_faces() const { return flags[static_cast(Flags::input_faces)]; } - void input_faces(bool b) { flags.set(static_cast(Flags::input_faces), b); } + bool Steiner_points_construction() const { return get(Flag::Steiner_points_construction); } + void Steiner_points_construction(bool b) { get(Flag::Steiner_points_construction) = b; } - bool missing_region() const { return flags[static_cast(Flags::missing_region)]; } - void missing_region(bool b) { flags.set(static_cast(Flags::missing_region), b); } + bool Steiner_points() const { return get(Flag::Steiner_points); } + void Steiner_points(bool b) { get(Flag::Steiner_points) = b; } - bool regions() const { return flags[static_cast(Flags::regions)]; } - void regions(bool b) { flags.set(static_cast(Flags::regions), b); } + bool input_faces() const { return get(Flag::input_faces); } + void input_faces(bool b) { get(Flag::input_faces) = b; } - bool copy_triangulation_into_hole() const { return flags[static_cast(Flags::copy_triangulation_into_hole)]; } - void copy_triangulation_into_hole(bool b) { flags.set(static_cast(Flags::copy_triangulation_into_hole), b); } + bool missing_region() const { return get(Flag::missing_region); } + void missing_region(bool b) { get(Flag::missing_region) = b; } - bool validity() const { return flags[static_cast(Flags::validity)]; } - void validity(bool b) { flags.set(static_cast(Flags::validity), b); } + bool regions() const { return get(Flag::regions); } + void regions(bool b) { get(Flag::regions) = b; } + bool copy_triangulation_into_hole() const { return get(Flag::copy_triangulation_into_hole); } + void copy_triangulation_into_hole(bool b) { get(Flag::copy_triangulation_into_hole) = b; } - bool finite_edges_map() const { return flags[static_cast(Flags::debug_finite_edges_map)]; } - void finite_edges_map(bool b) { flags.set(static_cast(Flags::debug_finite_edges_map), b); } + bool validity() const { return get(Flag::validity); } + void validity(bool b) { get(Flag::validity) = b; } - bool subconstraints_to_conform() const { return flags[static_cast(Flags::debug_subconstraints_to_conform)]; } - void subconstraints_to_conform(bool b) { flags.set(static_cast(Flags::debug_subconstraints_to_conform), b); } + bool finite_edges_map() const { return get(Flag::debug_finite_edges_map); } + void finite_edges_map(bool b) { get(Flag::debug_finite_edges_map) = b; } - bool use_finite_edges_map_flag() const { return flags[static_cast(Flags::use_finite_edges_map)]; } - void use_finite_edges_map(bool b) { flags.set(static_cast(Flags::use_finite_edges_map), b); } + bool subconstraints_to_conform() const { return get(Flag::debug_subconstraints_to_conform); } + void subconstraints_to_conform(bool b) { get(Flag::debug_subconstraints_to_conform) = b; } - bool verbose_special_cases() const { return flags[static_cast(Flags::verbose_special_cases)]; } - void verbose_special_cases(bool b) { flags.set(static_cast(Flags::verbose_special_cases), b); } + bool use_finite_edges_map_flag() const { return get(Flag::use_finite_edges_map); } + void use_finite_edges_map(bool b) { get(Flag::use_finite_edges_map) = b; } - bool encroaching_vertices() const { return flags[static_cast(Flags::debug_encroaching_vertices)]; } - void encroaching_vertices(bool b) { flags.set(static_cast(Flags::debug_encroaching_vertices), b); } + bool verbose_special_cases() const { return get(Flag::verbose_special_cases); } + void verbose_special_cases(bool b) { get(Flag::verbose_special_cases) = b; } - bool conforming_validation() const { return flags[static_cast(Flags::debug_conforming_validation)]; } - void conforming_validation(bool b) { flags.set(static_cast(Flags::debug_conforming_validation), b); } + bool encroaching_vertices() const { return get(Flag::debug_encroaching_vertices); } + void encroaching_vertices(bool b) { get(Flag::debug_encroaching_vertices) = b; } - bool constraint_hierarchy() const { return flags[static_cast(Flags::debug_constraint_hierarchy)]; } - void constraint_hierarchy(bool b) { flags.set(static_cast(Flags::debug_constraint_hierarchy), b); } + bool conforming_validation() const { return get(Flag::debug_conforming_validation); } + void conforming_validation(bool b) { get(Flag::debug_conforming_validation) = b; } - bool geometric_errors() const { return flags[static_cast(Flags::debug_geometric_errors)]; } - void geometric_errors(bool b) { flags.set(static_cast(Flags::debug_geometric_errors), b); } + bool constraint_hierarchy() const { return get(Flag::debug_constraint_hierarchy); } + void constraint_hierarchy(bool b) { get(Flag::debug_constraint_hierarchy) = b; } - bool polygon_insertion() const { return flags[static_cast(Flags::debug_polygon_insertion)]; } - void polygon_insertion(bool b) { flags.set(static_cast(Flags::debug_polygon_insertion), b); } + bool geometric_errors() const { return get(Flag::debug_geometric_errors); } + void geometric_errors(bool b) { get(Flag::debug_geometric_errors) = b; } - bool restore_faces() const { return flags[static_cast(Flags::debug_restore_faces)]; } - void restore_faces(bool b) { flags.set(static_cast(Flags::debug_restore_faces), b); } + bool polygon_insertion() const { return get(Flag::debug_polygon_insertion); } + void polygon_insertion(bool b) { get(Flag::debug_polygon_insertion) = b; } - bool display_statistics() const { return flags[static_cast(Flags::display_statistics)]; } - void display_statistics(bool b) { flags.set(static_cast(Flags::display_statistics), b); } + bool restore_faces() const { return get(Flag::debug_restore_faces); } + void restore_faces(bool b) { get(Flag::debug_restore_faces) = b; } - bool use_epeck_for_normals() const { return flags[static_cast(Flags::use_epeck_for_normals)]; } - void use_epeck_for_normals(bool b) { flags.set(static_cast(Flags::use_epeck_for_normals), b); } + bool move_Steiner_vertices() const { + return get(Flag::debug_move_Steiner_vertices_bit1) || get(Flag::debug_move_Steiner_vertices_bit2); + } + + unsigned int move_Steiner_vertices_level() const { + return get(Flag::debug_move_Steiner_vertices_bit1) + 2 * get(Flag::debug_move_Steiner_vertices_bit2); + } + void move_Steiner_vertices(unsigned int level) { + CGAL_assertion(level <= 3); + get(Flag::debug_move_Steiner_vertices_bit1) = level & 1; + get(Flag::debug_move_Steiner_vertices_bit2) = (level >> 1) & 1; + } + + bool move_Steiner_vertices_allow_negative_tets() const { return get(Flag::move_Steiner_vertices_allow_negative_tets); } + void move_Steiner_vertices_allow_negative_tets(bool b) { get(Flag::move_Steiner_vertices_allow_negative_tets) = b; } + + bool display_statistics() const { return get(Flag::display_statistics); } + void display_statistics(bool b) { get(Flag::display_statistics) = b; } - bool use_epeck_for_Steiner_points() const { return flags[static_cast(Flags::use_epeck_for_Steiner_points)]; } - void use_epeck_for_Steiner_points(bool b) { flags.set(static_cast(Flags::use_epeck_for_Steiner_points), b); } + bool use_epeck_for_normals() const { return get(Flag::use_epeck_for_normals); } + void use_epeck_for_normals(bool b) { get(Flag::use_epeck_for_normals) = b; } + + bool use_epeck_for_Steiner_points() const { return get(Flag::use_epeck_for_Steiner_points); } + void use_epeck_for_Steiner_points(bool b) { get(Flag::use_epeck_for_Steiner_points) = b; } double segment_vertex_epsilon() const { return segment_vertex_epsilon_; } void set_segment_vertex_epsilon(double eps) { segment_vertex_epsilon_ = eps; } @@ -164,7 +188,7 @@ struct Debug_options { void set_vertex_vertex_epsilon(double eps) { vertex_vertex_epsilon_ = eps; } private: - std::bitset(Flags::nb_of_flags)> flags{}; + Flags flags{}; double segment_vertex_epsilon_ = 0.0; double vertex_vertex_epsilon_ = 0.0; }; // end struct Debug_options @@ -173,13 +197,14 @@ namespace internal { inline auto& tasks_manager() { struct Tasks_manager { - enum { + enum class Tm { READ_INPUT = 0, MERGE_FACETS, INSERT_VERTICES, COMPUTE_DISTANCES, CONFORMING, CDT, + MOVE_STEINER_VERTICES, OUTPUT, VALIDATION, NB_TASKS @@ -187,18 +212,19 @@ inline auto& tasks_manager() { #if CGAL_USE_ITT __itt_domain* cdt_3_domain = __itt_domain_create("org.cgal.CDT_3"); - const std::array<__itt_string_handle*, NB_TASKS> task_handles = { + const std::array<__itt_string_handle*, static_cast(Tm::NB_TASKS)> task_handles = { __itt_string_handle_create("CDT_3: read input file"), __itt_string_handle_create("CDT_3: merge facets"), __itt_string_handle_create("CDT_3: insert vertices"), __itt_string_handle_create("CDT_3: compute distances"), __itt_string_handle_create("CDT_3: conforming"), __itt_string_handle_create("CDT_3: cdt"), + __itt_string_handle_create("CDT_3: move Steiner vertices"), __itt_string_handle_create("CDT_3: outputs"), __itt_string_handle_create("CDT_3: validation") }; #endif - std::array timers{}; + std::array(Tm::NB_TASKS)> timers{}; struct Scope_guard { Tasks_manager *instance = nullptr; int task_id; @@ -222,18 +248,19 @@ inline auto& tasks_manager() { } }; - Scope_guard make_task_scope_guard(int task_id) { - return Scope_guard(this, task_id); + Scope_guard make_task_scope_guard(Tm task_id) { + return Scope_guard(this, static_cast(task_id)); } - Scope_guard READ_INPUT_TASK_guard() { return make_task_scope_guard(READ_INPUT); } - Scope_guard MERGE_FACETS_TASK_guard() { return make_task_scope_guard(MERGE_FACETS); } - Scope_guard INSERT_VERTICES_TASK_guard() { return make_task_scope_guard(INSERT_VERTICES); } - Scope_guard COMPUTE_DISTANCES_TASK_guard() { return make_task_scope_guard(COMPUTE_DISTANCES); } - Scope_guard CONFORMING_TASK_guard() { return make_task_scope_guard(CONFORMING); } - Scope_guard CDT_TASK_guard() { return make_task_scope_guard(CDT); } - Scope_guard OUTPUT_TASK_guard() { return make_task_scope_guard(OUTPUT); } - Scope_guard VALIDATION_TASK_guard() { return make_task_scope_guard(VALIDATION); } + Scope_guard READ_INPUT_TASK_guard() { return make_task_scope_guard(Tm::READ_INPUT); } + Scope_guard MERGE_FACETS_TASK_guard() { return make_task_scope_guard(Tm::MERGE_FACETS); } + Scope_guard INSERT_VERTICES_TASK_guard() { return make_task_scope_guard(Tm::INSERT_VERTICES); } + Scope_guard COMPUTE_DISTANCES_TASK_guard() { return make_task_scope_guard(Tm::COMPUTE_DISTANCES); } + Scope_guard CONFORMING_TASK_guard() { return make_task_scope_guard(Tm::CONFORMING); } + Scope_guard CDT_TASK_guard() { return make_task_scope_guard(Tm::CDT); } + Scope_guard MOVE_STEINER_VERTICES_TASK_guard() { return make_task_scope_guard(Tm::MOVE_STEINER_VERTICES); } + Scope_guard OUTPUT_TASK_guard() { return make_task_scope_guard(Tm::OUTPUT); } + Scope_guard VALIDATION_TASK_guard() { return make_task_scope_guard(Tm::VALIDATION); } }; // end struct Intel_OneAPI_ITT_API @@ -273,6 +300,10 @@ inline auto CDT_3_OUTPUT_TASK_guard() { return CDT_3::internal::tasks_manager().OUTPUT_TASK_guard(); } +inline auto CDT_3_MOVE_STEINER_VERTICES_TASK_guard() { + return CDT_3::internal::tasks_manager().MOVE_STEINER_VERTICES_TASK_guard(); +} + inline auto CDT_3_VALIDATION_TASK_guard() { return CDT_3::internal::tasks_manager().VALIDATION_TASK_guard(); } @@ -291,9 +322,24 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { using Line = typename T_3::Geom_traits::Line_3; using Locate_type = typename T_3::Locate_type; - inline static With_offset_tag with_offset{ -1 }; - inline static With_point_tag with_point{ {-1} }; - inline static With_point_and_info_tag with_point_and_info{ { {-1} } }; + inline static With_offset_tag with_offset_tag{ -1 }; + inline static With_point_tag with_point_tag{ {-1} }; + inline static With_point_and_info_tag with_point_and_info_tag{ { {-1} } }; + + template + static auto with_offset(T&& arg) { + return IO::oformat(std::forward(arg), with_offset_tag); + }; + + template + static auto with_point(T&& arg) { + return IO::oformat(std::forward(arg), with_point_tag); + }; + + template + static auto with_point_and_info(T&& arg) { + return IO::oformat(std::forward(arg), with_point_and_info_tag); + }; Conforming_Delaunay_triangulation_3(const Geom_traits& gt = Geom_traits()) : T_3(gt) @@ -318,16 +364,13 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { using Subconstraint = typename Constraint_hierarchy::Subconstraint; auto display_vert(Vertex_handle v) const{ - std::stringstream os; - os.precision(17); - os << IO::oformat(v, with_point); - return os.str(); + return IO::oformat(v, with_point_tag); } auto display_subcstr(Subconstraint subconstraint) const { auto [va, vb] = subconstraint; std::stringstream os; - os << "(" << IO::oformat(va, with_offset) << ", " << IO::oformat(vb, with_offset) << ")" + os << "(" << with_offset(va) << ", " << with_offset(vb) << ")" << ": [ " << display_vert(va) << " - " << display_vert(vb) << " ]"; return os.str(); } @@ -564,6 +607,8 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { public: CDT_3::Debug_options& debug() { return debug_options_; } const CDT_3::Debug_options& debug() const { return debug_options_; } + CDT_3::Debug_options& options() { return debug_options_; } + const CDT_3::Debug_options& options() const { return debug_options_; } // Backward compatibility wrappers (deprecated, use debug().method() instead) bool use_finite_edges_map() const { return update_all_finite_edges_ && debug_options_.use_finite_edges_map_flag(); } @@ -624,9 +669,9 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { const auto is_edge = this->is_edge(va, vb); if constexpr (cdt_3_can_use_cxx20_format()) if(debug().conforming_validation()) { std::cerr << cdt_3_format("is_conforming>> Edge is 3D: {} ({} , {})\n", - is_edge, - CGAL::IO::oformat(va, with_point_and_info), - CGAL::IO::oformat(vb, with_point_and_info)); + is_edge, + with_point_and_info(va), + with_point_and_info(vb)); } return is_edge; }); @@ -810,7 +855,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { ss << "insert_Steiner_point_on_subconstraint: Steiner point coincides with an existing vertex\n"; ss << " -> Steiner point: " << steiner_pt << '\n'; ss << " on constraint: " << display_vert(c_va) << " - " << display_vert(c_vb) << '\n'; - ss << " -> existing vertex: " << IO::oformat(other_v, with_point_and_info) << '\n'; + ss << " -> existing vertex: " << with_point_and_info(other_v) << '\n'; if(other_v->ccdt_3_data().number_of_incident_constraints() > 0) { const auto c_id = other_v->ccdt_3_data().constrained_polyline_id(*this); const auto [c_va, c_vb] = constraint_extremities(c_id); @@ -850,7 +895,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { insert_Steiner_point_on_subconstraint(steiner_pt, hint, subconstraint, constraint, visitor); if(debug().Steiner_points()) { const auto [c_start, c_end] = constraint_extremities(constraint); - std::cerr << "(" << IO::oformat(va, with_offset) << ", " << IO::oformat(vb, with_offset) << ")"; + std::cerr << "(" << with_offset(va) << ", " << with_offset(vb) << ")"; std::cerr << ": [ " << display_vert(c_start) << " - " << display_vert(c_end) << " ] "; std::cerr << " new vertex " << display_vert(v) << '\n'; } @@ -863,6 +908,7 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { return false; } +public: Constrained_polyline_id constraint_from_extremities(Vertex_handle va, Vertex_handle vb) const { if(va->ccdt_3_data().number_of_incident_constraints() == 0 || vb->ccdt_3_data().number_of_incident_constraints() == 0) { @@ -882,15 +928,16 @@ class Conforming_Delaunay_triangulation_3 : public T_3 { return c_id; } +protected: auto constraint_extremities(Constrained_polyline_id c_id) const { - CGAL_assertion(this->constraint_hierarchy.vertices_in_constraint_begin(c_id) != - this->constraint_hierarchy.vertices_in_constraint_end(c_id)); + CGAL_assertion(this->constraint_hierarchy.is_valid_constraint(c_id)); if(debug().constraint_hierarchy()) { std::cerr << "constraint " << static_cast(c_id.vl_ptr()) << " has " << c_id.vl_ptr()->skip_size() << " vertices\n"; } const auto begin = this->constraint_hierarchy.vertices_in_constraint_begin(c_id); const auto end = this->constraint_hierarchy.vertices_in_constraint_end(c_id); + CGAL_assertion(begin != end); const auto c_va = *begin; const auto c_vb = *std::prev(end); return std::make_pair(c_va, c_vb); diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h index 0bdc28ddb442..7d8e31742ee1 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_3.h @@ -12,7 +12,6 @@ #ifndef CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H #define CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H -#include #include #include @@ -44,8 +43,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -53,8 +54,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -125,7 +128,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -133,6 +139,7 @@ #include #include #include +#include #include #include #include @@ -583,20 +590,24 @@ struct Output_rep, With_point_and_info_t } std::ostream& operator()(std::ostream& out) const { - out << Time_stamper::display_id(this->it.operator->(), offset); - if(this->it.operator->() != nullptr) { - out << (this->it->ccdt_3_data().is_Steiner_vertex_on_edge() ? "(Steiner)" : "") - << (this->it->ccdt_3_data().is_Steiner_vertex_in_face() ? "(Steiner in face)" : "") - << "= " << this->it->point(); - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_BORDER)) out << " (region border)"; - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_INSIDE)) out << " (inside region)"; - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY)) out << " (cavity vertex)"; - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY_ABOVE)) out << " (vertex above)"; - if(this->it->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY_BELOW)) out << " (vertex below)"; - return out; - } - else - return out; + auto v_ptr = this->it.operator->(); + if(v_ptr == nullptr) + return out << "nullptr"; + if(Time_stamper::time_stamp(v_ptr) == 0) + return out << "# = infinite_vertex()"; + + out << Time_stamper::display_id(v_ptr, offset); + + out << (v_ptr->ccdt_3_data().is_Steiner_vertex_on_edge() ? "(Steiner)" : "") + << (v_ptr->ccdt_3_data().is_Steiner_vertex_in_face() ? "(Steiner in face)" : "") + << (v_ptr->ccdt_3_data().is_Steiner_vertex_in_volume() ? "(Steiner in volume)" : "") + << "= " << v_ptr->point(); + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_BORDER)) out << " (region border)"; + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::REGION_INSIDE)) out << " (inside region)"; + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY)) out << " (cavity vertex)"; + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY_ABOVE)) out << " (vertex above)"; + if(v_ptr->ccdt_3_data().is_marked(CDT_3_vertex_marker::CAVITY_BELOW)) out << " (vertex below)"; + return out; } }; @@ -646,14 +657,213 @@ class Conforming_constrained_Delaunay_triangulation_3 public: using Vertex_handle = typename Triangulation::Vertex_handle; + using Edge = typename Triangulation::Edge; + using Facet = typename Triangulation::Facet; + using Cell_handle = typename Triangulation::Cell_handle; + + using Concurrency_tag = typename Triangulation::Concurrency_tag; #ifndef DOXYGEN_RUNNING using Constrained_polyline_id = typename CDT_3_impl::Constrained_polyline_id; #endif // not DOXYGEN_RUNNING using size_type = typename Triangulation::size_type; + using Subdomain_index = int; + using Surface_patch_index = CDT_3_signed_index; + using Curve_index = std::size_t;//Constrained_polyline_id::size_type; + using Corner_index = int;//check that + using Index = typename Triangulation::Triangulation_data_structure::Vertex::Index; + + void rescan_after_load_of_triangulation() { /*todo*/ } + + bool is_in_complex(const Cell_handle& c) const + { + return !triangulation().is_infinite(c); + for(const auto v : triangulation().vertices(c)) + { + if(impl().vertex_type(v) == CDT_3_vertex_type::BBOX) + return false; + } + return true; + } + bool is_in_complex(const Facet& f) const + { + return is_in_complex(f.first, f.second); + } + bool is_in_complex(const Cell_handle c, const int i) const + { + return is_facet_constrained({c, i}); + } + bool is_in_complex(const Vertex_handle u, const Vertex_handle v) const + { + Constrained_polyline_id c_id = impl().constraint_from_extremities(u, v); + return (c_id != Constrained_polyline_id{}); + } + bool is_in_complex(const Edge& e) const + { + return is_in_complex(e.first->vertex(e.second), e.first->vertex(e.third)); + } + bool is_in_complex(const Vertex_handle& v) const + { + return impl().vertex_type(v) == CDT_3_vertex_type::INPUT_VERTEX; + } + Subdomain_index subdomain_index(const Cell_handle& c) const + { + return is_in_complex(c) ? Subdomain_index{1} : Subdomain_index(); + } + Surface_patch_index surface_patch_index(const Cell_handle c, const int i) const + { + return surface_patch_index({c, i}); + } + Surface_patch_index surface_patch_index(const Facet& f) const + { + Surface_patch_index index = face_constraint_index(f.first, f.second) + 1; + // valid face_constraint_index() starts at 0 + // Surface_patch_index 0 is for "not a surface" + return index; + } + void set_surface_patch_index(const Facet& f, const Surface_patch_index& index) + { + const Facet& mf = impl().mirror_facet(f); + + // warning : cdt2_index may be outdated + f.first->ccdt_3_data().set_face_constraint_index(f.second, index); + mf.first->ccdt_3_data().set_face_constraint_index(mf.second, index); + } + Curve_index curve_index(const Vertex_handle v1, const Vertex_handle v2) const + { + return impl().constraint_from_extremities(v1, v2).index(); + } + Curve_index curve_index(const Edge& e) const + { + return curve_index(e.first->vertex(e.second), e.first->vertex(e.third)); + } + Index index(const Vertex_handle&) const + { + return 1;//not used in tetrahedral remeshing outside of sizing field + } + int in_dimension(const Vertex_handle& v) const + { + auto type = impl().vertex_type(v); + switch(type) + { + case CDT_3_vertex_type::INPUT_VERTEX : + return 0; + case CDT_3_vertex_type::STEINER_ON_EDGE: + return 1; + case CDT_3_vertex_type::STEINER_IN_FACE: + return 2; + case CDT_3_vertex_type::STEINER_IN_VOLUME: + case CDT_3_vertex_type::FREE: + case CDT_3_vertex_type::BBOX: + return 3; + } + CGAL_unreachable(); + } + + void add_to_complex(const Cell_handle c, const Subdomain_index& index) + { + CGAL_precondition(!triangulation().is_infinite(c)); + CGAL_precondition(index == Subdomain_index{1}); + CGAL_USE(index); + } + void set_subdomain_index(const Cell_handle, const Subdomain_index&) + { + ; + } + void add_to_complex(const Facet& f, const Surface_patch_index& index) + { + //todo : use index and cdt2_index too + impl().set_facet_constrained(f, index, {}); + set_surface_patch_index(f, index); + } + void add_to_complex(const Edge& e, const Curve_index& index) + { + add_to_complex(e.first->vertex(e.second), e.first->vertex(e.third), index); + } + void add_to_complex(const Vertex_handle& v, const Corner_index&) + { + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::INPUT_VERTEX); + } + void add_to_complex(const Vertex_handle, const Vertex_handle, const Curve_index&) + { +// impl().insert_constrained_edge(v1, v2, false/*restore Delaunay*/); + } + void remove_from_complex(const Cell_handle c) + { + CGAL_precondition(!triangulation().is_infinite(c)); + } + void remove_from_complex(const Facet& f) + { + CGAL_precondition(is_in_complex(f.first, f.second)); + const Facet mf = impl().mirror_facet(f); + impl().set_facet_as_not_constrained(f); + impl().set_facet_as_not_constrained(mf); + } + void remove_from_complex(const Cell_handle c, const int i) + { + remove_from_complex({c, i}); + } + void remove_from_complex(const Edge&) + { +// remove_from_complex(e.first->vertex(e.second), e.first->vertex(e.third)); + } + void remove_from_complex(const Vertex_handle, const Vertex_handle) + { + //should not happen during tetrahedral remeshing since we keep constrained edges + CGAL_error_msg("removing a constrained edge is not supported"); + } + + void set_index(Vertex_handle, const Index&) const + { + // nothing to do since index is not used in tetrahedral remeshing outside of sizing field + } + void set_dimension(Vertex_handle v, const int dim) const + { + switch (dim) + { + case 0: + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::INPUT_VERTEX); + break; + case 1: + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_ON_EDGE); + break; + case 2: + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_FACE); + break; + case 3: + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); + break; + }; + } + + using Edges_in_complex_iterator = typename CDT_3_impl::Complex_edges_iterator; + auto edges_in_complex() const + { + return impl().edges_in_complex(); + } + auto edges_in_complex_begin() const { using std::begin; return begin(impl().edges_in_complex()); } + auto edges_in_complex_end() const { using std::end; return end(impl().edges_in_complex()); } + using Facets_in_complex_iterator = typename CDT_3_impl::Constrained_facets_iterator; + auto facets_in_complex() const + { + return impl().constrained_facets(); + } + auto facets_in_complex_begin() const { using std::begin; return begin(impl().constrained_facets()); } + auto facets_in_complex_end() const { using std::end; return end(impl().constrained_facets()); } + auto cells_in_complex() const + { + return impl().finite_cell_handles(); + } + std::size_t number_of_edges() const + { + const auto& [ebegin, eend] = edges_in_complex(); + return std::distance(ebegin, eend); + } + /// \cond SKIP_IN_MANUAL - CDT_3_impl& impl() { return cdt_impl; } + CDT_3_impl& impl() { return cdt_impl; } + const CDT_3_impl& impl() const { return cdt_impl; } /// \endcond public: /** \name Constructors @@ -913,9 +1123,6 @@ class Conforming_constrained_Delaunay_triangulation_3 std::cout << "Number of vertices after CDT: " << cdt_impl.number_of_vertices() << "\n\n"; } } - - // std::cerr << cdt_3_format("cdt_impl: {} vertices, {} cells\n", cdt_impl.number_of_vertices(), - // cdt_impl.number_of_cells()); } /*! @@ -1018,6 +1225,15 @@ class Conforming_constrained_Delaunay_triangulation_3 /// @} // end constructors section + /** + * swaps `this` and `rhs` + */ + void swap(Conforming_constrained_Delaunay_triangulation_3& rhs) + { + std::swap(cdt_impl, rhs.cdt_impl); + } + + // ----------------------- // Accessors // ----------------------- @@ -1038,6 +1254,8 @@ class Conforming_constrained_Delaunay_triangulation_3 return cdt_impl; } + Triangulation& triangulation() & { return cdt_impl; } + /*! * \brief moves and returns the underlying triangulation, then clears the object. * @@ -1084,24 +1302,24 @@ class Conforming_constrained_Delaunay_triangulation_3 * \link TriangulationDataStructure_3::Facet `Triangulation::Facet`\endlink, * as defined by its triangulation data structure. */ - bool is_facet_constrained(const typename Triangulation::Facet& f) const { + bool is_facet_constrained(const Facet& f) const { return cdt_impl.is_facet_constrained(f); } /*! * \brief same as `is_facet_constrained(f)` with `f` being `Triangulation::Facet(ch, index)`. */ - bool is_facet_constrained(typename Triangulation::Cell_handle ch, int index) const { - return cdt_impl.is_facet_constrained(typename Triangulation::Facet(ch, index)); + bool is_facet_constrained(Cell_handle ch, int index) const { + return cdt_impl.is_facet_constrained(Facet(ch, index)); } /*! * @brief same as `face_constraint_index(f)` with `f` being `Triangulation::Facet(ch, index)`. * @pre \link Conforming_constrained_Delaunay_triangulation_3::is_facet_constrained `is_facet_constrained`\endlink(`f`) */ - CDT_3_signed_index face_constraint_index(typename Triangulation::Cell_handle ch, int i) const + CDT_3_signed_index face_constraint_index(Cell_handle ch, int i) const { - return ch->ccdt_3_data().face_constraint_index(i); + return cdt_impl.face_constraint_index(ch, i); } /*! @@ -1109,9 +1327,9 @@ class Conforming_constrained_Delaunay_triangulation_3 * facet \p f * @pre \link Conforming_constrained_Delaunay_triangulation_3::is_facet_constrained `is_facet_constrained`\endlink(`f`) */ - CDT_3_signed_index face_constraint_index(const typename Triangulation::Facet& f) const + CDT_3_signed_index face_constraint_index(const Facet& f) const { - return face_constraint_index(f.first, f.second); + return cdt_impl.face_constraint_index(f.first, f.second); } /*! @@ -1154,6 +1372,13 @@ class Conforming_constrained_Delaunay_triangulation_3 /// \name Checking /// These methods are mainly a debugging help for the users of advanced features. /// @{ + /*! + \brief returns whether the triangulation is valid. + When `verbose` is set to `true`, messages describing the first invalidity encountered are + printed. + */ + bool is_valid(bool verbose = false) const { return cdt_impl.is_valid(verbose); } + /// @} // ----------------------- @@ -1180,11 +1405,23 @@ class Conforming_constrained_Delaunay_triangulation_3 // Validation helpers bool is_conforming() const { return cdt_impl.is_conforming(); } - bool is_valid(bool verbose = false) const { return cdt_impl.is_valid(verbose); } // IO helpers for debugging bool write_missing_segments_file(std::ostream& out) { return cdt_impl.write_missing_segments_file(out); } void write_all_segments_file(std::ostream& out) { cdt_impl.write_all_segments_file(out); } + void dump_constrained_facets_to_off(std::string filename) const { cdt_impl.dump_constrained_facets_to_off(filename); } + void save_binary_file(std::string filename = "dump.binary.cgal") const { + cdt_impl.dump_triangulation(filename); + } + + // Post-processing helpers + /// \brief Moves Steiner vertices on the boundary to the volume. + /// + /// This function moves Steiner vertices that lie on edges or faces + /// of the constrained surface to inside the volume. + void move_Steiner_vertices_to_the_volume() { + cdt_impl.move_Steiner_vertices_to_the_volume(); + } // Lightweight accessors used by tests/tools size_type number_of_vertices() const { return static_cast(cdt_impl.number_of_vertices()); } @@ -1205,6 +1442,50 @@ class Conforming_constrained_Delaunay_triangulation_3 void write_facets(std::ostream& out, const CDT& cdt, FacetRange&& facets) const { cdt_impl.write_facets(out, cdt, std::forward(facets)); } + + auto statistics(std::string prefix = "") const { + using Stats_array = std::array(CDT_3_vertex_type::last) + 1>; + return IO::oformat([this, prefix=std::move(prefix)](std::ostream& out) -> auto& { + out << prefix << "Number of vertices: " << triangulation().number_of_vertices() << "\n"; + out << prefix << "Number of cells: " << triangulation().number_of_cells() << "\n"; + + auto nb = [nb_vertices_by_type = Stats_array{}](CDT_3_vertex_type type) mutable -> auto& { + return nb_vertices_by_type[static_cast(type)]; + }; + + for(auto v: triangulation().finite_vertex_handles()) { + ++nb(v->ccdt_3_data().vertex_type()); + } + out << prefix << "Number of vertices by type:\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::INPUT_VERTEX) << " input vertices\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_ON_EDGE) << " Steiner vertices on edges\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_IN_FACE) << " Steiner vertices in faces\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::STEINER_IN_VOLUME) << " Steiner vertices in volume\n"; + out << prefix << " - " << nb(CDT_3_vertex_type::FREE) << " free vertices\n"; + if(impl().options().move_Steiner_vertices_allow_negative_tets()) { + std::size_t nb_of_negative_tets = 0; + auto point = [this](typename Triangulation::Vertex_handle vh) { + return triangulation().point(vh); + }; + CGAL::unordered_flat_set vertices_with_negative_tets; + for(auto c: triangulation().finite_cell_handles()) { + const auto verts + = triangulation().vertices(c); + if(triangulation().orientation(point(verts[0]), point(verts[1]), point(verts[2]), point(verts[3])) < 0) { + ++nb_of_negative_tets; + for(auto v : verts) { + if(v->ccdt_3_data().vertex_type() == CDT_3_vertex_type::STEINER_IN_VOLUME) { + vertices_with_negative_tets.insert(v); + } + } + } + } + out << prefix << " - " << nb_of_negative_tets << " negative tetrahedra\n"; + out << prefix << " - " << vertices_with_negative_tets.size() << " Steiner vertices with negative tetrahedra around\n"; + } + return out; + }, IO_manip_tag{}); + } }; #ifndef DOXYGEN_RUNNING @@ -1226,6 +1507,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Geom_traits = typename T_3::Geom_traits; using Point_3 = typename T_3::Point; using Segment_3 = typename Geom_traits::Segment_3; + using Triangle_3 = typename Geom_traits::Triangle_3; using Vector_3 = typename Geom_traits::Vector_3; using Locate_type = typename T_3::Locate_type; using size_type = typename T_3::size_type; @@ -1234,6 +1516,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Cell_marker = CDT_3_cell_marker; using Face_index = CDT_3_signed_index; + static inline constexpr Face_index INVALID_FACE_INDEX = -1; using Conforming_Dt::Conforming_Dt; @@ -1250,6 +1533,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D bool operator()(Facet f) const { return cdt.is_facet_constrained(f); } + bool operator()(Edge e) const { + return Constrained_polyline_id{} + != cdt.constraint_from_extremities(e.first->vertex(e.second), + e.first->vertex(e.third)); + } }; using Constrained_facets_iterator = boost::filter_iterator; @@ -1264,6 +1552,21 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D Constrained_facets_range constrained_facets() const { return {constrained_facets_begin(), constrained_facets_end()}; } + + + using Complex_edges_iterator = boost::filter_iterator; + using Complex_edges_range = CGAL::Iterator_range; + + Complex_edges_iterator edges_in_complex_begin() const { + return {Is_constrained{*this}, this->finite_edges_begin(), this->finite_edges_end()}; + } + Complex_edges_iterator edges_in_complex_end() const { + return {Is_constrained{*this}, this->finite_edges_end(), this->finite_edges_end()}; + } + Complex_edges_range edges_in_complex() const { + return { edges_in_complex_begin(), edges_in_complex_end()}; + } + private: struct CDT_2_types { @@ -1279,9 +1582,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; using Color_value_type = std::int8_t; + enum class Face_is_outside_the_face : Color_value_type { UNKNOWN = -1, INSIDE = 0, OUTSIDE = 1 }; struct Face_info { - Color_value_type is_outside_the_face = -1; + Face_is_outside_the_face is_outside_the_face = Face_is_outside_the_face::UNKNOWN; Color_value_type is_in_region = 0; std::bitset<3> is_edge_also_in_3d_triangulation = 0; bool missing_subface = true; @@ -1295,6 +1599,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Itag = No_constraint_intersection_requiring_constructions_tag; using CDT_base = Constrained_Delaunay_triangulation_2; using CDT = Constrained_triangulation_plus_2; + using CT = typename CDT::Ctr; template struct CDT_2_dual_color_map { @@ -1306,15 +1611,23 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D friend reference get(CDT_2_dual_color_map, key_type fh) { return fh->info().*member_ptr; } friend void put(CDT_2_dual_color_map, key_type fh, value_type value) { fh->info().*member_ptr = value; } }; - using Color_map_is_outside_the_face = CDT_2_dual_color_map<&Face_info::is_outside_the_face>; using Color_map_is_in_region = CDT_2_dual_color_map<&Face_info::is_in_region>; }; // CDT_2_types using CDT_2 = typename CDT_2_types::CDT; + using CT_2 = typename CDT_2_types::CT; using CDT_2_traits = typename CDT_2_types::Projection_traits; using CDT_2_face_handle = typename CDT_2::Face_handle; using CDT_2_edge = typename CDT_2::Edge; + using CDT_2_vertex_handle = typename CDT_2::Vertex_handle; + using Face_is_outside_the_face = typename CDT_2_types::Face_is_outside_the_face; + static inline constexpr Face_is_outside_the_face OUTSIDE = Face_is_outside_the_face::OUTSIDE; + static inline constexpr Face_is_outside_the_face INSIDE = Face_is_outside_the_face::INSIDE; static_assert(CGAL::cdt_3_msvc_2019_or_older() || CGAL::is_nothrow_movable_v); + static Vertex_handle& vertex_3d(CDT_2_vertex_handle v) { + return v->info().vertex_handle_3d; + } + protected: struct Null_normal_error : Error_exception { Null_normal_error(std::string msg, std::string file, int line) @@ -1325,10 +1638,1220 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D using Constraint_hierarchy = typename Conforming_Dt::Constraint_hierarchy; using Subconstraint = typename Constraint_hierarchy::Subconstraint; + + using Conforming_Dt::with_offset; + using Conforming_Dt::with_point; + using Conforming_Dt::with_point_and_info; + public: using Constrained_polyline_id = typename Constraint_hierarchy::Constraint_id; protected: + auto display_vert(Vertex_handle v) const { + return with_point_and_info(v); + } + + auto display_facet(std::string_view prefix, Facet f) const { + return IO::oformat([&,prefix,f](auto& out) -> auto& { + const auto vertices = tr().vertices(f); + return out << cdt_3_format("\n{}{}" + "\n{}{}" + "\n{}{}" + "\n{}(fourth vertex of its cell: {})", + prefix, with_offset(vertices[0]), + prefix, with_offset(vertices[1]), + prefix, with_offset(vertices[2]), + prefix, with_offset(f.first->vertex((f.second)))); + }, IO_manip_tag{}); + } + + const auto& dbg() const { return this->debug(); } + + bool same_triple(const std::array f1_vertices, + std::array f2_vertices, + Orientation o = EQUAL) const + { + auto f2_it_equal_to_f_first_vertex = std::find(f2_vertices.begin(), f2_vertices.end(), f1_vertices[0]); + if(f2_it_equal_to_f_first_vertex == f2_vertices.end()) return false; + std::rotate(f2_vertices.begin(), f2_it_equal_to_f_first_vertex, f2_vertices.end()); + CGAL_assertion(f1_vertices[0] == f2_vertices[0]); + if(o == NEGATIVE) { + return (f1_vertices[1] == f2_vertices[2] && f1_vertices[2] == f2_vertices[1]); + } else { + return (f1_vertices[1] == f2_vertices[1] && f1_vertices[2] == f2_vertices[2]); + } + } + + bool same_triple(Facet f, Facet f2, Orientation o = EQUAL) const { + return same_triple(tr().vertices(f), tr().vertices(f2), o); + } + + bool same_triple(Facet f, CDT_2_face_handle fh, Orientation o = EQUAL) const { + auto f_vertices = tr().vertices(f); + std::array fh_vertices{vertex_3d(fh->vertex(0)), + vertex_3d(fh->vertex(1)), + vertex_3d(fh->vertex(2))}; + return same_triple(f_vertices, fh_vertices, o); + } + + bool opposite_triple(Facet f, Facet f2) const { + return same_triple(f, f2, NEGATIVE); + } + +public: + void set_facet_constrained(Facet f, CDT_3_signed_index polygon_constraint_id, + CDT_2_face_handle fh, [[maybe_unused]] Orientation o = EQUAL) + { + CGAL_assertion(fh == CDT_2_face_handle{} || same_triple(f, fh, o)); + CGAL_assertion(o == EQUAL); + + const auto [c, facet_index] = f; + c->ccdt_3_data().set_facet_constraint(facet_index, polygon_constraint_id, fh); + if(tr().dimension() > 2) { + const auto [n, n_index] = tr().mirror_facet(f); + n->ccdt_3_data().set_facet_constraint(n_index, polygon_constraint_id, fh); + } + if(fh != CDT_2_face_handle{}) { + fh->info().facet_3d = f; + fh->info().missing_subface = false; + } + } + + auto set_facet_as_not_constrained(Facet f) { + return set_facet_constrained(f, INVALID_FACE_INDEX, CDT_2_face_handle{}); + } + +protected: + void set_adjacency_between_3d_facets(Facet f1, Facet f2) const { + tr().tds().set_adjacency(f1.first, f1.second, f2.first, f2.second); + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << cdt_3_format("-> set adjacency between facets ({})=[{}]+{} and ({})=[{}]+{}\n", + with_offset(f1), with_offset(tr().vertices(f1)), + with_offset(f1.first->vertex(f1.second)), + with_offset(f2), with_offset(tr().vertices(f2)), + with_offset(f2.first->vertex(f2.second))); + CGAL_assertion(opposite_triple(f1, f2)); + } + CGAL_assertion(std::all_of(boost::counting_iterator(1), boost::counting_iterator(4), + [f1, f2](int i) { + return f1.first->neighbor((f1.second + i) % 4) != f2.first && + f2.first->neighbor((f2.second + i) % 4) != f1.first; + } + )); + } + + template + std::optional + find_in_incident_facets(Vertex_handle v, Pred pred) const + { + boost::container::flat_set, + boost::container::small_vector> found_cells; + boost::container::small_vector cells; + cells.push_back(v->cell()); + found_cells.insert(cells[0]); + int head=0; + int tail=1; + do { + Cell_handle c = cells[head]; + for (int i=0; i<4; ++i) { + if (c->vertex(i) == v) continue; + Cell_handle next = c->neighbor(i); + if(c < next && pred(Facet(c, i))) { + return Facet(c, i); + } + if (! found_cells.insert(next).second ) + continue; + cells.push_back(next); + ++tail; + } + ++head; + } while(head != tail); + return std::nullopt; + } + + template + CGAL::Surface_mesh + construct_star_component_mesh(std::size_t component_index, + const Components& components, + const Map& map_from_incident_constraint_face_id_to_new_triangles) const + { + CGAL::Surface_mesh component_mesh; + std::vector points; + std::vector> triangles; + std::size_t vertex_index = 0; + for(std::size_t facet_id = components.component_offsets[component_index - 1], + end = components.component_offsets[component_index]; + facet_id < end; ++facet_id) + { + const auto& [cell, facet_index, type] = components.facets[facet_id]; + if(!this->debug().move_Steiner_vertices_allow_negative_tets() && + type == Star_components_facets::Type_of_facet::INCIDENT_TO_V) + { + continue; + } + if(tr().is_infinite(Facet(cell, facet_index))) { + continue; + } + for(auto v: this->vertices(Facet{cell, facet_index})) { + points.push_back(tr().point(v)); + } // end for each vertex of the facet + // swap the orientation: our facets are oriented toward the center of the star component + // but we want them to be oriented outward + triangles.push_back({vertex_index, vertex_index + 2, vertex_index + 1}); + vertex_index += 3; + } // end for each facet in the component + if(!this->debug().move_Steiner_vertices_allow_negative_tets()) { + const auto& incident_face_ids = components.component_incident_constraint_face_ids[component_index - 1]; + for(auto face_id : incident_face_ids) { + if(face_id == INVALID_FACE_INDEX) { + continue; + } + const auto& new_triangles = map_from_incident_constraint_face_id_to_new_triangles.at(face_id); + for(const auto& triangle : new_triangles) { + points.push_back(triangle[0]); + points.push_back(triangle[1]); + points.push_back(triangle[2]); + triangles.push_back({vertex_index, vertex_index + 1, vertex_index + 2}); + vertex_index += 3; + } + } + } + CGAL::Polygon_mesh_processing::merge_duplicate_points_in_polygon_soup(points, triangles); + CGAL::Polygon_mesh_processing::orient_polygon_soup(points, triangles); + CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, triangles, component_mesh); + return component_mesh; + } + + std::optional compute_center_point_if_possible(const CGAL::Surface_mesh& component_mesh) const { + return CGAL::Polygon_mesh_processing::kernel_point(component_mesh); + } + + template + CGAL::Surface_mesh + construct_link_mesh_for_debug(const Components& components, + const Map& map_from_incident_constraint_face_id_to_new_triangles) const + { + using Mesh = CGAL::Surface_mesh; + using face_descriptor = typename Mesh::Face_index; + Mesh link_mesh; + auto [patch_id_map, _] = link_mesh.template add_property_map("f:patch_id", 0); + + for(std::size_t component_index = 1; component_index < components.component_offsets.size(); ++component_index) { + Mesh component_mesh = construct_star_component_mesh(component_index, components, + map_from_incident_constraint_face_id_to_new_triangles); + + std::vector new_faces; + auto out_it = boost::make_function_output_iterator( + [&new_faces](std::pair fd_pair) { new_faces.push_back(fd_pair.second); }); + CGAL::copy_face_graph(component_mesh, link_mesh, CGAL::parameters::face_to_face_output_iterator(out_it)); + for(auto fd : new_faces) { + put(patch_id_map, fd, static_cast(component_index)); + } + } + return link_mesh; + } + + template + void dump_link_mesh_of_vertex_to_ply(Vertex_handle v, + const Components& components, + const Map& map_from_incident_constraint_face_id_to_new_triangles) const { + const auto link_mesh = + construct_link_mesh_for_debug(components, map_from_incident_constraint_face_id_to_new_triangles); + + std::stringstream filename; + filename << "dump_link_mesh_vertex" << IO::oformat(v, With_offset_tag{}) << ".ply"; + std::ofstream out(filename.str()); + CGAL::IO::write_PLY(out, link_mesh); + out.close(); + } + + struct Star_components_facets { // output type of `collect_star_components_facets()` + enum class Type_of_facet { + INCIDENT_TO_V, + OPPOSITE_TO_V + }; + + struct Facet_of_star_component { + Cell_handle cell; + int index_of_opposite_vertex; + Type_of_facet type; + + bool operator<(const Facet_of_star_component& other) const { + if(cell != other.cell) + return cell < other.cell; + return index_of_opposite_vertex < other.index_of_opposite_vertex; + } + }; + + template + using small_vector = boost::container::small_vector; + + small_vector facets; + small_vector component_offsets = {0}; + small_vector nb_of_incident_facets_for_each_component; + small_vector> face_ids_between_consecutive_components; + small_vector> component_incident_constraint_face_ids; + + auto component_facets_range(std::size_t component_index) const { + return CGAL::make_range(facets.begin() + component_offsets[component_index - 1], + facets.begin() + component_offsets[component_index]); + } + }; + + Orientation constrained_facet_orientation(const Facet& f) const { + const auto& cdt_2 = this->face_cdt_2(face_constraint_index(f)); + const auto fh_2d = f.first->ccdt_3_data().face_2(cdt_2, f.second); + if(fh_2d->info().facet_3d == f) { + return CGAL::EQUAL; + } + CGAL_assertion(fh_2d->info().facet_3d == T_3::mirror_facet(f)); + return CGAL::NEGATIVE; + } + + Star_components_facets collect_star_components_facets(Vertex_handle v, Facet incident_constrained_facet) const { + const auto [first_cell, first_facet_index] = incident_constrained_facet; + const auto first_facet_face_id = face_constraint_index(incident_constrained_facet); + + const Orientation first_facet_orientation = constrained_facet_orientation(incident_constrained_facet); + + const auto vertex_Steiner_type = vertex_type(v); + CGAL_assertion(vertex_Steiner_type == CDT_3_vertex_type::STEINER_IN_FACE || + vertex_Steiner_type == CDT_3_vertex_type::STEINER_ON_EDGE); + + Star_components_facets out; // the returned object, yet to be filled + + auto register_cell_and_check_if_new = + [seen_cells = CGAL::unordered_flat_set{}](Cell_handle c) mutable + { + auto [_, inserted] = seen_cells.insert(c); + return inserted; + }; + + // `cells_in_other_components` is used to keep track of cells that belong to other components: + // when we finish processing a component, we take one of these cells (if not yet processed) to + // start processing the next component. + // -> Start with one of the facets incident to `v` that is constrained, and explore the star + // of `v` through unconstrained facets as long as possible; + boost::container::small_vector, 128> cells_in_other_components = { + { first_cell, first_facet_face_id, first_facet_orientation } + }; + + while(!cells_in_other_components.empty()) { + auto [other_cell, component_first_incident_face_id, component_first_facet_orientation] = + cells_in_other_components.back(); + cells_in_other_components.pop_back(); + auto other_cell_is_a_new_cell = register_cell_and_check_if_new(other_cell); + if(!other_cell_is_a_new_cell) continue; + + // start a new component from this cell + out.facets.emplace_back(other_cell, other_cell->index(v), Star_components_facets::Type_of_facet::OPPOSITE_TO_V); + + out.face_ids_between_consecutive_components.push_back( + {component_first_incident_face_id, component_first_facet_orientation}); + out.component_incident_constraint_face_ids.push_back( + {component_first_incident_face_id, INVALID_FACE_INDEX}); // placeholder for the next component + auto& current_component_incident_constraint_face_ids = out.component_incident_constraint_face_ids.back(); + cells_in_other_components.clear(); // clear it here, and fill it again with the cells opposite to a constrained + // facet during the exploration of the current component + + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << " (starting a new component through face with id " << component_first_incident_face_id << ")\n"; + } + // - `out.facets` is part of the result, and a queue as well, with `queue_head` as the index of + // the head of the queue: popping is done by incrementing `queue_head`, and pushing is done by + // adding new facets at the end of the vector. + for(auto queue_head = out.facets.size() - 1; queue_head != out.facets.size(); ++queue_head) { + const auto [c, facet_index, facet_type] = out.facets[queue_head]; + if(facet_type == Star_components_facets::Type_of_facet::INCIDENT_TO_V) continue; + + for(int i = 0; i < 4; ++i) + { + // follow only cells in the star of `v` + if(c->vertex(i) == v) continue; + const Cell_handle next_cell = c->neighbor(i); + + const auto facet_c_to_next_is_constrained = is_facet_constrained(Facet(c, i)); + if(facet_c_to_next_is_constrained) { + out.facets.emplace_back(c, i, Star_components_facets::Type_of_facet::INCIDENT_TO_V); + const auto constrained_facet_face_id = face_constraint_index(c, i); + if(INVALID_FACE_INDEX == current_component_incident_constraint_face_ids[1] && + constrained_facet_face_id != current_component_incident_constraint_face_ids[0]) + { + current_component_incident_constraint_face_ids[1] = constrained_facet_face_id; + } + CGAL_assertion(constrained_facet_face_id == current_component_incident_constraint_face_ids[0] || + constrained_facet_face_id == current_component_incident_constraint_face_ids[1]); + + // unless the Steiner vertex is in a face, the component will always be incident to + // two different constraint faces, and therefore we can prefer to push only cells that + // are on the other side a constraint with a different face id. + if(constrained_facet_face_id != component_first_incident_face_id || + vertex_Steiner_type == CDT_3_vertex_type::STEINER_IN_FACE ) + { + const auto orientation = constrained_facet_orientation(Facet(c, i)); + cells_in_other_components.push_back({next_cell, constrained_facet_face_id, orientation}); + } + } else { // Facet(c, i), between `c` and `next_cell` is not constrained + auto next_is_a_new_cell = register_cell_and_check_if_new(next_cell); + if(next_is_a_new_cell) { + out.facets.emplace_back(next_cell, next_cell->index(v), Star_components_facets::Type_of_facet::OPPOSITE_TO_V); + } + } + } // end for each facet of `c` + } // end while there is still a facet in the current component to process + + // end of the current component reached, start a new one if there is any cell in + // another component that we haven't processed yet + + // reorder the component to that incident facets are first + const auto component_offset = out.component_offsets.back(); + const auto begin_it = out.facets.begin() + component_offset; + const auto incident_facets_end = std::partition(begin_it, out.facets.end(), [](const auto& facet_info) { + return facet_info.type == Star_components_facets::Type_of_facet::INCIDENT_TO_V; + }); + + out.nb_of_incident_facets_for_each_component.push_back(std::distance(begin_it, incident_facets_end)); + out.component_offsets.push_back(out.facets.size()); + + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << "- Star component #" << out.nb_of_incident_facets_for_each_component.size() + << " has:\n - " + << (out.facets.size() - component_offset) << " total facets,\n - " + << out.nb_of_incident_facets_for_each_component.back() << " facets incident to the vertex.\n" + << " It is incident to constraint faces with ids: {" + << IO::oformat(current_component_incident_constraint_face_ids) << "}\n"; + } + } // end while there is still a component to process + + CGAL_assertion_code(const auto nb_of_components = out.component_incident_constraint_face_ids.size()); + + CGAL_assertion(false == out.facets.empty()); + CGAL_assertion(nb_of_components >= 1); + CGAL_assertion(out.component_offsets.size() == nb_of_components + 1); + + CGAL_assertion(out.component_offsets.front() == 0); + CGAL_assertion(out.component_offsets.back() == out.facets.size()); + CGAL_assertion(out.face_ids_between_consecutive_components.size() == nb_of_components); + + if(vertex_Steiner_type == CDT_3_vertex_type::STEINER_IN_FACE) { + CGAL_assertion(nb_of_components == 2); + CGAL_assertion(std::all_of(out.component_incident_constraint_face_ids.begin(), out.component_incident_constraint_face_ids.end(), + [first_facet_face_id](const std::array& face_ids) + { + return face_ids[0] == first_facet_face_id && face_ids[1] == INVALID_FACE_INDEX; + })); + CGAL_assertion(std::all_of(out.face_ids_between_consecutive_components.begin(), out.face_ids_between_consecutive_components.end(), + [first_facet_face_id](const std::pair& face_id_orientation) + { + return face_id_orientation.first == first_facet_face_id; + })); + } else { + CGAL_assertion(vertex_Steiner_type == CDT_3_vertex_type::STEINER_ON_EDGE); + CGAL_assertion(std::adjacent_find(out.face_ids_between_consecutive_components.begin(), + out.face_ids_between_consecutive_components.end()) == + out.face_ids_between_consecutive_components.end()); + CGAL_assertion(nb_of_components == 1 || + std::all_of(out.component_incident_constraint_face_ids.begin(), out.component_incident_constraint_face_ids.end(), + [](const std::array& face_ids) + { + return face_ids[1] != INVALID_FACE_INDEX; + })); + } + + return out; + } // end collect_star_components_facets(..) + + enum Run_mode { NORMAL_RUN, DRY_RUN }; + + template + std::pair + fill_cdt_2_hole_with_delaunay(CDT_2& cdt_2, + Face_index face_index, + CDT_3_vertex_type vertex_type, + std::list& hole_boundary, + const Map_from_hole_edge_to_facet_3d& map_from_hole_edge_to_facet_3d, + Face_is_outside_the_face is_outside_the_face, + New_CDT_2_face_handles_output_iterator new_face_handles_out, + Run_mode run_mode = NORMAL_RUN) + { + const bool debug = dbg().move_Steiner_vertices_level() > 1 && run_mode == NORMAL_RUN; + const bool is_a_half_hole = vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; + if(debug) { + std::cerr << cdt_3_format("Filling {}hole in CDT_2 with {} edges, is_outside_the_face = {}\n", + is_a_half_hole ? "half " : "", hole_boundary.size(), + enum_cast(is_outside_the_face)); + } + auto indent_guard = IO::make_indenting_guards(debug ? "# " : "", std::cerr); + const auto at_scope_exit_guard = + make_scope_exit([&cdt_2, hole_edges = std::vector(hole_boundary.begin(), hole_boundary.end())]() { + if(std::uncaught_exceptions() > 0) { + std::cerr << "WARNING: an exception is being thrown while filling the hole, the triangulation may be left " + "in an invalid state.\n"; + return; + } + // restore the constrained/unconstrained status of edges, from a copy of `hole_boundary`. + std::for_each(hole_edges.begin(), hole_edges.end(), [&cdt_2](CDT_2_edge e) { + auto [f, i] = cdt_2.mirror_edge(e); + f->set_constraint(i, cdt_2.is_constrained(e)); + }); + }); + + if(hole_boundary.size() <= 2) { // degenerated hole + CGAL_assertion(hole_boundary.size() == 2); // size == 1 is not possible + auto [f0, i0] = hole_boundary.front(); + auto [f1, i1] = hole_boundary.back(); + cdt_2.tds().set_adjacency(f1, i1, f0, i0); + return {new_face_handles_out, {}}; + } + + if(is_outside_the_face == OUTSIDE) { + CGAL::fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator([&](CDT_2_face_handle fh) { + auto& info = fh->info(); + info.is_outside_the_face = OUTSIDE; + info.missing_subface = false; + info.is_in_region = 0; + info.facet_3d = {}; + })); + return {new_face_handles_out, {}}; + } + + const CDT_2_face_handle half_hole_neighbor_face = is_a_half_hole ? hole_boundary.back().first : CDT_2_face_handle{}; + Facet half_hole_front_facet{Cell_handle{}, 0}; + + std::for_each(hole_boundary.begin(), hole_boundary.end(), + [](const CDT_2_edge& e) { e.first->info().is_in_region = 0; }); + + const auto [outside_2d_face, outside_2d_index] = hole_boundary.front(); + const auto first_2d_face = outside_2d_face->neighbor(outside_2d_index); + const auto v0 = vertex_3d(outside_2d_face->vertex(cdt_2.cw(outside_2d_index))); + const auto v1 = vertex_3d(outside_2d_face->vertex(cdt_2.ccw(outside_2d_index))); + const auto [front_cell, front_index] = first_2d_face->info().facet_3d; + CGAL_assertion(front_cell != Cell_handle{}); + const auto [back_cell, back_index] = tr().mirror_facet({front_cell, front_index}); + + const Vertex_handle v_front = front_cell->vertex(6 - front_index - front_cell->index(v0) - front_cell->index(v1)); + const Vertex_handle v_back = back_cell->vertex(6 - back_index - back_cell->index(v0) - back_cell->index(v1)); + if(debug) { + std::cerr << cdt_3_format("- first_2d_face has vertices ( {} ,\n" + " {} ,\n" + " {} )\n", + display_vert(vertex_3d(first_2d_face->vertex(0))), + display_vert(vertex_3d(first_2d_face->vertex(1))), + display_vert(vertex_3d(first_2d_face->vertex(2)))); + std::cerr << cdt_3_format("- front_cell is ({}) with vertices:{}\n", + IO::oformat(front_cell), + display_facet(" - {}\n", Facet{front_cell, front_index})) + << "\n- back vertex is "<< display_vert(v_back) << '\n'; + } + + auto action_for_each_new_2d_face = [&](CDT_2_face_handle fh_2d) { + auto& info = fh_2d->info(); + info.is_outside_the_face = INSIDE; + info.missing_subface = false; + info.is_in_region = 1; + + const auto v1 = vertex_3d(fh_2d->vertex(0)); + const auto v2 = vertex_3d(fh_2d->vertex(1)); + const auto v3 = vertex_3d(fh_2d->vertex(2)); + + if(debug) { + std::cerr << cdt_3_format("New CDT_2 face with 3D vertices ( {} ,\n" + " {} ,\n" + " {} )\n", + display_vert(v1), + display_vert(v2), + display_vert(v3)); + } + auto indent_guard = IO::make_indenting_guards(debug ? " " : "", std::cerr); + + auto new_cell = [&](Vertex_handle v_a, Vertex_handle v_b, Vertex_handle v_c, Vertex_handle v_d) { + auto cell = tr().tds().create_cell(v_a, v_b, v_c, v_d); + CGAL_assertion(!debug || this->debug().move_Steiner_vertices_allow_negative_tets() || + tr().orientation(v_a->point(), v_b->point(), v_c->point(), v_d->point()) > 0); + return cell; + }; + + const auto new_front_cell = new_cell(v1, v2, v3, v_front); + const auto new_back_cell = new_cell(v1, v3, v2, v_back); + set_adjacency_between_3d_facets({new_front_cell, 3}, {new_back_cell, 3}); + set_facet_constrained({new_front_cell, 3}, face_index, fh_2d); + + for(int i = 0; i < 3; ++i) { + const CDT_2_face_handle neighbor_2d = fh_2d->neighbor(i); + if(debug) { + std::cerr << "-- explore neighbor(" << i << ") --\n"; + } + + if(neighbor_2d == CDT_2_face_handle{}) { + if(debug) { + std::cerr << "(neighbor not yet connected, continue)\n"; + } + // this edge of `fh_2d` is not yet connected + continue; + } else if(neighbor_2d == half_hole_neighbor_face) { + // this edge of `fh_2d` is dandling to be connected to the other part of the half hole + CGAL_assertion(half_hole_front_facet.first == Cell_handle{}); // only one edge of the hole can be a half hole + half_hole_front_facet = {new_front_cell, i}; + if(debug) { + std::cerr << cdt_3_format("- the 3D facet (new_front_cell={}, i={}) is ON THE BORDER of the half hole:{}\n" + , IO::oformat(new_front_cell), i + , display_facet(" ", Facet{new_front_cell, i})); + } + continue; + } + // else + CGAL_assertion(new_front_cell->neighbor(i) == Cell_handle{}); // facet (new_front_cell, i) is no yet connected + const bool neighbor_2d_is_in_the_star = neighbor_2d->info().is_in_region; + const auto mirror_index = cdt_2.cw(neighbor_2d->index(fh_2d->vertex(cdt_2.cw(i)))); + const auto [neighbor_front_cell_3d, neighbor_front_index_3d] = + neighbor_2d_is_in_the_star ? neighbor_2d->info().facet_3d + : map_from_hole_edge_to_facet_3d.at({neighbor_2d, mirror_index}); + CGAL_assertion(neighbor_front_cell_3d != Cell_handle{}); + + const auto neighbor_back_cell_3d = neighbor_front_cell_3d->neighbor(neighbor_front_index_3d); + + const auto fh_index_in_neighbor_2d = + neighbor_2d_is_in_the_star ? neighbor_2d->index(fh_2d) : neighbor_front_index_3d; + + if(debug) { + std::cerr << cdt_3_format("- the 2D face fh_2d->neighbor(i={}) IS{} in region. Its 3D vertices are:\n" + " {}\n" + " {}\n" + " {}\n" + , i, (neighbor_2d_is_in_the_star ? "" : " NOT") + , display_vert(vertex_3d(neighbor_2d->vertex(0))) + , display_vert(vertex_3d(neighbor_2d->vertex(1))) + , display_vert(vertex_3d(neighbor_2d->vertex(2)))); + std::cerr << cdt_3_format(" * the 3D facet (new_front_cell={}, i={}) is{}\n" + , IO::oformat(new_front_cell), i + , display_facet(" ", Facet{new_front_cell, i})); + std::cerr << cdt_3_format(" * the 3D facet (neighbor_front_cell_3d={}, fh_index_in_neighbor_2d={}) is{}\n" + , IO::oformat(neighbor_front_cell_3d), fh_index_in_neighbor_2d + , display_facet(" ", Facet{neighbor_front_cell_3d, fh_index_in_neighbor_2d})); + } // end of debug output + + set_adjacency_between_3d_facets({new_front_cell, i}, {neighbor_front_cell_3d, fh_index_in_neighbor_2d}); + + const int back_cell_i = (3 - i) % 3; // the index of the neighbor in `new_back_cell` is deduced from the + // fact that `new_back_cell` was created with vertices in reversed + // order compared to `new_front_cell` + const int back_cell_fh_index_in_neighbor_2d = neighbor_2d_is_in_the_star + ? (3 - fh_index_in_neighbor_2d) % 3 + : neighbor_back_cell_3d->index(neighbor_front_cell_3d); + + if(debug) { + std::cerr << cdt_3_format(" * the 3D facet (new_back_cell={}, back_cell_i={}) is{}\n" + , IO::oformat(new_back_cell), back_cell_i + , display_facet(" ", Facet{new_back_cell, back_cell_i})); + std::cerr << cdt_3_format(" * the 3D facet (neighbor_back_cell_3d={}, back_cell_fh_index_in_neighbor_2d={}) is{}\n" + , IO::oformat(neighbor_back_cell_3d), back_cell_fh_index_in_neighbor_2d + , display_facet(" ", Facet{neighbor_back_cell_3d, back_cell_fh_index_in_neighbor_2d})); + } // end of debug output + + set_adjacency_between_3d_facets({new_back_cell, back_cell_i}, + {neighbor_back_cell_3d, back_cell_fh_index_in_neighbor_2d}); + } + + + *new_face_handles_out++ = fh_2d; + }; // end of `action_for_each_new_2d_face` + switch(run_mode) { + case NORMAL_RUN: + CGAL::fill_hole_delaunay(cdt_2, hole_boundary, boost::make_function_output_iterator(action_for_each_new_2d_face)); + break; + case DRY_RUN: + CGAL::fill_hole_delaunay(cdt_2, hole_boundary, new_face_handles_out); + break; + default: + CGAL_unreachable(); + } + return {new_face_handles_out, half_hole_front_facet}; + } + + bool maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(Vertex_handle v, Face_index face_id) { + auto& mutable_cdt_2 = non_const_face_cdt_2(face_id); + typename CDT_2::Locate_type lt; + int i; + const auto fh_2d = mutable_cdt_2.locate(v->point(), lt, i); + CGAL_assertion(lt == CDT_2::VERTEX); + + const auto vh_2d = fh_2d->vertex(i); + CGAL_assertion(vertex_3d(vh_2d) == v); + auto face_circ = mutable_cdt_2.incident_faces(vh_2d); + const auto done = face_circ; + do { + CDT_2_face_handle current = face_circ; + CDT_2_face_handle next = ++face_circ; + CDT_2_edge current_edge = {current, CDT_2::ccw(current->index(vh_2d))}; + CGAL_assertion(current_edge.first->neighbor(current_edge.second) == next); + if(current->info().is_outside_the_face == OUTSIDE || next->info().is_outside_the_face == OUTSIDE) { + continue; + } + auto current_facet_3d = current->info().facet_3d; + auto current_back_facet_3d = tr().mirror_facet(current_facet_3d); + auto next_facet_3d = next->info().facet_3d; + auto next_back_facet_3d = tr().mirror_facet(next_facet_3d); + + std::optional>> + common_cell_incident_to_current_and_next{}; + + if(current_facet_3d.first == next_facet_3d.first) { + common_cell_incident_to_current_and_next.emplace( + current_facet_3d.first, NEGATIVE, + std::array{next_facet_3d.second, current_facet_3d.second}); + } + if(current_back_facet_3d.first == next_back_facet_3d.first) { + common_cell_incident_to_current_and_next.emplace( + current_back_facet_3d.first, EQUAL, + std::array{current_back_facet_3d.second, next_back_facet_3d.second}); + } + if(common_cell_incident_to_current_and_next) { + auto [common_cell, orientation, common_cell_opposite_vertices] = *common_cell_incident_to_current_and_next; + auto [index_va, index_vb] = common_cell_opposite_vertices; + auto index_vc = tr().next_around_edge(index_va, index_vb); + auto index_vd = tr().next_around_edge(index_vb, index_va); + CGAL_assertion(common_cell->vertex(index_vd) == v); + if(dbg().move_Steiner_vertices_level() > 0) { + const auto v = tr().vertices(common_cell); + std::cerr << cdt_3_format( + "The edge (from \n" + " {} to\n" + " {})\n" + "in the CDT_2 of face F#{} must be flipped, because its incident facets \n" + "are incident to the same cell {}, with the two other vertices\n" + " {},\n" + " {})\n", + display_vert(v[index_vc]), display_vert(v[index_vd]), + face_id, + IO::oformat(common_cell), + display_vert(v[index_va]), display_vert(v[index_vb])); + } + mutable_cdt_2.flip(current_edge.first, current_edge.second); + set_facet_as_not_constrained(current_facet_3d); + set_facet_as_not_constrained(next_facet_3d); + Facet facet_c{common_cell, index_vc}; + Facet facet_d{common_cell, index_vd}; + if(orientation == NEGATIVE) { + facet_c = tr().mirror_facet(facet_c); + facet_d = tr().mirror_facet(facet_d); + } + set_facet_constrained(facet_c, face_id, next); + set_facet_constrained(facet_d, face_id, current); + return true; + } + } while(face_circ != done); + return false; + } + + template + std::tuple + remove_Steiner_vertex_from_cdt_2(Vertex_handle v, + CDT_3_vertex_type vertex_type, + Face_index face_id, + New_CDT_2_face_handles_output_iterator new_face_handles_out, + Run_mode run_mode = NORMAL_RUN) + { + const bool debug = dbg().move_Steiner_vertices_level() > 1 && run_mode == NORMAL_RUN; + std::tuple result{new_face_handles_out, {}, {}}; + auto& [new_face_handles_out_it, half_hole_front_facet, second_half_hole_front_facet] = result; + + using CDT_2_base = typename CDT_2::Triangulation; + auto& original_mutable_cdt_2 = static_cast(non_const_face_cdt_2(face_id)); + std::unique_ptr cdt_2_copy_ptr{}; + if(run_mode == DRY_RUN) { + cdt_2_copy_ptr.reset(new CDT_2_base(original_mutable_cdt_2)); + } + auto& mutable_cdt_2 = (run_mode == DRY_RUN) ? *cdt_2_copy_ptr : original_mutable_cdt_2; + const auto & cdt_2 = mutable_cdt_2; + + typename CDT_2::Locate_type lt; + int i; + const auto fh_2d = cdt_2.locate(v->point(), lt, i); + CGAL_assertion(lt == CDT_2::VERTEX); + + const auto vh_2d = fh_2d->vertex(i); + CGAL_assertion(vertex_3d(vh_2d) == v); + + if(debug) { + using std::begin; using std::end; + std::for_each(begin(cdt_2.all_face_handles()), end(cdt_2.all_face_handles()), + [](CDT_2_face_handle fh) { + fh->info().is_in_region = 0; + }); + auto face_circ = cdt_2.incident_faces(vh_2d); + const auto done = face_circ; + auto nb_of_incident_faces = 0u; + do { + face_circ->info().is_in_region = 1; + ++nb_of_incident_faces; + } while(++face_circ != done); + dump_face(face_id); + std::cerr << " -> remove Steiner vertex " << display_vert(v) << " from CDT_2 of face F#" << face_id + << " (nb of incident faces: " << nb_of_incident_faces << ")\n"; + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid BEFORE removing Steiner vertex " << display_vert(v) + << " from face F#" << face_id << '\n'; + CGAL_error_msg("Aborting due to invalid CDT_2"); + } + } + auto indent_guard = IO::make_indenting_guards(debug ? " |" : "", std::cerr); + + auto map_from_hole_edges_to_incident_3d_facets = [&](const auto& hole_boundary_begin, const auto& hole_boundary_end, + Face_is_outside_the_face is_outside_the_face) { + CGAL::unordered_flat_map> map; + if(is_outside_the_face == OUTSIDE) + return map; + + for(auto it = hole_boundary_begin; it != hole_boundary_end; ++it) { + auto [fh_2d, index] = *it; + CGAL_assertion(!debug || cdt_2.tds().is_face(fh_2d)); + const auto neighbor_2d_inside_star = fh_2d->neighbor(index); + CGAL_assertion(!debug || cdt_2.tds().is_face(neighbor_2d_inside_star)); + const auto facet_3d = neighbor_2d_inside_star->info().facet_3d; + CGAL_assertion(facet_3d.first != Cell_handle{}); + [[maybe_unused]] auto [_, inserted] = map.insert({{fh_2d, index}, facet_3d}); + CGAL_assertion(inserted); + if(debug) { + auto back_facet_3d = tr().mirror_facet(facet_3d); + std::cerr << cdt_3_format("for the hole edge ({}, {}), the 3D facet is ({}){}\n" + " the opposite facet is ({})\n" + " opposite vertex: {}\n", + display_vert(vertex_3d(fh_2d->vertex(cdt_2.cw(index)))), + display_vert(vertex_3d(fh_2d->vertex(cdt_2.ccw(index)))), + IO::oformat(facet_3d), + display_facet(" ", facet_3d), + IO::oformat(back_facet_3d), + display_vert(back_facet_3d.first->vertex(back_facet_3d.second))); + } + } + return map; + }; + + auto bore_hole_and_fill_with_delaunay = [&](CDT_3_vertex_type vertex_type, auto& hole_boundary, + const auto& faces_to_delete, + Face_is_outside_the_face is_outside_the_face) { + if(debug) { + std::cerr << "The star of " << display_vert(v) << " in the CDT_2 of face F#" << face_id << " has a component " + << (is_outside_the_face == OUTSIDE ? "OUTSIDE the face " : "") << "with " + << hole_boundary.size() << " edges, and " << faces_to_delete.size() << " faces:\n"; + } + auto indent_guard = IO::make_indenting_guards(debug ? " >" : "", std::cerr); + if(debug) { + for(const auto& fh_2d : faces_to_delete) { + std::cerr << cdt_3_format("- CDT_2 face with 3D vertices ( {} ,\n" + " {} ,\n" + " {} )\n", + display_vert(vertex_3d(fh_2d->vertex(0))), + display_vert(vertex_3d(fh_2d->vertex(1))), + display_vert(vertex_3d(fh_2d->vertex(2)))); + } + } // end of debug output + + // if the vertex is a Steiner point on an edge, the hole is a half-hole, and the last boundary + // edge is not part of the original hole boundary. + const auto hole_boundary_end = + vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE ? std::prev(hole_boundary.end()) : hole_boundary.end(); + + const auto hole_boundary_map = + map_from_hole_edges_to_incident_3d_facets(hole_boundary.begin(), hole_boundary_end, is_outside_the_face); + std::for_each(faces_to_delete.begin(), faces_to_delete.end(), + [&mutable_cdt_2](CDT_2_face_handle f) { mutable_cdt_2.delete_face(f); }); + return fill_cdt_2_hole_with_delaunay(mutable_cdt_2, face_id, vertex_type, hole_boundary, hole_boundary_map, + is_outside_the_face, new_face_handles_out, run_mode); + }; + + auto set_constrained = [&](CDT_2_edge e, bool is_constrained) { + auto [f, i] = e; + f->set_constraint(i, is_constrained); + auto [n, ni] = cdt_2.mirror_edge(e); + n->set_constraint(ni, is_constrained); + }; + + if(vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE) { + + auto first_constrained_edge_circulator_before_face = [&](CDT_2_face_handle incident_fh) { + auto edge_circulator = cdt_2.incident_edges(vh_2d, incident_fh); + --edge_circulator; // take the edge before `incident_fh` + CGAL_assertion_code(const auto done = edge_circulator); + while(!cdt_2.is_constrained(*edge_circulator)) { + --edge_circulator; + CGAL_assertion(edge_circulator != done); + } + return edge_circulator; + }; + + auto advance_circulator_until_next_constrained_edge = [&](auto& edge_circulator, auto func) { + CGAL_assertion_code(const auto done = edge_circulator); + do { + ++edge_circulator; + func(*edge_circulator); + CGAL_assertion(edge_circulator != done); + } while(!cdt_2.is_constrained(*edge_circulator)); + return edge_circulator; + }; + + auto half_hole = [&](CDT_2_face_handle incident_fh) { + struct Result + { + std::list hole_side; + boost::container::small_vector to_delete; + CDT_2_edge last_incident_edge; + } result; + auto edge_circulator = first_constrained_edge_circulator_before_face(incident_fh); + result.last_incident_edge = + *advance_circulator_until_next_constrained_edge(edge_circulator, [&](CDT_2_edge current_edge) { + const auto [f, i] = current_edge; + result.to_delete.push_back(f); + const auto edge_opposite_to_v = cdt_2.mirror_edge(CDT_2_edge{f, cdt_2.cw(i)}); + result.hole_side.push_back(edge_opposite_to_v); + const auto [n, ni] = edge_opposite_to_v; + n->vertex(cdt_2.cw(ni))->set_face(n); + n->vertex(cdt_2.ccw(ni))->set_face(n); + }); + return result; + }; + + auto [hole_side_1, hole_side_1_faces_to_delete, side_1_last_incident_edge] = half_hole(vh_2d->face()); + const auto [side_1_last_face, side_1_last_edge_index] = side_1_last_incident_edge; + const auto side_1_hole_is_outside_the_face = side_1_last_face->info().is_outside_the_face; + + const auto side_2_first_face = side_1_last_face->neighbor(side_1_last_edge_index); + + auto [hole_side_2, hole_side_2_faces_to_delete, side_2_last_incident_edge] = half_hole(side_2_first_face); + const auto [side_2_last_face, side_2_last_edge_index] = side_2_last_incident_edge; + const auto side_2_hole_is_outside_the_face = side_2_last_face->info().is_outside_the_face; + + CGAL_assertion(side_1_last_incident_edge.first != side_2_last_incident_edge.first); + + // unmark the two constrained edges + set_constrained(side_1_last_incident_edge, false); + set_constrained(side_2_last_incident_edge, false); + + // start boring and sewing the two half-holes: + // - we start from side 1, + // - `side_2_last_face` will temporarily be modified to close `hole_side_1` + const auto v1 = side_1_last_face->vertex(cdt_2.ccw(side_1_last_edge_index)); + side_2_last_face->set_vertex(cdt_2.cw(side_2_last_edge_index), v1); + hole_side_1.push_back(side_2_last_incident_edge); + + // - bore the hole on side 1, and fill it with CDT triangles + auto [out_it, facet_side_1] = bore_hole_and_fill_with_delaunay( + vertex_type, hole_side_1, hole_side_1_faces_to_delete, side_1_hole_is_outside_the_face); + new_face_handles_out = out_it; + CGAL_assertion(run_mode == DRY_RUN || + side_1_hole_is_outside_the_face == OUTSIDE || facet_side_1.first != Cell_handle{}); + + // - now the edge opposite to `(side_2_last_face, side_2_last_edge_index)` is on the border of the hole + // `hole_side_2`, so we can start sewing from there + const auto side_1_border_edge = cdt_2.mirror_edge(CDT_2_edge{side_2_last_face, side_2_last_edge_index}); + hole_side_2.push_back(side_1_border_edge); + auto [out_it_2, facet_side_2] = bore_hole_and_fill_with_delaunay( + vertex_type, hole_side_2, hole_side_2_faces_to_delete, side_2_hole_is_outside_the_face); + new_face_handles_out = out_it_2; + CGAL_assertion(run_mode == DRY_RUN || + side_2_hole_is_outside_the_face == OUTSIDE || facet_side_2.first != Cell_handle{}); + + half_hole_front_facet = facet_side_1; + second_half_hole_front_facet = facet_side_2; + + // - then re-mark the edge between the two holes as a constrained edge + set_constrained(side_1_border_edge, true); + } else { // if vertex_type != CDT_3_vertex_type::STEINER_ON_EDGE + CGAL_assertion(vertex_type == CDT_3_vertex_type::STEINER_IN_FACE); + + auto [hole_boundary, faces_to_delete] = std::invoke([&]() { + struct Result + { + std::list hole_boundary; + boost::container::small_vector to_delete; + } result; + auto face_circulator = cdt_2.incident_faces(vh_2d); + const auto done = face_circulator; + do { + const CDT_2_face_handle fh_2d = face_circulator; + auto v_index = fh_2d->index(vh_2d); + CDT_2_edge opposite_edge = cdt_2.mirror_edge(CDT_2_edge{fh_2d, v_index}); + auto adjacent_vertex = fh_2d->vertex(cdt_2.cw(v_index)); + adjacent_vertex->set_face(opposite_edge.first); + result.hole_boundary.push_back(opposite_edge); + result.to_delete.push_back(fh_2d); + } while(++face_circulator != done); + return result; + }); + + auto [out_it, facet] = + bore_hole_and_fill_with_delaunay(vertex_type, hole_boundary, faces_to_delete, INSIDE); + new_face_handles_out = out_it; + CGAL_assertion(facet.first == Cell_handle{}); + } + mutable_cdt_2.delete_vertex(vh_2d); + if(debug) { + dump_face(face_id, std::string("dump_face_") + std::to_string(face_id) + "_after_removing_Steiner_vertex_" + + std::to_string(vh_2d->time_stamp()) + ".off"); + + if(!cdt_2.CT_2::is_valid(true, 3)) { + std::cerr << "ERROR: CDT_2 is not valid after removing Steiner vertex " << with_point_and_info(v) + << " from face F#" << face_id << '\n'; + CGAL_error_msg("Aborting due to invalid CDT_2"); + } + } + new_face_handles_out_it = new_face_handles_out; + return result; + } // end of `remove_Steiner_vertex_from_cdt_2` + + bool move_one_Steiner_vertex_to_the_volume(Vertex_handle v) { + if(dbg().move_Steiner_vertices()) { + std::cerr << "Moving Steiner vertex " << display_vert(v) << " to the volume\n"; + } + auto indent_guards = CGAL::IO::make_indenting_guards(dbg().move_Steiner_vertices_level() > 1 ? "| " : "", std::cerr); + + const auto v_type = vertex_type(v); + if(v_type == CDT_3_vertex_type::STEINER_IN_FACE) { + const auto incident_face_id = v->ccdt_3_data().face_index(); + while(maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(v, incident_face_id)) {} + } else { + CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); + const auto incident_polyline_constraint_id = v->ccdt_3_data().constrained_polyline_id(*this); + for(const auto& [_, incident_face_id] : + CGAL::make_range(this->incident_faces_to_polyline.equal_range(incident_polyline_constraint_id))) + { + while(maybe_flip_an_incident_edge_in_cdt_2_to_avoid_degeneracy(v, incident_face_id)) {} + } + } + + const auto incident_constrained_facet_opt = + find_in_incident_facets(v, [this](Facet f) { return is_facet_constrained(f); }); + CGAL_assertion(incident_constrained_facet_opt.has_value()); + + const auto incident_constrained_facet = *incident_constrained_facet_opt; + + if(dbg().move_Steiner_vertices_level() > 1) { + const auto nb_of_incident_cells = std::invoke([&]() { + std::size_t nb_of_incident_cells{0}; + tr().incident_cells(v, CGAL::Counting_output_iterator(&nb_of_incident_cells)); + return nb_of_incident_cells; + }); + + std::cerr << "- Number of incident cells: " << nb_of_incident_cells << '\n'; + } + + const auto star_components = collect_star_components_facets(v, incident_constrained_facet); + const auto nb_of_star_components = star_components.component_incident_constraint_face_ids.size(); + + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << "- Nb of components of the star: " << nb_of_star_components << '\n'; + } + if(nb_of_star_components == 1) { + if(dbg().move_Steiner_vertices_level() > 1) { + auto _ = IO::make_color_guards(std::cerr); + std::cerr << "- Only one component in the star, cannot move the vertex to the volume, aborting\n"; + } + return false; + } + std::map> map_from_incident_constraint_face_id_to_new_triangles; + + auto dry_run_output_iterator = [&](Face_index face_id) { + auto* triangles_for_this_face_ptr = &map_from_incident_constraint_face_id_to_new_triangles[face_id]; + std::function func = [&,triangles_for_this_face_ptr](CDT_2_face_handle fh_2d) { + const auto v1 = vertex_3d(fh_2d->vertex(0)); + const auto v2 = vertex_3d(fh_2d->vertex(1)); + const auto v3 = vertex_3d(fh_2d->vertex(2)); + triangles_for_this_face_ptr->emplace_back(v1->point(), v2->point(), v3->point()); + }; + return boost::make_function_output_iterator(func); + }; + + if(v_type == CDT_3_vertex_type::STEINER_IN_FACE) { + CGAL_assertion(star_components.component_incident_constraint_face_ids.size() == 2); + [[maybe_unused]] const auto& [face_id, _] = star_components.face_ids_between_consecutive_components.front(); + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, dry_run_output_iterator(face_id), DRY_RUN); + } else { + CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); + for(std::size_t component_index = 0, end = star_components.face_ids_between_consecutive_components.size(); + component_index < end; ++component_index) + { + const auto& [face_id, _] = star_components.face_ids_between_consecutive_components[component_index]; + + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, dry_run_output_iterator(face_id), DRY_RUN); + } + } + + const auto components_central_points = std::invoke([&]() { + std::optional> central_points{std::in_place}; + central_points->reserve(nb_of_star_components); + for(std::size_t component_index = 1; component_index <= nb_of_star_components; ++component_index) { + auto component_mesh = construct_star_component_mesh(component_index, star_components, + map_from_incident_constraint_face_id_to_new_triangles); + if(dbg().move_Steiner_vertices_level() > 1) { + std::ostringstream ss; + ss << "dump_vertex_" << with_offset(v) << "_component_" << component_index << ".off"; + const std::string filename = ss.str(); + std::ofstream out(filename); + out.precision(17); + out << component_mesh; + } + auto opt_kernel_center_point = compute_center_point_if_possible(component_mesh); + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << " - incident constraint faces ids of component #" << component_index << ": {" + << IO::oformat(star_components.component_incident_constraint_face_ids[component_index - 1]) + << "}\n"; + std::cerr << " kernel center point of component #" << component_index << ": "; + } + if(opt_kernel_center_point.has_value()) { + auto kernel_center_point = *opt_kernel_center_point; + central_points->push_back(kernel_center_point); + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << kernel_center_point + << " (distance to vertex: " << CGAL::sqrt(CGAL::squared_distance(kernel_center_point, v->point())) + << ")" + << "\n"; + } + } else { + if(dbg().move_Steiner_vertices_level() > 1) { + auto _ = IO::make_color_guards(std::cerr); + std::cerr << "none (the component is either degenerate or open) ABORT\n"; + } + if(!this->debug().move_Steiner_vertices_allow_negative_tets()) { + central_points.reset(); + return central_points; + } else { + central_points->push_back(tr().point(v)); // push the current position of the vertex as a fallback + } + } + } + return central_points; + }); // end compute central points for each component + + if(dbg().move_Steiner_vertices_level() > 1) { + dump_link_mesh_of_vertex_to_ply(v, star_components, map_from_incident_constraint_face_id_to_new_triangles); + } + + if(!components_central_points.has_value()) { + if(dbg().move_Steiner_vertices_level() > 0) { + auto _ = IO::make_color_guards(std::cerr); + std::cerr << "- No central point could be computed for at least one of the components, aborting moving the " + "vertex to the volume\n"; + // CGAL_error(); + } + return false; + } + CGAL_assertion(components_central_points->size() == nb_of_star_components); + boost::container::small_vector new_center_vertices; + new_center_vertices.reserve(nb_of_star_components); + + for(std::size_t component_index = 1; component_index <= nb_of_star_components; ++component_index) + { + const auto star_component_new_center_vertex = tr().tds().create_vertex(); + star_component_new_center_vertex->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_IN_VOLUME); + star_component_new_center_vertex->set_point((*components_central_points)[component_index - 1]); + new_center_vertices.push_back(star_component_new_center_vertex); + + const auto& component_facets = star_components.component_facets_range(component_index); + for(const auto& [cell, facet_index, facet_type] : component_facets) { + if(facet_type == Star_components_facets::Type_of_facet::INCIDENT_TO_V) { + continue; + } + cell->set_vertex(facet_index, star_component_new_center_vertex); + + // now unconstrain the facets of `cell` that were incident to `v`, because they are now + // incident to `star_component_new_center_vertex` instead of `v`. + for(int i = 0; i < 3; ++i) { + const auto incident_facet_index = Tds::vertex_triple_index(facet_index, i); + set_facet_as_not_constrained(Facet{cell, incident_facet_index}); + } + } // end for each facet of the current component, which are now all incident to `star_component_new_center_vertex` + using std::begin; + star_component_new_center_vertex->set_cell(begin(component_facets)->cell); // cell of the first facet + } // loop on each component of the star of `v` + + auto other_side_of_cdt_2 = [&](const Facet& facet, Face_index face_id) -> Facet { + const auto [cell, facet_index] = facet; + for(int i = 0; i < 3; ++i) { + const auto other_facet_index = Tds::vertex_triple_index(facet_index, i); + if(face_constraint_index(Facet{cell, other_facet_index}) == face_id) { + const auto opposite_vertex = cell->vertex(facet_index); + const auto neighbor_cell = cell->neighbor(other_facet_index); + return Facet{neighbor_cell, neighbor_cell->index(opposite_vertex)}; + } + } + CGAL_error(); + }; + + std::size_t nb_of_new_2d_faces = 0; + CGAL::Counting_output_iterator output_iterator{&nb_of_new_2d_faces}; + + if(v_type == CDT_3_vertex_type::STEINER_IN_FACE) { + CGAL_assertion(star_components.component_incident_constraint_face_ids.size() == 2); + const auto& [face_id, orientation] = star_components.face_ids_between_consecutive_components.front(); + auto [new_output_iterator, half_hole_front_facet, second_half_hole_front_facet] = + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, output_iterator); + output_iterator = new_output_iterator; + } else { + CGAL_assertion(v_type == CDT_3_vertex_type::STEINER_ON_EDGE); + std::optional previous_facet_to_reconnect_opt; + std::optional last_facet_to_reconnect_to_opt; + for(std::size_t component_index = 0, end = star_components.face_ids_between_consecutive_components.size(); + component_index < end; ++component_index) + { + const auto& [face_id, orientation] = star_components.face_ids_between_consecutive_components[component_index]; + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << "Component " << (component_index+1) << " with center " + << display_vert(new_center_vertices[component_index]) << "\n"; + if(this->debug().move_Steiner_vertices_level() > 2) { + const auto& component_facets = star_components.component_facets_range(component_index+1); + for(const auto& [cell, facet_index, facet_type] : component_facets) { + std::cerr << cdt_3_format(" - facet ({}) of with vertices:{}\n", + IO::oformat(Facet{cell, facet_index}), + display_facet(" ", Facet{cell, facet_index})); + } + } + std::cerr << "Now remove the Steiner vertex from the CDT_2 of face with id " << face_id << " (orientation: " + << (orientation == CGAL::NEGATIVE ? "negative" : "positive") << ")\n"; + } + auto [new_output_iterator, half_hole_front_facet, second_half_hole_front_facet] = + remove_Steiner_vertex_from_cdt_2(v, v_type, face_id, output_iterator); + output_iterator = new_output_iterator; + + Facet facet_to_reconnect = half_hole_front_facet; + if(facet_to_reconnect.first == Cell_handle{}) { + facet_to_reconnect = second_half_hole_front_facet; + } + CGAL_assertion(facet_to_reconnect.first != Cell_handle{}); + if(true == facet_to_reconnect.first->has_vertex(new_center_vertices[component_index])) { + facet_to_reconnect = other_side_of_cdt_2(facet_to_reconnect, face_id); + } + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << cdt_3_format("The facet ({}) to reconnect in the 3D triangulation is:{}\n", + IO::oformat(facet_to_reconnect), display_facet(" - ", facet_to_reconnect)); + auto other_facet = other_side_of_cdt_2(facet_to_reconnect, face_id); + std::cerr << cdt_3_format("The facet ({}) on the other side of the CDT_2 is:{}\n", + IO::oformat(other_facet), display_facet(" - ", other_facet)); + } + + if(!last_facet_to_reconnect_to_opt.has_value()) { + last_facet_to_reconnect_to_opt = facet_to_reconnect; + CGAL_assertion(previous_facet_to_reconnect_opt.has_value() == false); + } + if(previous_facet_to_reconnect_opt.has_value()) { + set_adjacency_between_3d_facets(*previous_facet_to_reconnect_opt, facet_to_reconnect); + } + previous_facet_to_reconnect_opt = other_side_of_cdt_2(facet_to_reconnect, face_id); + } + CGAL_assertion(previous_facet_to_reconnect_opt.has_value()); + CGAL_assertion(last_facet_to_reconnect_to_opt.has_value()); + set_adjacency_between_3d_facets(*previous_facet_to_reconnect_opt, *last_facet_to_reconnect_to_opt); + } + + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << "- number of new 2D faces created in the CDT: " << nb_of_new_2d_faces << '\n'; + } + + tr().tds().delete_vertex(v); + + CGAL_assertion(dbg().move_Steiner_vertices_level() < 2 || tr().tds().is_valid(true)); + + return true; + } // end move_one_Steiner_vertex_to_the_volume(..) void register_facet_to_be_constrained(Cell_handle cell, int facet_index) { const auto face_id = static_cast(cell->ccdt_3_data().face_constraint_index(facet_index)); @@ -1336,7 +2859,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto fh_2 = cell->ccdt_3_data().face_2(this->face_cdt_2(face_id), facet_index); fh_2->info().facet_3d = {}; fh_2->info().missing_subface = true; - this->set_facet_constrained({cell, facet_index}, -1, {}); + this->set_facet_as_not_constrained({cell, facet_index}); } void register_facet_to_be_constrained(Facet f) { @@ -1418,7 +2941,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D fh_2d->set_constraint(edge_index, false); mirror_fh_2d->set_constraint(mirror_edge_index, false); const auto v_Steiner_2d = non_const_cdt_2.insert(point, fh_2d); - v_Steiner_2d->info().vertex_handle_3d = v_Steiner; + vertex_3d(v_Steiner_2d) = v_Steiner; non_const_cdt_2.insert_constraint(va_2d, v_Steiner_2d); non_const_cdt_2.insert_constraint(vb_2d, v_Steiner_2d); @@ -1438,10 +2961,16 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D fc->info().is_outside_the_face = outside_on_left; fc->info().is_in_region = in_region_on_left; } while(++fc != fc_begin); - fc = cdt_2.incident_faces(v_Steiner_2d, fh_2d); - fc_begin = fc; + fc = fc_begin; do { - fc->info().missing_subface = true; + auto is_facet = self->is_2d_face_also_a_3d_facet(fc); + if(!is_facet) { + fc->info().missing_subface = true; + } else { + const auto [facet, orient] = *is_facet; + fc->info().missing_subface = false; + self->set_facet_constrained(facet, polygon_id, fc, orient); + } const auto v_Steiner_index = fc->index(v_Steiner_2d); const auto other_edge = cdt_2.mirror_edge({fc, v_Steiner_index}); fc->info().is_edge_also_in_3d_triangulation.set(other_edge.first->info().is_edge_also_in_3d_triangulation.test(other_edge.second)); @@ -1491,12 +3020,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D switch(tr().dimension()) { case 3: { typename T_3::Conflict_tester_3 tester(p, this); - this->find_conflicts(ch, tester, triple_of_output_iterators); + tr().find_conflicts(ch, tester, triple_of_output_iterators); break; } // dim 3 case 2: { typename T_3::Conflict_tester_2 tester(p, this); - this->find_conflicts(ch, tester, triple_of_output_iterators); + tr().find_conflicts(ch, tester, triple_of_output_iterators); break; } // dim 2 default: CGAL_error(); @@ -1528,8 +3057,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // vertices of the border of the cavity should point to cells outside of it for(auto [c, index]: exterior_border_facets_of_original_cavity) { - for(int i = 0; i < 3; ++i) { - auto v = c->vertex(tr().vertex_triple_index(index, i)); + for(auto v : tr().vertices(Facet{c, index})) { v->set_cell(c); } } @@ -1547,8 +3075,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D typename T_3::Vertex_triple_Facet_map outer_map; for(auto f: facets_of_cavity) { - typename T_3::Vertex_triple vt = this->make_vertex_triple(f); - this->make_canonical_oriented_triple(vt); + auto vt = T_3::make_canonical_oriented_triple(f); outer_map[vt] = f; } @@ -1645,12 +3172,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(false == get(v_filter_map, v)) continue; } auto vh = this->insert(get(vertex_point_map, v), hint_ch, false); - vh->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::CORNER); + vh->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::INPUT_VERTEX); hint_ch = vh->cell(); put(tr_vertex_pmap, v, vh); } - add_bbox_points_if_not_dimension_3(); + add_bbox_points(); if(this->debug().display_statistics()) { @@ -1755,43 +3282,20 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return f.first->ccdt_3_data().is_facet_constrained(f.second); } - auto number_of_constrained_facets() const + CDT_3_signed_index face_constraint_index(Cell_handle ch, int i) const { - return std::count_if(tr().all_facets_begin(), tr().all_facets_end(), - [this](auto f) { return is_facet_constrained(f); }); + return ch->ccdt_3_data().face_constraint_index(i); } - bool same_triangle(Facet f, CDT_2_face_handle fh) const { - return true; - const auto [c, facet_index] = f; - std::array f_vertices{c->vertex(tr().vertex_triple_index(facet_index,0)), - c->vertex(tr().vertex_triple_index(facet_index,1)), - c->vertex(tr().vertex_triple_index(facet_index,2))}; - std::sort(f_vertices.begin(), f_vertices.end()); - std::array fh_vertices{fh->vertex(0)->info().vertex_handle_3d, - fh->vertex(1)->info().vertex_handle_3d, - fh->vertex(2)->info().vertex_handle_3d}; - std::sort(fh_vertices.begin(), fh_vertices.end()); - return (f_vertices == fh_vertices); + CDT_3_signed_index face_constraint_index(const Facet& f) const + { + return face_constraint_index(f.first, f.second); } - void set_facet_constrained(Facet f, CDT_3_signed_index polygon_constraint_id, - CDT_2_face_handle fh) + auto number_of_constrained_facets() const { - CGAL_assertion(fh == CDT_2_face_handle{} || same_triangle(f, fh)); - - const auto [c, facet_index] = f; - c->ccdt_3_data().set_facet_constraint(facet_index, polygon_constraint_id, fh); - if(tr().dimension() > 2) { - const auto [n, n_index] = tr().mirror_facet({c, facet_index}); - n->ccdt_3_data().set_facet_constraint(n_index, polygon_constraint_id, fh); - } - if(fh == CDT_2_face_handle{}) return; - - if(fh != CDT_2_face_handle{}) { - fh->info().facet_3d = f; - fh->info().missing_subface = false; - } + return std::count_if(tr().all_facets_begin(), tr().all_facets_end(), + [this](auto f) { return is_facet_constrained(f); }); } template ) Polygon> @@ -1983,11 +3487,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return {min_distance, vertices_of_min_edge}; } - static void print_minimum_distance_info(const Min_distance_result& min_dist) { + void print_minimum_distance_info(const Min_distance_result& min_dist) const { std::cout << "Min distance between vertices: " << min_dist.min_distance << '\n' << " between vertices: : " - << CGAL::IO::oformat(min_dist.vertices_of_min_edge[0], CGAL::With_point_tag{}) << " " - << CGAL::IO::oformat(min_dist.vertices_of_min_edge[1], CGAL::With_point_tag{}) << "\n\n"; + << display_vert(min_dist.vertices_of_min_edge[0]) << " " + << display_vert(min_dist.vertices_of_min_edge[1]) << "\n\n"; } struct Constraint_distance_result { @@ -2125,6 +3629,38 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } + auto vertex_type(Vertex_handle v) const { + return v->ccdt_3_data().vertex_type(); + } + + bool is_Steiner_vertex_on_surface(Vertex_handle v) const { + switch(vertex_type(v)) { + case CDT_3_vertex_type::INPUT_VERTEX: + case CDT_3_vertex_type::FREE: + case CDT_3_vertex_type::STEINER_IN_VOLUME: + case CDT_3_vertex_type::BBOX: + return false; + case CDT_3_vertex_type::STEINER_IN_FACE: + case CDT_3_vertex_type::STEINER_ON_EDGE: + return true; + } + CGAL_unreachable(); + } + + auto move_Steiner_vertices_to_the_volume() { + if(dbg().move_Steiner_vertices_level() > 1) { + std::cerr << "move_Steiner_vertices_to_the_volume()\n"; + dump_constrained_facets_to_off("dump_before_moving_steiner_vertices.off"); + } + std::vector steiner_vertices; + for(auto v : this->finite_vertex_handles()) { + if(is_Steiner_vertex_on_surface(v)) steiner_vertices.push_back(v); + } + for(auto v : steiner_vertices) { + move_one_Steiner_vertex_to_the_volume(v); + } + CGAL_assertion(tr().tds().is_valid(true)); + } private: @@ -2138,7 +3674,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D case Vertex_marker::CAVITY: std::cerr << "CAVITY"; break; case Vertex_marker::CAVITY_ABOVE: std::cerr << "CAVITY_ABOVE"; break; case Vertex_marker::CAVITY_BELOW: std::cerr << "CAVITY_BELOW"; break; - case Vertex_marker::nb_of_markers: CGAL_unreachable(); break; } std::cerr << ")\n"; } @@ -2199,9 +3734,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto insert_constraint_in_cdt_2 = [&](const auto& va, const auto& vb) { if(this->debug().input_faces()) { std::cerr << "cdt_2.insert_constraint (" - << IO::oformat(va->info().vertex_handle_3d, with_point_and_info) - << " , " - << IO::oformat(vb->info().vertex_handle_3d, with_point_and_info) + << display_vert(vertex_3d(va)) << " , " << display_vert(vertex_3d(vb)) << ")\n"; } auto cstr_id = cdt_2.insert_constraint(va, vb); @@ -2209,10 +3742,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; auto insert_vertex_in_cdt_2 = [&](const auto& v) { auto vh_2d = cdt_2.insert(tr().point(v)); - vh_2d->info().vertex_handle_3d = v; + vertex_3d(vh_2d) = v; return vh_2d; }; + vertex_3d(cdt_2.infinite_vertex()) = this->infinite_vertex(); + for(const auto& handles : vec_of_handles) { const auto first_2d = insert_vertex_in_cdt_2(handles.front()); @@ -2221,7 +3756,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D it != end; /* incremented in the loop */) { const auto va = *it; - CGAL_assertion(previous_2d->info().vertex_handle_3d == va); + CGAL_assertion(vertex_3d(previous_2d) == va); ++it; const auto vb = *it; const auto c_id = this->constraint_from_extremities(va, vb); @@ -2286,25 +3821,28 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(auto fh: cdt_2.all_face_handles()) { - fh->info().is_outside_the_face = -1; + fh->info().is_outside_the_face = Face_is_outside_the_face::UNKNOWN; } struct Face_handle_and_outside { CDT_2_face_handle fh; - bool outside; + Face_is_outside_the_face outside; }; const auto d = cdt_2.dimension(); std::stack stack; - stack.push({cdt_2.infinite_face(), true}); + stack.push({cdt_2.infinite_face(), OUTSIDE}); while(!stack.empty()) { const auto [fh, outside] = stack.top(); stack.pop(); - if(fh->info().is_outside_the_face == -1) { + if(fh->info().is_outside_the_face == Face_is_outside_the_face::UNKNOWN) { fh->info().is_outside_the_face = outside; - CGAL_assertion(!cdt_2.is_infinite(fh) || outside == true); + CGAL_assertion(!cdt_2.is_infinite(fh) || outside == OUTSIDE); for(int i = 0; i <= d; ++i) { const auto neighbor = fh->neighbor(i); - const auto new_outside = is_constrained_with_odd_nb_of_constraints(cdt_2, fh, i) ? !outside : outside; - if(neighbor->info().is_outside_the_face == -1) { + const auto new_outside = + is_constrained_with_odd_nb_of_constraints(cdt_2, fh, i) + ? (outside == OUTSIDE ? INSIDE : OUTSIDE) + : outside; + if(neighbor->info().is_outside_the_face == Face_is_outside_the_face::UNKNOWN) { stack.push({neighbor, new_outside}); } } @@ -2314,7 +3852,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().input_faces()) { int counter = 0; for(const auto fh: cdt_2.finite_face_handles()) { - if(!fh->info().is_outside_the_face) ++counter; + if(fh->info().is_outside_the_face == INSIDE) ++counter; } std::cerr << counter << " triangles(s) in the face\n"; } @@ -2341,8 +3879,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto [ch2, index2] = cdt_2.mirror_edge({ch, index}); const auto vd = ch2->vertex(index2); CGAL_assertion(!cdt_2.is_edge(vb, vd)); - const auto vb_3d = vb->info().vertex_handle_3d; - const auto vd_3d = vd->info().vertex_handle_3d; + const auto vb_3d = vertex_3d(vb); + const auto vd_3d = vertex_3d(vd); if(this->is_edge(vb_3d, vd_3d)) { // let's insert the diagonal [bd] in the CDT_2 cdt_2.insert_constraint(vb, vd); @@ -2366,30 +3904,32 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D search_for_missing_subfaces(CDT_3_signed_index polygon_constraint_id, Search_for_missing_subfaces_option option = Search_for_missing_subfaces_option::DEFAULT) { bool something_has_changed = false; - const CDT_2& cdt_2 = face_cdt_2(polygon_constraint_id); + const CDT_2& cdt_2 = this->face_cdt_2(polygon_constraint_id); - face_constraint_misses_subfaces_reset(static_cast(polygon_constraint_id)); + this->face_constraint_misses_subfaces_reset(static_cast(polygon_constraint_id)); for(const auto fh: cdt_2.all_face_handles()) { - if(fh->info().is_outside_the_face) continue; - const auto v0 = fh->vertex(0)->info().vertex_handle_3d; - const auto v1 = fh->vertex(1)->info().vertex_handle_3d; - const auto v2 = fh->vertex(2)->info().vertex_handle_3d; - Cell_handle c; - int i, j, k; - if(!tr().is_facet(v0, v1, v2, c, i, j, k)) { + if(fh->info().is_outside_the_face == OUTSIDE) { + fh->info().missing_subface = false; + continue; + } + const auto v0 = vertex_3d(fh->vertex(0)); + const auto v1 = vertex_3d(fh->vertex(1)); + const auto v2 = vertex_3d(fh->vertex(2)); + auto is_facet = this->is_2d_face_also_a_3d_facet(fh); + if(!is_facet) { fh->info().missing_subface = true; - face_constraint_misses_subfaces_set(static_cast(polygon_constraint_id)); + this->face_constraint_misses_subfaces_set(static_cast(polygon_constraint_id)); something_has_changed = true; #if CGAL_CDT_3_DEBUG_MISSING_TRIANGLES std::cerr << cdt_3_format("Missing triangle in polygon #{}:\n", polygon_constraint_id); write_triangle(std::cerr, v0, v1, v2); #endif // CGAL_CDT_3_DEBUG_MISSING_TRIANGLES } else { + const auto [facet, orient] = *is_facet; fh->info().missing_subface = false; - const int facet_index = 6 - i - j - k; if(option == Search_for_missing_subfaces_option::SEARCH_FOR_UNCONSTRAINED_FACETS && - !is_facet_constrained({c, facet_index})) + !is_facet_constrained(facet)) { something_has_changed = true; if(this->debug().restore_faces()) { @@ -2398,7 +3938,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D << this->display_vert(v2) << '\n'; } } - set_facet_constrained({c, facet_index}, polygon_constraint_id, fh); + set_facet_constrained(facet, polygon_constraint_id, fh, orient); } } if(option == Search_for_missing_subfaces_option::SEARCH_FOR_UNCONSTRAINED_FACETS && something_has_changed) { @@ -2408,10 +3948,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "Some missing subfaces were found in polygon #" << polygon_constraint_id << ".\n"; for(const auto fh: cdt_2.all_face_handles()) { - if(fh->info().is_outside_the_face || !fh->info().missing_subface) continue; - const auto v0 = fh->vertex(0)->info().vertex_handle_3d; - const auto v1 = fh->vertex(1)->info().vertex_handle_3d; - const auto v2 = fh->vertex(2)->info().vertex_handle_3d; + if(fh->info().is_outside_the_face == OUTSIDE || !fh->info().missing_subface) continue; + const auto [v0, v1, v2] = this->apply_functor(vertex_3d, cdt_2.vertices(fh)); std::cerr << " - missing triangle with vertices " << this->display_vert(v0) << ", " << this->display_vert(v1) << ", " << this->display_vert(v2) << '\n'; } @@ -2430,7 +3968,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto i = unsigned(edge.second); return false == face->info().is_edge_also_in_3d_triangulation.test(i); }, - +[](CDT_2_face_handle face_handle) { return false == face_handle->info().is_outside_the_face; }); + +[](CDT_2_face_handle face_handle) { + return INSIDE == face_handle->info().is_outside_the_face; + }); boost::breadth_first_search(dual, fh, boost::color_map(typename CDT_2_types::Color_map_is_in_region()) .visitor(boost::make_bfs_visitor(boost::write_property( @@ -2452,8 +3992,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(int index = 0; index < 3; ++index) { if(fh_region_set.count(fh->neighbor(index)) > 0) continue; // otherwise we have a border edge: fh->neighbor(i) is not in the region - const auto va = fh->vertex(CDT_2::ccw(index))->info().vertex_handle_3d; - const auto vb = fh->vertex(CDT_2:: cw(index))->info().vertex_handle_3d; + const auto va = vertex_3d(fh->vertex(CDT_2::ccw(index))); + const auto vb = vertex_3d(fh->vertex(CDT_2:: cw(index))); Cell_handle c; int i, j; CGAL_assume_code(bool b =) @@ -2491,9 +4031,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto pc = this->point(vc); const auto pd = this->point(vd); for(const auto fh_2d : fh_region) { - const auto v0 = fh_2d->vertex(0)->info().vertex_handle_3d; - const auto v1 = fh_2d->vertex(1)->info().vertex_handle_3d; - const auto v2 = fh_2d->vertex(2)->info().vertex_handle_3d; + const auto v0 = vertex_3d(fh_2d->vertex(0)); + const auto v1 = vertex_3d(fh_2d->vertex(1)); + const auto v2 = vertex_3d(fh_2d->vertex(2)); if(vc == v0 || vc == v1 || vc == v2 || vd == v0 || vd == v1 || vd == v2) { continue; } @@ -2600,14 +4140,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().regions()) { std::cerr << "Edge interior intersects region in face with vertices:\n " - << IO::oformat(fh->vertex(0)->info().vertex_handle_3d, with_point_and_info) << "\n " - << IO::oformat(fh->vertex(1)->info().vertex_handle_3d, with_point_and_info) << "\n " - << IO::oformat(fh->vertex(2)->info().vertex_handle_3d, with_point_and_info) << "\n"; + << display_vert(vertex_3d(fh->vertex(0))) << "\n " + << display_vert(vertex_3d(fh->vertex(1))) << "\n " + << display_vert(vertex_3d(fh->vertex(2))) << "\n"; } - return {1, - {fh->vertex(0)->info().vertex_handle_3d, fh->vertex(1)->info().vertex_handle_3d, - fh->vertex(2)->info().vertex_handle_3d}}; + return {1, {vertex_3d(fh->vertex(0)), vertex_3d(fh->vertex(1)), vertex_3d(fh->vertex(2))}}; } while(false == line_face_circulator->has_vertex(vq_2d) && ++line_face_circulator != start); } // End of if(region_vertices.count(vc) > 0 && region_vertices.count(vd) > 0 && // border_edges_set.count(CGAL::make_sorted_pair(vc, vd)) == 0) @@ -2896,7 +4434,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }); CGAL_assume(vertex_above != Vertex_handle{}); if(this->debug().regions()) { - std::cerr << "The vertex above the region is " << IO::oformat(vertex_above, with_point_and_info) << "\n"; + std::cerr << "The vertex above the region is " << display_vert(vertex_above) << "\n"; } CGAL::unordered_flat_set> remaining_facets_of_border(facets_of_border.begin(), @@ -2913,9 +4451,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto [c, i] = facet; if(this->debug().regions()) { std::cerr << " stack facet " << (above_below == 1 ? "above" : "below") << ": " - << IO::oformat(c->vertex(tr().vertex_triple_index(i, 0)), with_point_and_info) << " " - << IO::oformat(c->vertex(tr().vertex_triple_index(i, 1)), with_point_and_info) << " " - << IO::oformat(c->vertex(tr().vertex_triple_index(i, 2)), with_point_and_info) << "\n"; + << display_vert(c->vertex(tr().vertex_triple_index(i, 0))) << " " + << display_vert(c->vertex(tr().vertex_triple_index(i, 1))) << " " + << display_vert(c->vertex(tr().vertex_triple_index(i, 2))) << "\n"; } if(above_below == 1) { @@ -2972,14 +4510,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(remaining_facets_of_border.erase(neighbor_facet) > 0) { if(this->debug().regions()) { std::cerr << " -> stack push new facet " << (next_facet_is_above_below == 1 ? "above" : "below") << ": " - << IO::oformat(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 0)), - with_point_and_info) + << display_vert(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 0))) << " " - << IO::oformat(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 1)), - with_point_and_info) + << display_vert(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 1))) << " " - << IO::oformat(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 2)), - with_point_and_info) + << display_vert(neighbor_facet.first->vertex(tr().vertex_triple_index(neighbor_facet.second, 2))) << "\n"; } stack.push({neighbor_facet, next_facet_is_above_below}); @@ -3025,7 +4560,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(is_marked(v, Vertex_marker::CAVITY)) { clear_mark(v, Vertex_marker::CAVITY); if(this->debug().regions()) { - std::cerr << " " << IO::oformat(v, with_point_and_info) << "\n"; + std::cerr << " " << display_vert(v) << "\n"; } } } @@ -3054,10 +4589,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D })); } - using Conforming_Dt::with_offset; - using Conforming_Dt::with_point; - using Conforming_Dt::with_point_and_info; - // ------------------------- // restore_subface_region // ------------------------- @@ -3070,7 +4601,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto guard_color = CGAL::IO::make_color_guards(std::cerr); std::cerr << "restore_subface_region face index: " << face_index << ", region #" << region_index << "\n"; } - auto guard_indenting = CGAL::IO::make_indenting_guards("| ", std::cerr, std::cout); + auto guard_indenting = CGAL::IO::make_indenting_guards("| ", std::cerr); const auto& cdt_2 = non_const_cdt_2; const auto& fh_region = non_const_fh_region; @@ -3079,7 +4610,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::set vertices; for(const auto fh_2d: fh_region) { for(int i = 0; i < 3; ++i) { - vertices.insert(fh_2d->vertex(i)->info().vertex_handle_3d); + vertices.insert(vertex_3d(fh_2d->vertex(i))); } } return vertices; @@ -3095,7 +4626,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << "region_border_vertices.size() = " << region_border_vertices.size() << "\n"; for(auto v : region_border_vertices) { - std::cerr << cdt_3_format(" {}\n", IO::oformat(v, with_point)); + std::cerr << cdt_3_format(" {}\n", with_point(v)); } std::ofstream out(cdt_3_format("dump_border_edges_region_{}_{}.polylines.txt", face_index, region_index)); out.precision(17); @@ -3113,7 +4644,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::set diagonal; for(auto fh : fh_region) { for(int i = 0; i < 3; ++i) { - auto [it, new_vertex] = vertices.insert(fh->vertex(i)->info().vertex_handle_3d); + auto [it, new_vertex] = vertices.insert(vertex_3d(fh->vertex(i))); if(!new_vertex) { diagonal.insert(*it); } @@ -3128,10 +4659,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto diagonal_index = fh_region[0]->index(fh_region[1]); CGAL_assertion(diagonal_index >= 0 && diagonal_index < 3); - const auto v0 = fh_region[0]->vertex(diagonal_index)->info().vertex_handle_3d; - const auto v1 = fh_region[0]->vertex(cdt_2.ccw(diagonal_index))->info().vertex_handle_3d; - const auto v2 = fh_region[0]->vertex(cdt_2.cw(diagonal_index))->info().vertex_handle_3d; - const auto v3 = fh_region[1]->vertex(fh_region[1]->index(fh_region[0]))->info().vertex_handle_3d; + const auto v0 = vertex_3d(fh_region[0]->vertex(diagonal_index)); + const auto v1 = vertex_3d(fh_region[0]->vertex(cdt_2.ccw(diagonal_index))); + const auto v2 = vertex_3d(fh_region[0]->vertex(cdt_2.cw(diagonal_index))); + const auto v3 = vertex_3d(fh_region[1]->vertex(fh_region[1]->index(fh_region[0]))) ; if(tr().is_facet(v0, v1, v3) && tr().is_facet(v0, v3, v2)) { if(cdt_2.orientation(v0->point(), v1->point(), v3->point()) == CGAL::POSITIVE && @@ -3146,14 +4677,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto mirror_edge = cdt_2.mirror_edge({fh, i}); fh->set_constraint(i, mirror_edge.first->is_constrained(mirror_edge.second)); } - int i, j, k; - Cell_handle c; - [[maybe_unused]] bool fh_is_3d_facet = tr().is_facet(fh->vertex(0)->info().vertex_handle_3d, - fh->vertex(1)->info().vertex_handle_3d, - fh->vertex(2)->info().vertex_handle_3d, - c, i, j, k); - CGAL_assertion(fh_is_3d_facet); - set_facet_constrained({c, 6-i-j-k}, face_index, fh); + const auto fh_is_3d_facet = is_2d_face_also_a_3d_facet(fh); + CGAL_assertion(fh_is_3d_facet.has_value()); + const auto [facet, orient] = *fh_is_3d_facet; + set_facet_constrained(facet, face_index, fh, orient); fh->info().missing_subface = false; } return true; @@ -3170,13 +4697,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format ("NOTE: diagonal: {:.6} {:.6} {} in tr\n", - IO::oformat(*diagonal.begin(), with_point), - IO::oformat(*std::next(diagonal.begin()), with_point), + with_point(*diagonal.begin()), + with_point(*std::next(diagonal.begin())), this->is_edge(*diagonal.begin(), *std::next(diagonal.begin())) ? "IS" : "is NOT"); std::cerr << cdt_3_format( "NOTE: the other diagonal: {:.6} {:.6} {} in tr\n", - IO::oformat(*other_diagonal.begin(), with_point), - IO::oformat(*std::next(other_diagonal.begin()), with_point), + with_point(*other_diagonal.begin()), + with_point(*std::next(other_diagonal.begin())), this->is_edge(*other_diagonal.begin(), *std::next(other_diagonal.begin())) ? "IS" : "is NOT"); if(cdt_2.geom_traits().side_of_oriented_circle_2_object()( (*region_border_vertices.begin())->point(), (*std::next(region_border_vertices.begin()))->point(), @@ -3305,13 +4832,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << " "; auto vertices = tr().vertices(f); for(int i = 0; i < 3; ++i) { - std::cerr << IO::oformat(vertices[i], with_point_and_info) << " "; + std::cerr << display_vert(vertices[i]) << " "; } std::cerr << "\n"; } std::cerr << " Vertices:\n"; for(const auto& v : std::set(original_vertices_of_lower_cavity.begin(), original_vertices_of_lower_cavity.end())) { - std::cerr << " " << IO::oformat(v, with_point_and_info) << "\n"; + std::cerr << " " << display_vert(v) << "\n"; } std::cerr << "Upper cavity:\n"; std::cerr << " Facets:\n"; @@ -3319,21 +4846,21 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << " "; auto vertices = tr().vertices(f); for(int i = 0; i < 3; ++i) { - std::cerr << IO::oformat(vertices[i], with_point_and_info) << " "; + std::cerr << display_vert(vertices[i]) << " "; } std::cerr << "\n"; } std::cerr << " Vertices:\n"; for(const auto& v : std::set(original_vertices_of_upper_cavity.begin(), original_vertices_of_upper_cavity.end())) { - std::cerr << " " << IO::oformat(v, with_point_and_info) << "\n"; + std::cerr << " " << display_vert(v) << "\n"; } } auto register_internal_constrained_facet = [this](Facet f) { this->register_facet_to_be_constrained(f); }; - std::optional indent_guards; + std::optional indent_guards; if(this->debug().copy_triangulation_into_hole() || this->debug().regions()) { std::cerr << "# upper cavity\n"; - indent_guards.emplace(IO::make_indenting_guards(" ", std::cerr, std::cout, std::clog)); + indent_guards.emplace(IO::make_indenting_guards(" ", std::cerr)); } [[maybe_unused]] const auto [upper_cavity_triangulation, vertices_of_upper_cavity, map_upper_cavity_vertices_to_ambient_vertices, facets_of_upper_cavity, @@ -3346,7 +4873,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D if(this->debug().copy_triangulation_into_hole() || this->debug().regions()) { indent_guards.reset(); std::cerr << "# lower cavity\n"; - indent_guards.emplace(IO::make_indenting_guards(" ", std::cerr, std::cout, std::clog)); + indent_guards.emplace(IO::make_indenting_guards(" ", std::cerr)); } [[maybe_unused]] const auto [lower_cavity_triangulation, vertices_of_lower_cavity, map_lower_cavity_vertices_to_ambient_vertices, facets_of_lower_cavity, @@ -3362,9 +4889,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // the following transform_reduce is like `std::any_of` but without the fast-exit if(std::transform_reduce(fh_region.begin(), fh_region.end(), false, std::logical_or{}, [&](auto fh) { - const auto v0 = fh->vertex(0)->info().vertex_handle_3d; - const auto v1 = fh->vertex(1)->info().vertex_handle_3d; - const auto v2 = fh->vertex(2)->info().vertex_handle_3d; + const auto v0 = vertex_3d(fh->vertex(0)); + const auto v1 = vertex_3d(fh->vertex(1)); + const auto v2 = vertex_3d(fh->vertex(2)); auto is_fh_facet_of = [&](const auto& tr) -> std::optional { return this->vertex_triple_is_facet_of_other_triangulation(*this, v0, v1, v2, tr); }; @@ -3376,8 +4903,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto display_face = [&]() { std::stringstream s; s.precision(std::cerr.precision()); - s << "(" << IO::oformat(v0, this->with_offset) << ", " << IO::oformat(v1, this->with_offset) - << ", " << IO::oformat(v2, this->with_offset) << ") = ( " + s << "(" << with_offset(v0) << ", " << with_offset(v1) << ", " << with_offset(v2) << ") = ( " << tr().point(v0) << " " << tr().point(v1) << " " << tr().point(v2) << " )"; return s.str(); @@ -3441,12 +4967,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D cells_of_upper_cavity.size() > original_intersecting_cells.size()) { std::cerr << cdt_3_format("!! Cavity has grown and has now " - "{} vertices in upper cavity and {} in lower, " - "{} facets in upper cavity and {} in lower\n", - vertices_of_upper_cavity.size(), - vertices_of_lower_cavity.size(), - facets_of_upper_cavity.size(), - facets_of_lower_cavity.size()); + "{} vertices in upper cavity and {} in lower, " + "{} facets in upper cavity and {} in lower\n", + vertices_of_upper_cavity.size(), + vertices_of_lower_cavity.size(), + facets_of_upper_cavity.size(), + facets_of_lower_cavity.size()); } } @@ -3461,9 +4987,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D CGAL_assertion(vt[0] != vt[2]); CGAL_assertion(vt[1] != vt[2]); std::cerr << cdt_3_format("outer map: Adding {}triple ({:.6}, {:.6}, {:.6})\n", extra, - IO::oformat(vt[0], with_point), - IO::oformat(vt[1], with_point), - IO::oformat(vt[2], with_point)); + with_point(vt[0]), + with_point(vt[1]), + with_point(vt[2])); std::stringstream filename; filename << "dump_upper_outer_map_region_" << face_index << "_" << region_index << ".off"; std::ofstream out(filename.str()); @@ -3475,8 +5001,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; auto fill_outer_map_of_cavity = [&](const auto&, const auto& facets) { for(auto f : facets) { - typename T_3::Vertex_triple vt = this->make_vertex_triple(f); - this->make_canonical_oriented_triple(vt); + auto vt = T_3::make_canonical_oriented_triple(f); add_to_outer_map(vt, f); } }; @@ -3496,19 +5021,17 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D continue; // we might be in a sliver in the plane of the polygon const auto [fh_2d, reverse_orientation] = *is_facet; if(this->debug().regions()) facets_of_polygon.push_back(f); - const auto vt_aux = this->make_vertex_triple(f); - typename T_3::Vertex_triple vt{map_cavity_vertices_to_ambient_vertices[vt_aux[0]], - map_cavity_vertices_to_ambient_vertices[vt_aux[1]], - map_cavity_vertices_to_ambient_vertices[vt_aux[2]]}; - this->make_canonical_oriented_triple(vt); + auto vt = T_3::make_canonical_oriented_mapped_triple(map_cavity_vertices_to_ambient_vertices, f); if(reverse_orientation == is_upper_cavity) { std::swap(vt[1], vt[2]); } - auto new_cell = this->tds().create_cell(); + auto new_cell = this->tds().create_cell(vt[0], vt[1], vt[2], this->infinite_vertex()); pseudo_cells.emplace_back(new_cell, fh_2d); - new_cell->set_vertices(vt[0], vt[1], vt[2], this->infinite_vertex()); - CGAL_assertion(static_cast(facet_is_facet_of_cdt_2(*this, {new_cell, 3}, cdt_2))); - add_to_outer_map(vt, {new_cell, 3}, "pseudo "); + Facet new_facet{new_cell, 3}; + CGAL_assertion_code(const auto new_facet_is_2d = facet_is_facet_of_cdt_2(*this, new_facet, cdt_2)); + CGAL_assertion(new_facet_is_2d.has_value() && + new_facet_is_2d->fh == fh_2d && new_facet_is_2d->reversed_orientation != is_upper_cavity); + add_to_outer_map(vt, new_facet, "pseudo "); } if(this->debug().regions()) { std::ofstream out(cdt_3_format("dump_{}_pseudo_cells_region_{}_{}.off", is_upper_cavity ? "upper" : "lower", @@ -3529,9 +5052,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "upper_inner_map:\n"; for(auto [vt, _] : upper_inner_map) { std::cerr << cdt_3_format(" {:.6}, {:.6}, {:.6})\n", - IO::oformat(vt[0], with_point), - IO::oformat(vt[1], with_point), - IO::oformat(vt[2], with_point)); + with_point(vt[0]), with_point(vt[1]), with_point(vt[2])); } } this->copy_triangulation_into_hole(map_upper_cavity_vertices_to_ambient_vertices, @@ -3547,12 +5068,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::vector> new_constrained_facets; new_constrained_facets.reserve(pseudo_cells.size()); for(const auto& [c, fh_2d] : pseudo_cells) { - const Facet f = this->mirror_facet({c, 3}); - new_constrained_facets.emplace_back(f, fh_2d); - CGAL_assertion(static_cast(facet_is_facet_of_cdt_2(*this, f, cdt_2))); - auto vt = this->make_vertex_triple(f); - this->make_canonical_oriented_triple(vt); - add_to_outer_map(vt, f, "pseudo "); + const Facet f{c, 3}; + const Facet opposite_f = this->mirror_facet(f); + new_constrained_facets.emplace_back(opposite_f, fh_2d); + CGAL_assertion(static_cast(facet_is_facet_of_cdt_2(*this, opposite_f, cdt_2))); + auto vt = T_3::make_canonical_oriented_triple(opposite_f); + add_to_outer_map(vt, opposite_f, "pseudo "); this->tds().delete_cell(c); } fill_outer_map_of_cavity(lower_cavity_triangulation, facets_of_lower_cavity); @@ -3564,9 +5085,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "outer_map:\n"; for(auto [vt, _] : outer_map) { std::cerr << cdt_3_format(" {:.6}, {:.6}, {:.6})\n", - IO::oformat(vt[0], with_point), - IO::oformat(vt[1], with_point), - IO::oformat(vt[2], with_point)); + with_point(vt[0]), with_point(vt[1]), with_point(vt[2])); } std::stringstream filename; filename << "dump_lower_outer_map_region_" << face_index << "_" << region_index << ".off"; @@ -3603,7 +5122,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::for_each(facets_of_upper_cavity.begin(), facets_of_upper_cavity.end(), restore_markers); for(const auto& [f, f2d] : new_constrained_facets) { - set_facet_constrained(f, face_index, f2d); + set_facet_constrained(T_3::mirror_facet(f), face_index, f2d); f2d->info().missing_subface = false; } CGAL_assume(!this->debug().validity() || this->is_valid(true)); @@ -3636,11 +5155,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D static auto facet_is_facet_of_cdt_2(const Tr& tr, typename Tr::Facet f, const CDT_2& cdt_2) -> std::optional { - const auto [c, facet_index] = f; - const auto v0 = c->vertex(Tr::vertex_triple_index(facet_index, 0)); - const auto v1 = c->vertex(Tr::vertex_triple_index(facet_index, 1)); - const auto v2 = c->vertex(Tr::vertex_triple_index(facet_index, 2)); - + const auto [v0, v1, v2] = tr.vertices(f); auto v = vertex_of_cdt_2_functor(cdt_2); const auto cdt_2_v0 = v(tr.point(v0)); @@ -3658,6 +5173,38 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D return std::nullopt; } + std::optional> + is_2d_face_also_a_3d_facet(CDT_2_face_handle fh_2d) const + { + const auto v0 = vertex_3d(fh_2d->vertex(0)); + const auto v1 = vertex_3d(fh_2d->vertex(1)); + const auto v2 = vertex_3d(fh_2d->vertex(2)); + typename T_3::Cell_handle c; + std::array indices; + const bool is_facet = tr().is_facet(v0, v1, v2, c, indices[0], indices[1], indices[2]); + if(is_facet) { + const int facet_index = 6 - indices[0] - indices[1] - indices[2]; + auto it = std::min_element(indices.begin(), indices.end()); + std::rotate(indices.begin(), it, indices.end()); + const std::array vertex_triple_index = { tr().vertex_triple_index(facet_index, 0), + tr().vertex_triple_index(facet_index, 1), + tr().vertex_triple_index(facet_index, 2) }; + CGAL_assertion(indices[0] == vertex_triple_index[0]); + CGAL_assertion((indices[1] == vertex_triple_index[1] && indices[2] == vertex_triple_index[2]) || + (indices[1] == vertex_triple_index[2] && indices[2] == vertex_triple_index[1])); + Orientation orient = indices[1] == vertex_triple_index[1] ? CGAL::EQUAL : CGAL::NEGATIVE; + + Facet facet{c, facet_index}; + if(orient == CGAL::NEGATIVE) { + facet = this->mirror_facet(facet); + } + CGAL_assertion(same_triple(facet, fh_2d)); + return std::make_pair(facet, CGAL::EQUAL); + } else { + return std::nullopt; + } + } + auto edge_of_cdt_2(const CDT_2& cdt_2, const Vertex_handle va, const Vertex_handle vb) const -> std::optional { @@ -3744,9 +5291,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D map_cavity_vertices_to_ambient_vertices[cavity_v] = v; if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format("inserted {}cavity vertex {:.6} -> {:.6}\n", - extra, - IO::oformat(cavity_v, with_point_and_info), - IO::oformat(v, with_point_and_info)); + extra, + display_vert(cavity_v), + display_vert(v)); } return cavity_v; }; @@ -3827,8 +5374,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // IO::oformat(a), IO::oformat(b), IO::oformat(CGAL::angle(a, steiner_pt, b))) // << '\n'; if(CGAL::angle(a, steiner_pt, b) != CGAL::ACUTE) { - const auto va_3d = va->info().vertex_handle_3d; - const auto vb_3d = vb->info().vertex_handle_3d; + const auto va_3d = vertex_3d(va); + const auto vb_3d = vertex_3d(vb); return std::make_pair(va_3d, vb_3d); } } @@ -3918,25 +5465,21 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } cleanup(); - // assert(is_valid(true)); - // this->study_bug = true; const auto v = this->insert_in_cdt_3(steiner_pt, lt, ch, li, lj, insert_in_conflict_visitor);// TODO: use "insert in hole" - // this->study_bug = false; - // assert(is_valid(true)); if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().Steiner_points()) { - std::cerr << " -> " << IO::oformat(v, with_offset) << '\n'; + std::cerr << " -> " << with_offset(v) << '\n'; } v->ccdt_3_data().set_Steiner_vertex_in_face(face_index); [[maybe_unused]] typename CDT_2::Locate_type lt_2; int i; auto fh = cdt_2.locate(steiner_pt, lt_2, i, fh_2d); - CGAL_assertion(!fh->info().is_outside_the_face); CGAL_USE(fh); + CGAL_assertion(fh->info().is_outside_the_face == INSIDE); CGAL_USE(fh); const auto v_2d = non_const_cdt_2.insert(steiner_pt, fh_2d); - v_2d->info().vertex_handle_3d = v; + vertex_3d(v_2d) = v; auto f_circ = cdt_2.incident_faces(v_2d); const auto end = f_circ; do { - f_circ->info().is_outside_the_face = false; + f_circ->info().is_outside_the_face = INSIDE; } while(++f_circ != end); search_for_missing_subfaces(face_index); return std::nullopt; @@ -3949,8 +5492,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto mid = construct_midpoint(a, b); if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().Steiner_points()) { std::cerr << cdt_3_format("Inserting Steiner (midpoint) point {} of constrained edge ({:.6} , {:.6})\n", - IO::oformat(mid), IO::oformat(va_3d, with_point_and_info), - IO::oformat(vb_3d, with_point_and_info)); + IO::oformat(mid), display_vert(va_3d), display_vert(vb_3d)); } auto&& contexts = this->constraint_hierarchy.contexts(va_3d, vb_3d); if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().verbose_special_cases()) { @@ -3963,8 +5505,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D vh_it != end; ++vh_it) { std::cerr << cdt_3_format(" {} {}\n", - (vh_it == current) ? '>' : '-', - IO::oformat(*vh_it, with_point_and_info)); + (vh_it == current) ? '>' : '-', + display_vert(*vh_it)); } } } @@ -3973,7 +5515,6 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto& context = *contexts.begin(); const auto constrained_polyline_id = context.id(); CGAL_assertion(constrained_polyline_id != Constrained_polyline_id{}); - // this->study_bug = true; Locate_type mid_lt; int mid_li, min_lj; Cell_handle mid_c = tr().locate(mid, mid_lt, mid_li, min_lj, va_3d->cell()); @@ -3982,10 +5523,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D this->insert_Steiner_point_on_subconstraint(mid, mid_c, {va_3d, vb_3d}, constrained_polyline_id, insert_in_conflict_visitor); if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().Steiner_points()) { - std::cerr << " -> " << IO::oformat(v, with_offset) << '\n'; + std::cerr << " -> " << with_offset(v) << '\n'; } - // this->study_bug = false; - // assert(is_valid(true)); } bool restore_face(CDT_3_signed_index face_index) { @@ -4001,13 +5540,13 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(const auto& edge : cdt_2.finite_edges()) { const auto fh = edge.first; const auto i = edge.second; - const auto va_3d = fh->vertex(cdt_2.cw(i))->info().vertex_handle_3d; - const auto vb_3d = fh->vertex(cdt_2.ccw(i))->info().vertex_handle_3d; + const auto va_3d = vertex_3d(fh->vertex(cdt_2.cw(i))); + const auto vb_3d = vertex_3d(fh->vertex(cdt_2.ccw(i))); const bool is_3d = this->is_edge(va_3d, vb_3d); if constexpr(cdt_3_can_use_cxx20_format()) { if(this->debug().copy_triangulation_into_hole() || this->debug().conforming_validation()) { - std::cerr << cdt_3_format("Edge is 3D: {:6} ({} , {})\n", is_3d, IO::oformat(va_3d, with_point_and_info), - IO::oformat(vb_3d, with_point_and_info)); + std::cerr << cdt_3_format("Edge is 3D: {:6} ({} , {})\n", is_3d, display_vert(va_3d), + display_vert(vb_3d)); } } CGAL_assertion(is_3d || !cdt_2.is_constrained(edge)); @@ -4018,18 +5557,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::set processed_faces; auto& region_index = face_region_number(face_index); for(const CDT_2_face_handle fh : cdt_2.finite_face_handles()) { - if(fh->info().is_outside_the_face) continue; + if(fh->info().is_outside_the_face == OUTSIDE) continue; if(false == fh->info().missing_subface) { continue; } - Cell_handle c; - int i, j, k; - if(tr().is_facet(fh->vertex(0)->info().vertex_handle_3d, - fh->vertex(1)->info().vertex_handle_3d, - fh->vertex(2)->info().vertex_handle_3d, c, i, j, k)) + const auto is_facet_opt = is_2d_face_also_a_3d_facet(fh); + if(is_facet_opt) { - const int facet_index = 6 - i - j - k; - set_facet_constrained({c, facet_index}, face_index, fh); + const auto [facet, orientation] = *is_facet_opt; + set_facet_constrained(facet, face_index, fh, orientation); fh->info().missing_subface = false; continue; } @@ -4050,11 +5586,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D for(auto [c, index]: cdt_2.constrained_edges()) { const auto va = c->vertex(cdt_2.cw(index)); const auto vb = c->vertex(cdt_2.ccw(index)); - const auto va_3d = va->info().vertex_handle_3d; - const auto vb_3d = vb->info().vertex_handle_3d; - std::cerr << cdt_3_format(" ({:.6} , {:.6})\n", - IO::oformat(va_3d, with_point_and_info), - IO::oformat(vb_3d, with_point_and_info)); + const auto va_3d = vertex_3d(va); + const auto vb_3d = vertex_3d(vb); + std::cerr << cdt_3_format(" ({:.6} , {:.6})\n", display_vert(va_3d), display_vert(vb_3d)); } } const auto encroach_edge_opt = @@ -4111,11 +5645,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto v = tr().vertices(it); std::cerr << "non-empty sphere at non-constrained facet (" << IO::oformat(Cell_handle(it)) << ", " << i << ") the cell is:\n " - << IO::oformat(v[0], with_point) << "\n " - << IO::oformat(v[1], with_point) << "\n " - << IO::oformat(v[2], with_point) << "\n " - << IO::oformat(v[3], with_point) << "\ncontains:\n " - << IO::oformat(n->vertex(n_index), with_point_and_info) << '\n'; + << with_point(v[0]) << "\n " + << with_point(v[1]) << "\n " + << with_point(v[2]) << "\n " + << with_point(v[3]) << "\ncontains:\n " + << display_vert(n->vertex(n_index)) << '\n'; using EK = CGAL::Exact_predicates_exact_constructions_kernel; const auto to_exact = CGAL::Cartesian_converter(); const auto from_exact = CGAL::Cartesian_converter(); @@ -4180,8 +5714,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D test = this->is_edge(v1, v2); result = result && test; if(!test && verbose) { - std::cerr << "edge (" << IO::oformat(v1, with_point_and_info) << ", " - << IO::oformat(v2, with_point_and_info) << ") is not an edge" << std::endl; + std::cerr << "edge (" << display_vert(v1) << ", " << display_vert(v2) << ") is not an edge" << std::endl; } } } @@ -4191,9 +5724,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D test = this->all_finite_edges[v1_index].find(v2)!= this->all_finite_edges[v1_index].end(); result = result && test; if(!test && verbose) { - std::cerr << "finite edge (" << IO::oformat(v1, with_point_and_info) << ", " - << IO::oformat(v2, with_point_and_info) << ") is not in the set all_finite_edges" - << std::endl; + std::cerr << "finite edge (" << display_vert(v1) << ", " << display_vert(v2) + << ") is not in the set all_finite_edges" << std::endl; } } } @@ -4205,8 +5737,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } bool recheck_for_missing_subfaces() { + CGAL::Real_timer timer; + if(this->debug().display_statistics()) { + timer.start(); + } bool nothing_has_changed = true; - for(CDT_3_signed_index i = 0, end = static_cast(face_constraint_misses_subfaces.size()); i < end; ++i) { + for(CDT_3_signed_index i = 0, end = signed_number_of_faces(); i < end; ++i) { if(this->face_data[i].skip_face == false) { nothing_has_changed = nothing_has_changed && @@ -4220,8 +5756,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void restore_constrained_Delaunay(Args&&... args) { this->is_Delaunay = false; - for(CDT_3_signed_index i = 0, end = static_cast (face_constraint_misses_subfaces.size()); i < end; - ++i) + for(CDT_3_signed_index i = 0, end = signed_number_of_faces(); i < end; ++i) { CDT_2& cdt_2 = non_const_face_cdt_2(i); try { @@ -4239,7 +5774,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } } if(this->debug().input_faces()) { - for(CDT_3_signed_index i = 0, end = static_cast (face_constraint_misses_subfaces.size()); i < end; ++i) { + for(CDT_3_signed_index i = 0, end = signed_number_of_faces(); i < end; ++i) { dump_face(i); } } @@ -4253,7 +5788,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // same face const CDT_2& cdt_2 = face_cdt_2(i); if(std::any_of(begin(cdt_2.finite_face_handles()), end(cdt_2.finite_face_handles()), - [](const auto& fh) { return !fh->info().is_outside_the_face && fh->info().missing_subface; })) + [](const auto& fh) { + return fh->info().is_outside_the_face == INSIDE && fh->info().missing_subface; + })) { face_constraint_misses_subfaces_set(i); } else { @@ -4269,7 +5806,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << "restore_face(" << i << ") incomplete, back to conforming...\n"; } Conforming_Dt::restore_Delaunay(insert_in_conflict_visitor); - // restart from the beginning, as the state of the triangulation has changed + // and then restart the loop at the beginning, to be sure that all faces are treated i = face_constraint_misses_subfaces_find_first(); } } @@ -4277,7 +5814,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D [](const auto& fd) { return fd.skip_face; })) { std::vector failed_faces; - for(CDT_3_signed_index i = 0, end = static_cast(face_data.size()); i < end; ++i) { + for(CDT_3_signed_index i = 0, end = signed_number_of_faces(); i < end; ++i) { if(face_data[i].skip_face) { failed_faces.push_back(i); } @@ -4300,30 +5837,39 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D } CGAL_assertion_msg( recheck_for_missing_subfaces() && - face_constraint_misses_subfaces_find_first() == face_constraint_misses_subfaces_npos, + are_there_any_face_constraint_misses_subfaces() == false, "All faces have been restored, but the triangulation is still not a CDT. This should not happen."); } + void add_bbox_points() { + const auto bbox = CGAL::bbox_3(this->points_begin(), this->points_end(), this->geom_traits()); + double d_x = bbox.xmax() - bbox.xmin(); + double d_y = bbox.ymax() - bbox.ymin(); + double d_z = bbox.zmax() - bbox.zmin(); + + const double d = (std::max)({d_x, d_y, d_z}); + + using Point = typename T_3::Point_3; + + std::vector bbox_points + = { Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmin() - d), + Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmin() - d), + Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmax() + d), + Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmax() + d), + Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmin() - d), + Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmin() - d), + Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmax() + d), + Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmax() + d)}; + for(const auto& p : bbox_points) + { + Vertex_handle v = this->insert(p); + v->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::BBOX); + } + } + void add_bbox_points_if_not_dimension_3() { if(this->dimension() != 3) { - const auto bbox = CGAL::bbox_3(this->points_begin(), this->points_end(), this->geom_traits()); - double d_x = bbox.xmax() - bbox.xmin(); - double d_y = bbox.ymax() - bbox.ymin(); - double d_z = bbox.zmax() - bbox.zmin(); - - const double d = (std::max)({d_x, d_y, d_z}); - - using Point = typename T_3::Point_3; - - this->insert(Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmin() - d)); - this->insert(Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmin() - d)); - this->insert(Point(bbox.xmin() - d, bbox.ymin() - d, bbox.zmax() + d)); - this->insert(Point(bbox.xmin() - d, bbox.ymax() + d, bbox.zmax() + d)); - this->insert(Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmin() - d)); - this->insert(Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmin() - d)); - this->insert(Point(bbox.xmax() + d, bbox.ymin() - d, bbox.zmax() + d)); - this->insert(Point(bbox.xmax() + d, bbox.ymax() + d, bbox.zmax() + d)); - + add_bbox_points(); CGAL_assertion(this->dimension() == 3); } } @@ -4331,7 +5877,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D static void write_region_to_OFF(std::ostream& out, const CDT_2& cdt_2) { out.precision(17); auto color_fn = [](CDT_2_face_handle fh_2d) -> CGAL::IO::Color { - if(fh_2d->info().is_outside_the_face) return CGAL::IO::gray(); + if(fh_2d->info().is_outside_the_face == OUTSIDE) return CGAL::IO::gray(); if(fh_2d->info().is_in_region) { if(fh_2d->info().is_in_region == 1) return CGAL::IO::violet(); else return CGAL::IO::red(); @@ -4395,14 +5941,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D { if constexpr (cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format("test_edge {} {} return {} {}\n", - IO::oformat(v0, with_point_and_info), - IO::oformat(v1, with_point_and_info), + with_point_and_info(v0), + with_point_and_info(v1), b, not_visited ? "(new)" : "(cached)"); if(not_visited && b) { - std::cerr << " triangle " << IO::oformat(triangle_vertices[0], with_point_and_info) << "\n" - << " " << IO::oformat(triangle_vertices[1], with_point_and_info) << "\n" - << " " << IO::oformat(triangle_vertices[2], with_point_and_info) << "\n"; + std::cerr << " triangle " << with_point_and_info(triangle_vertices[0]) << "\n" + << " " << with_point_and_info(triangle_vertices[1]) << "\n" + << " " << with_point_and_info(triangle_vertices[2]) << "\n"; } } CGAL_USE(this, v0, v1, b, not_visited); @@ -4422,6 +5968,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::swap(v0, v1); } if constexpr(cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { + auto indent_guard = CGAL::IO::make_indenting_guards("| ", std::cerr); debug_dump_edge_region_intersection(face_index, region_index, fh_region, region_vertices, border_edges, intersecting_edges.size(), v0, v1, {cell, index_v0, index_v1}, triangle_vertices); } @@ -4430,8 +5977,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::stringstream ss; ss.precision(std::cerr.precision()); ss << "Error: detected a self-intersection.\n" - " The constrained edge (" << IO::oformat(v0, with_point_and_info) - << ", " << IO::oformat(v1, with_point_and_info) + " The constrained edge (" << with_point_and_info(v0) << ", " << with_point_and_info(v1) << ") intersects the interior of the polygonal constraint #" << face_index << " (sub-region #" << region_index << ").\n" << " The input data is either self-intersecting or the input" @@ -4450,16 +5996,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; auto test_cell = [&](Cell_handle cell) { - std::optional indent_guards; if constexpr(cdt_3_can_use_cxx20_format()) if(this->debug().regions()) { std::cerr << cdt_3_format("test_cell #{}\n {}\n {}\n {}\n {}\n", cell->time_stamp(), - 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)); - indent_guards.emplace(CGAL::IO::make_indenting_guards("| ", std::cerr, std::cout, std::clog)); + with_point_and_info(cell->vertex(0)), + with_point_and_info(cell->vertex(1)), + with_point_and_info(cell->vertex(2)), + with_point_and_info(cell->vertex(3))); } + auto indent_guards = CGAL::IO::make_indenting_guards(this->debug().regions() ? "| " : "", std::cerr); bool does_intersect = false; for(int i = 0; i < 4; ++i) { const auto v0 = cell->vertex(i); @@ -4509,8 +6054,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D // Dump intersecting edges information for(auto edge : intersecting_edges) { auto [v1, v2] = tr().vertices(edge); - std::cerr << cdt_3_format(" edge: {} {}\n", IO::oformat(v1, with_point_and_info), - IO::oformat(v2, with_point_and_info)); + std::cerr << cdt_3_format(" edge: {} {}\n", with_point_and_info(v1), with_point_and_info(v2)); } // Dump upper and lower cavity facets @@ -4533,7 +6077,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::cerr << " facet: "; const auto facet_vertices = tr().vertices(facet); for(auto v: facet_vertices) { - std::cerr << IO::oformat(v, with_point_and_info) << " "; + std::cerr << with_point_and_info(v) << " "; } // This assertion is wrong, because there might be only one half-cavity and not a full cavity. // CGAL_assertion(!std::all_of(facet_vertices.begin(), facet_vertices.end(), @@ -4557,19 +6101,14 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D const auto to_exact = CGAL::Cartesian_converter(); const auto& cdt_2 = this->face_cdt_2(face_index); - auto of = [](auto&&... args) { - return IO::oformat(std::forward(args)..., with_point_and_info); - }; - - auto indent_guard = CGAL::IO::make_indenting_guards("| ", std::cerr, std::cout, std::clog); std::cerr << cdt_3_format("intersecting edge #{}: ( {} {} )\n" "intersected triangle vertices: {}\n" " {}\n" " {}\n", - edge_index, of(v_above), of(v_below), - of(first_intersected_triangle_vertices[0]), - of(first_intersected_triangle_vertices[1]), - of(first_intersected_triangle_vertices[2])); + edge_index, with_point_and_info(v_above), with_point_and_info(v_below), + with_point_and_info(first_intersected_triangle_vertices[0]), + with_point_and_info(first_intersected_triangle_vertices[1]), + with_point_and_info(first_intersected_triangle_vertices[2])); dump_region(face_index, region_index, cdt_2); const auto p_above = this->point(v_above); @@ -4583,9 +6122,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D std::ofstream intersect_out(ss_filename.str()); intersect_out.precision(17); for(auto fh: fh_region) { - auto v0 = fh->vertex(0)->info().vertex_handle_3d; - auto v1 = fh->vertex(1)->info().vertex_handle_3d; - auto v2 = fh->vertex(2)->info().vertex_handle_3d; + auto v0 = vertex_3d(fh->vertex(0)); + auto v1 = vertex_3d(fh->vertex(1)); + auto v2 = vertex_3d(fh->vertex(2)); auto triangle = typename Geom_traits::Triangle_3{tr().point(v0), tr().point(v1), tr().point(v2)}; auto exact_triangle = to_exact(triangle); if(auto edge_intersection_opt = CGAL::intersection(exact_edge_segment, exact_triangle)) { @@ -4604,7 +6143,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D continue; } std::cerr << cdt_3_format("Test tetrahedron (#{}):\n {}\n {}\n {}\n {}\n", ch->time_stamp(), - of(ch->vertex(0)), of(ch->vertex(1)), of(ch->vertex(2)), of(ch->vertex(3))); + with_point_and_info(ch->vertex(0)), + with_point_and_info(ch->vertex(1)), + with_point_and_info(ch->vertex(2)), + with_point_and_info(ch->vertex(3))); bool does_intersect_region = std::invoke([&]() { for(int i = 0; i < 4; ++i) { for(int j = i + 1; j < 4; ++j) { @@ -4624,7 +6166,11 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D exception_ostream() << cdt_3_format( "ERROR: The following tetrahedron (#{}) does not intersect the region:\n {}\n {}\n {}\n {}", - ch->time_stamp(), of(ch->vertex(0)), of(ch->vertex(1)), of(ch->vertex(2)), of(ch->vertex(3))) + ch->time_stamp(), + with_point_and_info(ch->vertex(0)), + with_point_and_info(ch->vertex(1)), + with_point_and_info(ch->vertex(2)), + with_point_and_info(ch->vertex(3))) << std::endl; } } @@ -4694,9 +6240,9 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D auto tetrahedron = tr().tetrahedron(ch); auto exact_tetrahedron = to_exact(tetrahedron); for(auto fh : fh_region) { - auto v0 = fh->vertex(0)->info().vertex_handle_3d; - auto v1 = fh->vertex(1)->info().vertex_handle_3d; - auto v2 = fh->vertex(2)->info().vertex_handle_3d; + auto v0 = vertex_3d(fh->vertex(0)); + auto v1 = vertex_3d(fh->vertex(1)); + auto v2 = vertex_3d(fh->vertex(2)); auto triangle = typename Geom_traits::Triangle_3{tr().point(v0), tr().point(v1), tr().point(v2)}; auto exact_triangle = to_exact(triangle); @@ -4742,8 +6288,8 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D write_3d_triangulation_to_OFF(dump, tr); } - void dump_triangulation() const { - std::ofstream dump("dump.binary.cgal", std::ios::binary); + void dump_triangulation(std::string filename = "dump.binary.cgal") const { + std::ofstream dump(filename, std::ios::binary); CGAL::IO::save_binary_file(dump, *this); } @@ -4754,6 +6300,12 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D write_3d_triangulation_to_OFF(dump, *this); } + void dump_constrained_facets_to_off(std::string filename) const { + std::ofstream dump(filename); + dump.precision(17); + write_facets(dump, *this, this->constrained_facets()); + } + void dump_face_polygons( const std::vector>& vec_of_handles, CDT_3_signed_index polygon_constraint_id, @@ -4779,9 +6331,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D write_region_to_OFF(dump_region, cdt_2); } - void dump_face(CDT_3_signed_index face_index) { + void dump_face(CDT_3_signed_index face_index, std::string filename = {}) const { const auto& cdt_2 = face_cdt_2(face_index); - std::ofstream dump_region(std::string("dump_face_") + std::to_string(face_index) + ".off"); + if(filename.empty()) filename = std::string("dump_face_") + std::to_string(face_index) + ".off"; + std::ofstream dump_region(filename); dump_region.precision(17); write_region_to_OFF(dump_region, cdt_2); } @@ -4869,17 +6422,15 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void write_2d_triangle(std::ostream &out, const CDT_2_face_handle fh) { - const auto v0 = fh->vertex(0)->info().vertex_handle_3d; - const auto v1 = fh->vertex(1)->info().vertex_handle_3d; - const auto v2 = fh->vertex(2)->info().vertex_handle_3d; + const auto v0 = vertex_3d(fh->vertex(0)); + const auto v1 = vertex_3d(fh->vertex(1)); + const auto v2 = vertex_3d(fh->vertex(2)); write_triangle(out, v0, v1, v2); } bool write_missing_subfaces_file(std::ostream& out) { - const auto npos = face_constraint_misses_subfaces_npos; - auto i = face_constraint_misses_subfaces_find_first(); - bool has_missing_subfaces = i != npos; - while(i != npos) { + const bool has_missing_subfaces = are_there_any_face_constraint_misses_subfaces(); + loop_on_face_constraint_misses_subfaces([&](auto i) { const CDT_2& cdt = face_cdt_2(i); for(const auto fh: cdt.finite_face_handles()) { if (false == fh->info().is_outside_the_face && @@ -4888,8 +6439,7 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D write_2d_triangle(out, fh); } } - i = face_constraint_misses_subfaces_find_next(i); - } + }); return has_missing_subfaces; } @@ -4919,6 +6469,10 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D }; protected: + CDT_3_signed_index signed_number_of_faces() const { + return static_cast(face_data.size()); + } + // Accessor functions for Face_data members CDT_2& non_const_face_cdt_2(std::size_t face_id) { return face_data[face_id].cdt_2; @@ -5008,7 +6562,22 @@ class Conforming_constrained_Delaunay_triangulation_3_impl : public Conforming_D void face_constraint_misses_subfaces_reset(std::size_t pos) { face_constraint_misses_subfaces[pos] = false; } -}; + + template + auto loop_on_face_constraint_misses_subfaces(F f) const { + auto i = face_constraint_misses_subfaces_find_first(); + bool has_any = i != face_constraint_misses_subfaces_npos; + while(i != face_constraint_misses_subfaces_npos) { + f(i); + i = face_constraint_misses_subfaces_find_next(i); + } + return has_any; + } + + auto are_there_any_face_constraint_misses_subfaces() const { + return face_constraint_misses_subfaces_find_first() != face_constraint_misses_subfaces_npos; + } +}; // end of Conforming_constrained_Delaunay_triangulation_3_impl #endif // DOXYGEN_RUNNING @@ -5066,7 +6635,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 { for(auto v : ccdt.triangulation().all_vertex_handles()) { switch(v->ccdt_3_data().vertex_type()) { - case CDT_3_vertex_type::CORNER: + case CDT_3_vertex_type::INPUT_VERTEX: v->set_dimension(0); v->set_index(0); break; @@ -5079,12 +6648,11 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 v->set_index(v->ccdt_3_data().face_index()); break; case CDT_3_vertex_type::FREE: + case CDT_3_vertex_type::BBOX: + case CDT_3_vertex_type::STEINER_IN_VOLUME: v->set_dimension(3); v->set_index(1); break; - default: - CGAL_error(); - break; } } @@ -5123,6 +6691,8 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 using TDS = typename CDT_3::Triangulation::Triangulation_data_structure; static_assert(std::is_same_v>); + static constexpr int INVALID_SUBDOMAIN_INDEX = -1; + if(tr.dimension() < 3) { for(auto ch : tr.all_cell_handles()) { ch->set_subdomain_index(0); @@ -5135,7 +6705,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 } } else { for(auto ch : tr.all_cell_handles()) { - ch->set_subdomain_index(-1); + ch->set_subdomain_index(INVALID_SUBDOMAIN_INDEX); } // Use a flood algorithm to mark constrained connected components. @@ -5149,7 +6719,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 // Function to flood-fill a connected component with a given subdomain index auto flood_component = [&border](typename Tr::Cell_handle start, int subdomain_index) { - if(start->subdomain_index() != -1) + if(start->subdomain_index() != INVALID_SUBDOMAIN_INDEX) return; std::stack queue; @@ -5158,12 +6728,12 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 while(!queue.empty()) { auto ch = queue.top(); queue.pop(); - if(ch->subdomain_index() == -1) { + if(ch->subdomain_index() == INVALID_SUBDOMAIN_INDEX) { ch->set_subdomain_index(subdomain_index); for(int i = 0; i < 4; i++) { typename Tr::Facet f(ch, i); auto n = ch->neighbor(i); - if(n->subdomain_index() == -1) { + if(n->subdomain_index() == INVALID_SUBDOMAIN_INDEX) { if(ch->ccdt_3_data().is_facet_constrained(i)) border.push(f); else @@ -5183,7 +6753,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 auto f = border.top(); border.pop(); auto n = f.first->neighbor(f.second); - if(n->subdomain_index() == -1) { + if(n->subdomain_index() == INVALID_SUBDOMAIN_INDEX) { // If we are coming from an even subdomain, use next odd, and vice versa bool from_even = (f.first->subdomain_index() % 2 == 0); int next_index = from_even ? next_odd_subdomain : next_even_subdomain; @@ -5197,7 +6767,7 @@ auto get_remeshing_triangulation(Conforming_constrained_Delaunay_triangulation_3 bool found_unvisited = false; for(auto ch : tr.finite_cell_handles()) { - if(ch->subdomain_index() == -1) { + if(ch->subdomain_index() == INVALID_SUBDOMAIN_INDEX) { flood_component(ch, next_even_subdomain); next_even_subdomain += 2; found_unvisited = true; diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h index f109feaed163..039912b48fc3 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h @@ -69,6 +69,20 @@ class Conforming_constrained_Delaunay_triangulation_cell_base_3 return ccdt_3_data_; } + ///////// for C3T3 : dummy implementation for compilation purposes + using Subdomain_index = int; + Subdomain_index subdomain_index() const { return 1; } // dummy value, not used in the implementation of CCDT + // todo : remove this function and the corresponding typedef, as they are not used in the implementation of CCDT, and + // are only here to satisfy the requirements of the concept `SimplicialMeshCellBase_3` + // for tetrahedral remeshing + // PLUS value 1 does not make any sense in the general case + void set_subdomain_index(Subdomain_index) {} + void reset_cache_validity() {} + bool is_cache_valid() const { return false;} + void set_sliver_value(double) {} + double sliver_value() const { return 0.; } + ///////// end of for C3T3 + static std::string io_signature() { static_assert( std::is_same_v< diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h index 91d9b73044ae..dc1eff2b045c 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -25,7 +26,7 @@ enum class CDT_3_cell_marker { IN_REGION = 1, VISITED = 1, ON_REGION_BOUNDARY = 2, - nb_of_markers + last = ON_REGION_BOUNDARY, }; /*! @@ -44,7 +45,7 @@ class Conforming_constrained_Delaunay_triangulation_cell_data_3 { std::array face_id = { -1, -1, -1, -1 }; std::array facet_2d = {nullptr, nullptr, nullptr, nullptr}; - std::bitset(CDT_3_cell_marker::nb_of_markers)> markers; + std::bitset(CDT_3_cell_marker::last) + 1> markers{}; bool is_marked() const { return markers.any(); } bool is_marked(CDT_3_cell_marker m) const { return markers.test(static_cast(m)); } diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h index 30b1c1c54f12..305c3e9456dc 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h @@ -55,6 +55,11 @@ class Conforming_constrained_Delaunay_triangulation_vertex_base_3 using Base = Triangulation_simplex_base_with_time_stamp; using Base::Base; + ///////// for C3T3 : dummy implementation for compilation purposes + using Index = int; + void set_dimension(int) {} + ///////// end of for C3T3 + // model of ConformingConstrainedDelaunayTriangulationVertexBase_3 Conforming_constrained_Delaunay_triangulation_vertex_data_3& ccdt_3_data() { return ccdt_3_data_; } const Conforming_constrained_Delaunay_triangulation_vertex_data_3& ccdt_3_data() const { return ccdt_3_data_; } diff --git a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h index e1e716be8c84..a1f728015354 100644 --- a/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h +++ b/Constrained_triangulation_3/include/CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h @@ -35,7 +35,15 @@ namespace CGAL { struct Conforming_constrained_Delaunay_triangulation_vertex_data_3 {}; #else // DOXYGEN_RUNNING -enum class CDT_3_vertex_type { FREE, CORNER, INPUT_VERTEX = CORNER, STEINER_ON_EDGE, STEINER_IN_FACE }; +enum class CDT_3_vertex_type : unsigned char { + FREE, + INPUT_VERTEX, + STEINER_ON_EDGE, + STEINER_IN_FACE, + STEINER_IN_VOLUME, + BBOX, + last = BBOX, +}; enum class CDT_3_vertex_marker { CLEAR = 0, @@ -44,14 +52,14 @@ enum class CDT_3_vertex_marker { CAVITY, CAVITY_ABOVE, CAVITY_BELOW, - nb_of_markers + last = CAVITY_BELOW }; class Conforming_constrained_Delaunay_triangulation_vertex_data_3 { protected: // TODO: check and improve the compactness of this class CDT_3_vertex_type m_vertex_type = CDT_3_vertex_type::FREE; - std::bitset(CDT_3_vertex_marker::nb_of_markers)> mark{}; + std::bitset(CDT_3_vertex_marker::last) + 1> mark{}; struct C_id { void* ptr = nullptr; std::size_t id = 0; @@ -143,6 +151,7 @@ class Conforming_constrained_Delaunay_triangulation_vertex_data_3 { void set_vertex_type(CDT_3_vertex_type type) { m_vertex_type = type; } bool is_Steiner_vertex_on_edge() const { return m_vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; } bool is_Steiner_vertex_in_face() const { return m_vertex_type == CDT_3_vertex_type::STEINER_IN_FACE; } + bool is_Steiner_vertex_in_volume() const { return m_vertex_type == CDT_3_vertex_type::STEINER_IN_VOLUME; } }; #endif // DOXYGEN_RUNNING diff --git a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/cdt_debug_io.h b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/cdt_debug_io.h index 752aaacfec32..e3d3ed247db1 100644 --- a/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/cdt_debug_io.h +++ b/Constrained_triangulation_3/include/CGAL/Constrained_triangulation_3/internal/cdt_debug_io.h @@ -35,9 +35,7 @@ namespace CGAL { std::size_t i = 0; for(const auto& [cell, facet_index] : facets_range) { - const auto v0 = cell->vertex(Tr::vertex_triple_index(facet_index, 0)); - const auto v1 = cell->vertex(Tr::vertex_triple_index(facet_index, 1)); - const auto v2 = cell->vertex(Tr::vertex_triple_index(facet_index, 2)); + const auto [v0, v1, v2] = tr.vertices({cell, facet_index}); points.push_back(tr.point(v0)); points.push_back(tr.point(v1)); points.push_back(tr.point(v2)); diff --git a/Constrained_triangulation_3/package_info/Constrained_triangulation_3/dependencies b/Constrained_triangulation_3/package_info/Constrained_triangulation_3/dependencies index 854e686ee58b..722c7f611507 100644 --- a/Constrained_triangulation_3/package_info/Constrained_triangulation_3/dependencies +++ b/Constrained_triangulation_3/package_info/Constrained_triangulation_3/dependencies @@ -1,3 +1,4 @@ +AABB_tree Algebraic_foundations Arithmetic_kernel BGL @@ -10,6 +11,7 @@ Constrained_triangulation_3 Distance_2 Distance_3 Filtered_kernel +Generator Hash_map Homogeneous_kernel Installation @@ -21,6 +23,7 @@ Kernel_d Mesh_3 Modular_arithmetic Number_types +PMP_Boolean_operations PMP_Mesh_repair PMP_Remeshing Polygon @@ -32,6 +35,7 @@ Random_numbers SMDS_3 STL_Extension Solver_interface +Spatial_searching Spatial_sorting Stream_support Surface_mesh diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt index 83e6440f3f41..52601668847d 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/CMakeLists.txt @@ -10,28 +10,37 @@ create_single_source_cgal_program(test_ccdt_remeshing.cpp) find_package(Eigen3 QUIET) include(CGAL_Eigen3_support) +set(CMAKE_CXX_SCAN_FOR_MODULES FALSE) + # CDT_3: C++20 for structured bindings -add_library(CDT_3_dependencies INTERFACE) -target_compile_features(CDT_3_dependencies INTERFACE cxx_std_20) -target_link_libraries(CDT_3_dependencies INTERFACE CGAL::CGAL CGAL::Data CGAL::Eigen3_support) +add_library(CGAL_CDT_3_dependencies INTERFACE) +target_compile_features(CGAL_CDT_3_dependencies INTERFACE cxx_std_20) +target_link_libraries(CGAL_CDT_3_dependencies INTERFACE CGAL::CGAL CGAL::Data CGAL::Eigen3_support) + +find_package(CLI11 QUIET) +add_library(CGAL_CDT_3_CLI11 INTERFACE) +if(TARGET CLI11::CLI11) + target_link_libraries(CGAL_CDT_3_CLI11 INTERFACE CLI11::CLI11) + target_compile_definitions(CGAL_CDT_3_CLI11 INTERFACE CGAL_USE_CLI11) +endif() create_single_source_cgal_program( "cdt_test_insert_constrained_edge_from_EDG_file.cpp") -target_link_libraries(cdt_test_insert_constrained_edge_from_EDG_file PRIVATE CDT_3_dependencies) +target_link_libraries(cdt_test_insert_constrained_edge_from_EDG_file PRIVATE CGAL_CDT_3_dependencies) create_single_source_cgal_program( "cdt_test_insert_constrained_edge_from_OFF_file.cpp") -target_link_libraries(cdt_test_insert_constrained_edge_from_OFF_file PRIVATE CDT_3_dependencies) +target_link_libraries(cdt_test_insert_constrained_edge_from_OFF_file PRIVATE CGAL_CDT_3_dependencies) create_single_source_cgal_program( "cdt_3_from_off.cpp") -target_link_libraries(cdt_3_from_off PRIVATE CDT_3_dependencies) +target_link_libraries(cdt_3_from_off PRIVATE CGAL_CDT_3_dependencies CGAL_CDT_3_CLI11) create_single_source_cgal_program( "cdt_3_from_off_with_Epeck.cpp") -target_link_libraries(cdt_3_from_off_with_Epeck PRIVATE CDT_3_dependencies) +target_link_libraries(cdt_3_from_off_with_Epeck PRIVATE CGAL_CDT_3_dependencies CGAL_CDT_3_CLI11) create_single_source_cgal_program( "snap_and_cdt3.cpp") add_executable(test_CDT_3_insert_constrained_edge_from_EDG_file cdt_test_insert_constrained_edge_from_EDG_file.cpp) -target_link_libraries(test_CDT_3_insert_constrained_edge_from_EDG_file PRIVATE CDT_3_dependencies) +target_link_libraries(test_CDT_3_insert_constrained_edge_from_EDG_file PRIVATE CGAL_CDT_3_dependencies) target_compile_definitions(test_CDT_3_insert_constrained_edge_from_EDG_file PUBLIC CGAL_TEST_CDT_3_USE_CDT) cgal_add_test(test_CDT_3_insert_constrained_edge_from_EDG_file) add_executable(test_CDT_3_insert_constrained_edge_from_OFF_file cdt_test_insert_constrained_edge_from_OFF_file.cpp) -target_link_libraries(test_CDT_3_insert_constrained_edge_from_OFF_file PRIVATE CDT_3_dependencies) +target_link_libraries(test_CDT_3_insert_constrained_edge_from_OFF_file PRIVATE CGAL_CDT_3_dependencies) target_compile_definitions(test_CDT_3_insert_constrained_edge_from_OFF_file PUBLIC CGAL_TEST_CDT_3_USE_CDT) cgal_add_test(test_CDT_3_insert_constrained_edge_from_OFF_file) @@ -93,6 +102,9 @@ function(CGAL_add_cdt3_from_local_off_test data_name) CGAL_add_cdt3_from_off_test_aux(${data_name} ${CMAKE_CURRENT_SOURCE_DIR}/data ${ARGN}) endfunction() +CGAL_add_cdt3_from_local_off_test(bug-fixed --move-Steiner-vertices-to-the-volume) +CGAL_add_cdt3_from_local_off_test(bug3-fixed --move-Steiner-vertices-to-the-volume) +CGAL_add_cdt3_from_local_off_test(bug4-fixed --move-Steiner-vertices-to-the-volume) CGAL_add_cdt3_from_local_off_test(cheese18) CGAL_add_cdt3_from_local_off_test(cheese23) CGAL_add_cdt3_from_local_off_test(cheese28) diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp index 9c75167546d3..59eb8ad03b27 100644 --- a/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/cdt_3_from_off.cpp @@ -1,5 +1,7 @@ #include +#define CGAL_T3_ALLOW_NEGATIVE_VOLUME 1 +#define CGAL_TETRAHEDRAL_REMESHING_VERBOSE 1 // #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1 #include #include @@ -13,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +23,7 @@ #include #include #include +#include #include #include @@ -32,6 +36,7 @@ #include #include +#include #include #include @@ -39,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -48,8 +54,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -57,21 +63,19 @@ # include #endif -#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L -# include +#if CGAL_USE_CLI11 +# include #endif -#if CGAL_CDT_3_USE_EPECK - -#include - -using K = CGAL::Exact_predicates_exact_constructions_kernel; - -#else // use Epick +#ifndef CGAL_CDT_3_USE_EPECK +# define CGAL_CDT_3_USE_EPECK 0 +#endif -using K = CGAL::Exact_predicates_inexact_constructions_kernel; +namespace CGAL::CDT_3::test::cdt_3_from_off { -#endif // use Epick +using K = std::conditional_t; using CDT = CGAL::Conforming_constrained_Delaunay_triangulation_3; using Point = K::Point_3; @@ -82,68 +86,21 @@ using vertex_descriptor = boost::graph_traits::vertex_descriptor; using edge_descriptor = boost::graph_traits::edge_descriptor; using face_descriptor = boost::graph_traits::face_descriptor; -void help(std::ostream& out) { - out << R"!!!!!( -Usage: cdt_3_from_off [options] input.off output.off - - input.off: input mesh - output.off: output mesh - - --merge-facets/--no-merge-facets: merge facets into patches (set by default) - --merge-facets-old: merge facets using the old method - --bisect: bisect failures - --vertex-vertex-epsilon : epsilon for vertex-vertex min distance (default: 1e-6) - --segment-vertex-epsilon : epsilon for segment-vertex min distance (default: 0) - --coplanar-polygon-max-angle : max angle for coplanar polygons (default: 1) - --coplanar-polygon-max-distance : max distance for coplanar polygons (default: 1e-6) - - --dump-patches-after-merge : dump patches after merging facets in PLY - --dump-surface-mesh-after-merge : dump surface mesh after merging facets in OFF - --dump-patches-borders-prefix : dump patches borders - --dump-after-conforming : dump mesh after conforming in OFF - - --no-repair: do not repair the mesh - --read-mesh-with-operator: read the mesh with operator>> - --reject-self-intersections: reject self-intersecting polygon soups - --no-is-valid: do not call is_valid checks - - --debug-Steiner-points: debug Steiner point insertion - --debug-Steiner-points-construction: debug Steiner point construction - --debug-input-faces: debug input faces - --debug-missing-regions: debug missing regions - --debug-regions: debug regions - --debug_copy_triangulation_into_hole: debug copy_triangulation_into_hole - --debug-validity: add is_valid checks after modifications to the TDS - --debug-finite-edges-map: debug the use of a hash map for finite edges - --debug-subconstraints-to-conform: debug subconstraints to conform - --debug-verbose-special-cases: debug verbose output for special cases - --debug-encroaching-vertices: debug encroaching vertices computation - --debug-conforming-validation: debug edge conforming validation - --debug-constraint-hierarchy: debug constraint hierarchy operations - --debug-geometric-errors: debug geometric error handling - --debug-polygon-insertion: debug polygon insertion process - --debug-restore-faces: debug face restoration process - - --use-finite-edges-map: use a hash map for finite edges (default: false) - --use-epeck-for-normals/--no-use-epeck-for-normals: use exact kernel for normal computations (default: false) - --use-epeck-for-Steiner-points/--no-use-epeck-for-Steiner-points: use exact kernel for Steiner point computations (default: false) - - --verbose/-V: verbose (can be used several times) - --quiet: do not print anything - --help/-h: print this help -)!!!!!"; -} +template +auto milliseconds(std::chrono::duration duration) { + return std::chrono::duration_cast(duration).count(); +}; -[[noreturn]] void error(std::string_view message, std::string_view extra = "") { - std::cerr << "Error: " << message << extra << '\n'; - help(std::cerr); - std::exit(EXIT_FAILURE); -} +template +auto milliseconds_since(std::chrono::time_point start_time) { + return milliseconds(Clock::now() - start_time); +}; + +using clock = std::chrono::high_resolution_clock; struct CDT_options { int verbose_level = 0; - bool need_help = false; bool quiet = false; bool merge_facets = true; bool merge_facets_old_method = false; @@ -152,6 +109,7 @@ struct CDT_options bool read_mesh_with_operator = false; bool debug_Steiner_points = false; bool debug_Steiner_points_construction = false; + unsigned debug_move_Steiner_vertices = 0; bool debug_input_faces = false; bool debug_missing_regions = false; bool debug_regions = false; @@ -167,6 +125,8 @@ struct CDT_options bool debug_polygon_insertion = false; bool debug_restore_faces = false; bool use_finite_edges_map = false; + bool move_Steiner_vertices_to_the_volume = true; + bool allow_moving_Steiner_vertices_to_create_negative_tets = false; bool use_epeck_for_normals = false; bool use_epeck_for_Steiner_points = false; bool call_is_valid = true; @@ -186,116 +146,121 @@ struct CDT_options }; CDT_options::CDT_options(int argc, char* argv[]) { - const std::vector args(argv + 1, argv + argc); - int positional = 0; - - using namespace std::literals::string_view_literals; - for (auto it = args.begin(); it != args.end(); ++it) { - auto get_next_arg_or_error_out = [&it, &args]() -> std::string { - if(it + 1 == args.end()) { - error("extra argument required after "sv, *it); - } - return std::string(*++it); - }; - std::string_view arg = *it; - if(arg == "--merge-facets"sv) { - merge_facets = true; - } else if(arg == "--no-merge-facets"sv) { - merge_facets = false; - } else if(arg == "--reject-self-intersections"sv) { - reject_self_intersections = true; - } else if(arg == "--no-repair"sv) { - repair_mesh = false; - } else if(arg == "--read-mesh-with-operator"sv) { - read_mesh_with_operator = true; - } else if(arg == "--merge-facets-old"sv) { - merge_facets = true; - merge_facets_old_method = true; - } else if(arg == "--bisect"sv) { - bisect_failures = true; - } else if(arg == "--dump-patches-after-merge"sv) { - dump_patches_after_merge_filename = get_next_arg_or_error_out(); - } else if(arg == "--dump-patches-borders-prefix"sv) { - dump_patches_borders_prefix = get_next_arg_or_error_out(); - } else if(arg == "--dump-surface-mesh-after-merge"sv) { - dump_surface_mesh_after_merge_filename = get_next_arg_or_error_out(); - } else if(arg == "--dump-after-conforming"sv) { - dump_after_conforming_filename = get_next_arg_or_error_out(); - } else if(arg == "--vertex-vertex-epsilon"sv) { - vertex_vertex_epsilon = std::stod(get_next_arg_or_error_out()); - } else if(arg == "--segment-vertex-epsilon"sv) { - segment_vertex_epsilon = std::stod(get_next_arg_or_error_out()); - } else if(arg == "--coplanar-polygon-max-angle"sv) { - coplanar_polygon_max_angle = std::stod(get_next_arg_or_error_out()); - } else if(arg == "--coplanar-polygon-max-distance"sv) { - coplanar_polygon_max_distance = std::stod(get_next_arg_or_error_out()); - } else if(arg == "--quiet"sv) { - quiet = true; - } else if(arg == "--no-is-valid"sv) { - call_is_valid = false; - } else if(arg == "--debug-Steiner-points"sv) { - debug_Steiner_points = true; - } else if(arg == "--debug-Steiner-points-construction"sv) { - debug_Steiner_points_construction = true; - } else if(arg == "--debug-input-faces"sv) { - debug_input_faces = true; - } else if(arg == "--debug-missing-regions"sv) { - debug_missing_regions = true; - } else if(arg == "--debug-regions"sv) { - debug_regions = true; - } else if(arg == "--debug_copy_triangulation_into_hole"sv) { - debug_copy_triangulation_into_hole = true; - } else if(arg == "--debug-validity"sv) { - debug_validity = true; - } else if(arg == "--debug-finite-edges-map"sv) { - debug_finite_edges_map = true; - } else if(arg == "--debug-subconstraints-to-conform"sv) { - debug_subconstraints_to_conform = true; - } else if(arg == "--debug-verbose-special-cases"sv) { - debug_verbose_special_cases = true; - } else if(arg == "--debug-encroaching-vertices"sv) { - debug_encroaching_vertices = true; - } else if(arg == "--debug-conforming-validation"sv) { - debug_conforming_validation = true; - } else if(arg == "--debug-constraint-hierarchy"sv) { - debug_constraint_hierarchy = true; - } else if(arg == "--debug-geometric-errors"sv) { - debug_geometric_errors = true; - } else if(arg == "--debug-polygon-insertion"sv) { - debug_polygon_insertion = true; - } else if(arg == "--debug-restore-faces"sv) { - debug_restore_faces = true; - } else if(arg == "--use-finite-edges-map"sv) { - use_finite_edges_map = true; - } else if(arg == "--no-use-epeck-for-normals"sv) { - use_epeck_for_normals = false; - } else if(arg == "--no-use-epeck-for-Steiner-points"sv) { - use_epeck_for_Steiner_points = false; - } else if(arg == "--use-epeck-for-normals"sv) { - use_epeck_for_normals = true; - } else if(arg == "--use-epeck-for-Steiner-points"sv) { - use_epeck_for_Steiner_points = true; - } else if(arg == "--verbose"sv || arg == "-V"sv) { - ++verbose_level; - } else if(arg == "--help"sv || arg == "-h"sv) { - need_help = true; - } else if(arg[0] == '-') { - error("unknown option "sv, arg); - } else { - switch(positional) { - case 0: - input_filename = arg; - ++positional; - break; - case 1: - output_filename = arg; - ++positional; - break; - default: - error("too many arguments"sv); - } - } +#if CGAL_USE_CLI11 + CLI::App app{"Build and process a constrained Delaunay triangulation from an OFF mesh"}; + app.set_help_flag("-h,--help", "print this help"); + app.allow_extras(false); + + app.add_flag("-V,--verbose", verbose_level, + "verbose (can be used several times)") + ->multi_option_policy(CLI::MultiOptionPolicy::Sum); + + app.add_flag("--quiet", quiet, "do not print anything"); + + app.add_flag("--merge-facets,!--no-merge-facets", merge_facets, + "merge facets into patches"); + app.add_flag("--merge-facets-old", merge_facets_old_method, + "merge facets using the old method"); + app.add_flag("--bisect", bisect_failures, "bisect failures"); + app.add_flag("--reject-self-intersections", reject_self_intersections, + "reject self-intersecting polygon soups"); + app.add_flag("--read-mesh-with-operator", read_mesh_with_operator, + "read the mesh with operator>>"); + app.add_flag("--repair,!--no-repair", repair_mesh, + "repair the mesh while reading polygon soup"); + app.add_flag("--call-is-valid,!--no-is-valid", call_is_valid, + "call is_valid checks"); + + app.add_option("--vertex-vertex-epsilon", vertex_vertex_epsilon, + "epsilon for vertex-vertex min distance"); + app.add_option("--segment-vertex-epsilon", segment_vertex_epsilon, + "epsilon for segment-vertex min distance"); + app.add_option("--coplanar-polygon-max-angle", coplanar_polygon_max_angle, + "max angle for coplanar polygons"); + app.add_option("--coplanar-polygon-max-distance", coplanar_polygon_max_distance, + "max distance for coplanar polygons"); + + app.add_option("--dump-patches-after-merge", dump_patches_after_merge_filename, + "dump patches after merging facets in PLY"); + app.add_option("--dump-surface-mesh-after-merge", dump_surface_mesh_after_merge_filename, + "dump surface mesh after merging facets in OFF"); + app.add_option("--dump-patches-borders-prefix", dump_patches_borders_prefix, + "dump patches borders"); + app.add_option("--dump-after-conforming", dump_after_conforming_filename, + "dump mesh after conforming in OFF"); + + app.add_flag("--debug-Steiner-points", debug_Steiner_points, + "debug Steiner point insertion"); + app.add_flag("--debug-Steiner-points-construction", debug_Steiner_points_construction, + "debug Steiner point construction"); + auto debug_move_Steiner_vertices_option = + app.add_option("--debug-move-Steiner-vertices-to-the-volume", debug_move_Steiner_vertices, + "debug moving Steiner vertices to the volume (level 0..3)") + ->check(CLI::Range(0, 3)) + ->expected(0, 1); + app.add_flag("--debug-input-faces", debug_input_faces, "debug input faces"); + app.add_flag("--debug-missing-regions", debug_missing_regions, "debug missing regions"); + app.add_flag("--debug-regions", debug_regions, "debug regions"); + app.add_flag("--debug_copy_triangulation_into_hole", debug_copy_triangulation_into_hole, + "debug copy_triangulation_into_hole"); + app.add_flag("--debug-validity", debug_validity, + "add is_valid checks after modifications to the TDS"); + app.add_flag("--debug-finite-edges-map", debug_finite_edges_map, + "debug the use of a hash map for finite edges"); + app.add_flag("--debug-subconstraints-to-conform", debug_subconstraints_to_conform, + "debug subconstraints to conform"); + app.add_flag("--debug-verbose-special-cases", debug_verbose_special_cases, + "debug verbose output for special cases"); + app.add_flag("--debug-encroaching-vertices", debug_encroaching_vertices, + "debug encroaching vertices computation"); + app.add_flag("--debug-conforming-validation", debug_conforming_validation, + "debug edge conforming validation"); + app.add_flag("--debug-constraint-hierarchy", debug_constraint_hierarchy, + "debug constraint hierarchy operations"); + app.add_flag("--debug-geometric-errors", debug_geometric_errors, + "debug geometric error handling"); + app.add_flag("--debug-polygon-insertion", debug_polygon_insertion, + "debug polygon insertion process"); + app.add_flag("--debug-restore-faces", debug_restore_faces, + "debug face restoration process"); + + app.add_flag("--use-finite-edges-map", use_finite_edges_map, + "use a hash map for finite edges"); + app.add_flag("--move-Steiner-vertices-to-the-volume,!--no-move-Steiner-vertices-to-the-volume", + move_Steiner_vertices_to_the_volume, + "move Steiner vertices to the volume"); + app.add_flag("--allow-moving-Steiner-vertices-to-create-negative-tets", + allow_moving_Steiner_vertices_to_create_negative_tets, + "allow moving Steiner vertices to create negative volume tetrahedra"); + app.add_flag("--use-epeck-for-normals,!--no-use-epeck-for-normals", use_epeck_for_normals, + "use exact kernel for normal computations"); + app.add_flag("--use-epeck-for-Steiner-points,!--no-use-epeck-for-Steiner-points", + use_epeck_for_Steiner_points, + "use exact kernel for Steiner point computations"); + + app.add_option("input.off", input_filename, "input mesh"); + app.add_option("output.off", output_filename, "output mesh"); + + for(auto* option : app.get_options()) { + option->always_capture_default(true); + } + + app.get_formatter()->enable_default_flag_values(true); + + try { + app.parse(argc, argv); + } catch(const CLI::ParseError& e) { + std::exit(app.exit(e)); + } + + if(*debug_move_Steiner_vertices_option && debug_move_Steiner_vertices == 0) { + debug_move_Steiner_vertices = 1; } + + if(merge_facets_old_method) { + merge_facets = true; + } +#endif // CGAL_USE_CLI11 } CGAL::CDT_3::Debug_options cdt_debug_options(const CDT_options& options) { @@ -311,6 +276,8 @@ CGAL::CDT_3::Debug_options cdt_debug_options(const CDT_options& options) { cdt_debug.verbose_special_cases(options.debug_verbose_special_cases); cdt_debug.encroaching_vertices(options.debug_encroaching_vertices); cdt_debug.conforming_validation(options.debug_conforming_validation); + cdt_debug.move_Steiner_vertices(options.debug_move_Steiner_vertices); + cdt_debug.move_Steiner_vertices_allow_negative_tets(options.allow_moving_Steiner_vertices_to_create_negative_tets); cdt_debug.constraint_hierarchy(options.debug_constraint_hierarchy); cdt_debug.geometric_errors(options.debug_geometric_errors); cdt_debug.polygon_insertion(options.debug_polygon_insertion); @@ -348,8 +315,14 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& return [&cdt, &options]() { auto _ = CGAL::CDT_3_OUTPUT_TASK_guard(); { - auto dump_tets_to_medit = [](std::string fname, + if(std::uncaught_exceptions() > 0) { + std::cerr << "create_output_finalizer: an exception is being thrown, the triangulation may be left in an " + "invalid state.\n"; + return; + } + auto dump_to_medit = [](std::string fname, const std::vector &points, + const std::vector> &indexed_triangles, const std::vector> &indexed_tetra, const std::vector &cell_ids) { @@ -359,19 +332,23 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& out << points.size() << "\n"; for (const K::Point_3& p : points) out << p << " 0\n"; - out << "Triangles\n0\nTetrahedra\n"; + out << "Triangles\n" << indexed_triangles.size() << "\n"; + for (const auto& tri : indexed_triangles) + out << tri[0]+1 << " " << tri[1]+1 << " " << tri[2]+1 << " " << tri[3] << "\n"; + out << "Tetrahedra\n"; out << indexed_tetra.size() << "\n"; for (std::size_t k=0;k cells_map; + std::unordered_map cells_map; for(auto ch : tr.all_cell_handles()) { cells_map[ch] = 1; @@ -396,39 +373,62 @@ std::function create_output_finalizer(const CDT& cdt, const CDT_options& } } + const bool export_all_the_bbox = + std::all_of(cells_map.begin(), cells_map.end(), [](const auto& kv) { return kv.second == 0; }); + if (export_all_the_bbox) { + for(auto ch : tr.finite_cell_handles()) + { + cells_map[ch] = 1; + } + } + std::vector points(cdt.number_of_vertices()); + std::size_t idx = 0; for(auto v: cdt.finite_vertex_handles()) { - points.at(v->time_stamp() -1) = v->point(); + // renumber the vertices: there might be holes in the numbering due to removed vertices + v->set_time_stamp(++idx); + points.at(idx - 1) = v->point(); + } + std::vector> indexed_triangles; + std::size_t max_polygon_id = 0; + for(auto f : cdt.constrained_facets()) { + const auto verts = tr.vertices(f); + const auto polygon_id = static_cast(cdt.face_constraint_index(f)); + indexed_triangles.push_back({verts[0]->time_stamp() - 1, + verts[1]->time_stamp() - 1, + verts[2]->time_stamp() - 1, + polygon_id}); + if (polygon_id > max_polygon_id) { + max_polygon_id = polygon_id; + } + } + if(export_all_the_bbox) { + auto inf_v = tr.infinite_vertex(); + tr.incident_cells(inf_v, boost::make_function_output_iterator([&](Tr::Cell_handle ch) { + auto facet_index = ch->index(inf_v); + const auto verts = tr.vertices(Tr::Facet{ch, facet_index}); + indexed_triangles.push_back({verts[0]->time_stamp() - 1, + verts[1]->time_stamp() - 1, + verts[2]->time_stamp() - 1, + max_polygon_id + 1 /*a new polygon id for the bbox*/}); + })); } std::vector> indexed_tetra; indexed_tetra.reserve(cdt.number_of_cells()); for(auto ch: cdt.finite_cell_handles()) { if(cells_map[ch] > 0) { - indexed_tetra.push_back({ch->vertex(0)->time_stamp() -1, - ch->vertex(1)->time_stamp() -1, - ch->vertex(2)->time_stamp() -1, - ch->vertex(3)->time_stamp() -1}); + indexed_tetra.push_back({ch->vertex(0)->time_stamp() - 1, + ch->vertex(1)->time_stamp() - 1, + ch->vertex(2)->time_stamp() - 1, + ch->vertex(3)->time_stamp() - 1}); } } - std::vector cell_idsl(indexed_tetra.size(), 1); - dump_tets_to_medit(options.output_filename + ".mesh", points, indexed_tetra, cell_idsl); + std::vector cell_ids(indexed_tetra.size(), 1); + dump_to_medit(options.output_filename + ".mesh", points, indexed_triangles, indexed_tetra, cell_ids); + cdt.dump_constrained_facets_to_off(options.output_filename); } { - std::ofstream dump(options.output_filename); - dump.precision(17); -#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L - 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); }; - 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 + cdt.save_binary_file(options.output_filename + ".binary.cgal"); } }; } @@ -581,7 +581,7 @@ Borders_of_patches maybe_merge_facets( { auto _ = CGAL::CDT_3_MERGE_FACETS_TASK_guard(); - auto start_time = std::chrono::high_resolution_clock::now(); + auto start_time = clock::now(); if(options.merge_facets_old_method) { number_of_patches = merge_facets_old_method(mesh, pmaps, number_of_patches); @@ -592,9 +592,8 @@ Borders_of_patches maybe_merge_facets( } patch_edges = extract_patch_edges(mesh, pmaps, number_of_patches); if (!options.quiet) { - auto timing = std::chrono::high_resolution_clock::now() - start_time; std::cout << "[timings] found " << number_of_patches << " patches in " - << std::chrono::duration_cast(timing).count() << " ms\n"; + << milliseconds_since(start_time) << " ms\n"; } } @@ -701,16 +700,15 @@ int go(Mesh mesh, CDT_options options) { // Build CDT directly from the polygon mesh using the official API CDT cdt; auto output_on_exit_scope_guard = CGAL::make_scope_exit(create_output_finalizer(cdt, options)); - auto build_start = std::chrono::high_resolution_clock::now(); + auto build_start = clock::now(); cdt = CDT{std::invoke([&]() { auto np = CGAL::parameters::debug(cdt_debug).visitor(std::ref(dump_mesh_with_steiner_points)); return options.merge_facets ? CDT(mesh, np.plc_face_id(pmaps.patch_id_map)) : CDT(mesh, np); })}; if(!options.quiet) { - auto timing = std::chrono::high_resolution_clock::now() - build_start; std::cout << "[timings] built CDT from mesh in " - << std::chrono::duration_cast(timing).count() << " ms\n"; - std::cout << "Number of vertices: " << cdt.number_of_vertices() << "\n\n"; + << milliseconds_since(build_start) << " ms\n"; + std::cout << cdt.statistics() << "\n"; } // Validation: check conforming status and validity @@ -720,6 +718,24 @@ int go(Mesh mesh, CDT_options options) { CGAL_assertion(!options.call_is_valid || cdt.is_conforming()); } + // Move Steiner vertices to the volume if requested + if(options.move_Steiner_vertices_to_the_volume) { + auto move_vertices_guard = CGAL::CDT_3_MOVE_STEINER_VERTICES_TASK_guard(); + cdt.move_Steiner_vertices_to_the_volume(); + if(!options.quiet) { + std::cout << "[timings] moved Steiner vertices to the volume in " + << move_vertices_guard.time_ms() << " ms\n"; + std::cout << cdt.statistics() << "\n"; + } + CGAL::tetrahedral_isotropic_remeshing( + cdt, + bbox_max_span, + CGAL::parameters::number_of_iterations(30) + .remesh_boundaries(false)); + // CGAL_assertion(cdt.tr().tds().is_valid(true)); + std::cout << cdt.statistics() << "\n"; + } + return EXIT_SUCCESS; } @@ -778,19 +794,18 @@ int bisect_errors(Mesh mesh, CDT_options options) { return CGAL::bisect_failures(mesh, get_size, simplify, run_test, save_mesh); } +} // end of namespaces CGAL::CDT_3::test::cdt_3_from_off + int main(int argc, char* argv[]) { + using namespace CGAL::CDT_3::test::cdt_3_from_off; std::cerr.precision(17); std::cout.precision(17); CGAL::get_default_random() = CGAL::Random(42); CDT_options options(argc, argv); - if(options.need_help) { - help(std::cout); - return 0; - } CGAL::CDT_3_read_polygon_mesh_output read_mesh_result; - auto start_time = std::chrono::high_resolution_clock::now(); + auto start_time = clock::now(); { auto _ = CGAL::CDT_3_READ_INPUT_TASK_guard(); @@ -817,8 +832,9 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } if(!options.quiet) { - std::cout << "[timings] read mesh in " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; + std::cout << "[timings] read mesh \"" << options.input_filename << "\" in " + << milliseconds_since(start_time) + << " ms\n"; std::cout << "Number of vertices: " << read_mesh_result.polygon_mesh->number_of_vertices() << '\n'; std::cout << "Number of edges: " << read_mesh_result.polygon_mesh->number_of_edges() << '\n'; std::cout << "Number of faces: " << read_mesh_result.polygon_mesh->number_of_faces() << "\n\n"; @@ -849,8 +865,7 @@ int main(int argc, char* argv[]) { auto exit_code = go(std::move(*read_mesh_result.polygon_mesh), options); if(!options.quiet) { - std::cout << "[timings] total time: " << std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n"; + std::cout << "[timings] total time: " << milliseconds_since(start_time) << " ms\n"; if(exit_code != 0) std::cout << "ERROR with exit code " << exit_code << '\n'; } return exit_code; diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-fixed.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-fixed.off new file mode 100644 index 000000000000..12ce52dd9a52 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug-fixed.off @@ -0,0 +1,32 @@ +OFF +23 5 0 + +10 10 10 +-6.0240600000000004 -10.012 4.0160400000000003 +6.0144299999999999 -2.0024000000000002 4.00962 +-6.0072000000000001 -2.0024000000000002 4.00962 +-6.0240900000000002 -10.0321 -10.0321 +-6.0192600000000001 -6.0192600000000001 10.0321 +-6.0240900000000002 -6.0240900000000002 -10.0321 +-6.0192600000000001 -6.0192600000000001 -6.0192600000000001 +-6.0240900000000002 -6.0192699999999997 -10.0321 +10.019399999999999 -2.00387 0.33785900000000002 +10.0219 -0.92307300000000003 -0.92307300000000003 +-6.0240499999999999 -9.9999400000000005 10.0321 +6.0130600000000003 -0.86255199999999999 4.0087099999999998 +6.0144299999999999 -2.0018699999999998 5.3379099999999999 +-6.0130499999999998 -0.85634999999999994 4.0087000000000002 +-6.01823 -5.1587800000000001 4.0121500000000001 +-6.0072000000000001 -5.1587699999999996 4.0121500000000001 +-6.0192600000000001 -6.0192600000000001 4.0128399999999997 +-6.0116300000000003 -2.0038800000000001 0.33143699999999998 +10.0093 -2.0018699999999998 5.3355100000000002 +10 6 10 +10.013999999999999 0 2.9999899999999999 +-6.0192600000000001 -6.0192600000000001 5.0160499999999999 +7 1 17 22 15 14 5 11 +4 4 6 8 7 +6 15 16 3 2 12 14 +6 18 9 19 13 2 3 +6 20 21 19 9 10 0 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug3-fixed.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug3-fixed.off new file mode 100644 index 000000000000..cd8ee5797e79 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug3-fixed.off @@ -0,0 +1,16 @@ +OFF +8 4 0 + +0.41289999999999999 0.15625 -0.067400000000000002 +0.44080000000000003 0.14374999999999999 -0.065199999999999994 +0.40029999999999999 0.17394999999999999 -0.070499999999999993 +0.42680000000000001 0.16245000000000001 -0.068500000000000005 +0.40970000000000001 0.18395 -0.072300000000000003 +0.43359999999999999 0.17535000000000001 -0.070800000000000002 +0.43759999999999999 0.19075 -0.073499999999999996 +0.46029999999999999 0.18435000000000001 -0.072400000000000006 +3 0 3 1 +3 2 4 3 +3 3 4 5 +3 5 6 7 + diff --git a/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug4-fixed.off b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug4-fixed.off new file mode 100644 index 000000000000..44dbc9146ab1 --- /dev/null +++ b/Constrained_triangulation_3/test/Constrained_triangulation_3/data/bug4-fixed.off @@ -0,0 +1,27 @@ +OFF +16 7 0 + +185.80154797792815 6.314845734395881 -138.37941828626168 +185.87791334370158 3.8848603944873195 -138.37275344014969 +185.8220846343909 -5.6613587297849071 -136.31085013313518 +185.8410656525223 5.0573729395164362 -138.67501807936665 +185.80721796422316 6.1344238450376913 -137.33082227481412 +185.83031684350982 5.3994055881827281 -137.3485146794398 +185.8187674038665 5.7669147166102093 -137.33966847712696 +185.83252986996206 -5.3289859446530263 -137.33266393727405 +185.84186628315314 5.0318964597552469 -137.35736088175264 +185.83609156333148 5.2156510239689879 -137.35293778059622 +185.86496516243977 4.2968782029002828 -137.37505328637826 +185.80160282426402 6.3131004956894499 -137.32714866699001 +185.81299268404484 5.9506692808239503 -137.33524537597054 +185.82558201217887 -5.5500703640873645 -137.33569278130571 +185.82454212368816 5.5831601523964682 -137.34409157828338 +185.85919044261811 4.4806327671140238 -137.37063018522184 +3 0 11 4 +3 0 12 6 +3 2 13 7 +3 14 5 3 +3 3 9 8 +3 10 1 3 +3 3 15 10 + diff --git a/Lab/demo/Lab/Plugins/CDT_3/CDT_3_plugin.cpp b/Lab/demo/Lab/Plugins/CDT_3/CDT_3_plugin.cpp index ab33ceb8e31b..993aaca51ad4 100644 --- a/Lab/demo/Lab/Plugins/CDT_3/CDT_3_plugin.cpp +++ b/Lab/demo/Lab/Plugins/CDT_3/CDT_3_plugin.cpp @@ -37,7 +37,7 @@ struct Vertex_converter Vertex v; v.set_point(Wp{src.point()}); switch(src.ccdt_3_data().vertex_type()) { - case CDT_3_vertex_type::CORNER: + case CDT_3_vertex_type::INPUT_VERTEX: v.set_dimension(0); v.set_index(0); break; diff --git a/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h b/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h index 373de05e0644..4dfa3683bd0b 100644 --- a/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h +++ b/SMDS_3/include/CGAL/Mesh_complex_3_in_triangulation_3.h @@ -215,15 +215,15 @@ class Mesh_complex_3_in_triangulation_3 /*! Index type. */ - typedef typename Tr::Vertex::Index Index; + typedef typename Tr::Triangulation_data_structure::Vertex::Index Index; /*! Surface index type. */ - typedef typename Tr::Cell::Surface_patch_index Surface_patch_index; + typedef typename Tr::Triangulation_data_structure::Cell::Surface_patch_index Surface_patch_index; /*! Subdomain index type. */ - typedef typename Tr::Cell::Subdomain_index Subdomain_index; + typedef typename Tr::Triangulation_data_structure::Cell::Subdomain_index Subdomain_index; /*! Corner index type. */ diff --git a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h index c545f9ef70f8..fae3af940d36 100644 --- a/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h +++ b/SMDS_3/include/CGAL/SMDS_3/tet_soup_to_c3t3.h @@ -125,14 +125,15 @@ bool build_finite_cells(Tr& tr, { typedef typename Tr::Vertex_handle Vertex_handle; typedef typename Tr::Cell_handle Cell_handle; - typedef typename Tr::Cell::Surface_patch_index Surface_patch_index; bool success = true; +#ifndef CGAL_T3_ALLOW_NEGATIVE_VOLUME CGAL_assertion_code( typename Tr::Geom_traits::Construct_point_3 cp = tr.geom_traits().construct_point_3_object(); typename Tr::Geom_traits::Orientation_3 orientation = tr.geom_traits().orientation_3_object(); ) +#endif typename SubdomainsRange::value_type max_domain = 0; if(replace_domain_0) @@ -159,9 +160,11 @@ bool build_finite_cells(Tr& tr, vs[j]->set_dimension(3); } +#ifndef CGAL_T3_ALLOW_NEGATIVE_VOLUME // this assertion also tests for degeneracy CGAL_assertion(orientation(cp(tr.point(vs[0])), cp(tr.point(vs[1])), cp(tr.point(vs[2])), cp(tr.point(vs[3]))) == POSITIVE); +#endif Cell_handle c = tr.tds().create_cell(vs[0], vs[1], vs[2], vs[3]); c->set_subdomain_index(subdomains[i]); // the cell's info keeps the reference of the tetrahedron @@ -197,21 +200,21 @@ bool build_finite_cells(Tr& tr, } while(facet[0] != n0); - typename FacetPatchMap::const_iterator it = border_facets.find(facet); - if(it != border_facets.end()) - { - c->set_surface_patch_index(j, it->second); - } - else - { - std::swap(facet[1], facet[2]); // facet[0] is still the smallest, no need to rotate again - - it = border_facets.find(facet); - if(it != border_facets.end()) - c->set_surface_patch_index(j, it->second); - else - c->set_surface_patch_index(j, Surface_patch_index()); - } +// typename FacetPatchMap::const_iterator it = border_facets.find(facet); +// if(it != border_facets.end()) +// { +// c->set_surface_patch_index(j, it->second); +// } +// else +// { +// std::swap(facet[1], facet[2]); // facet[0] is still the smallest, no need to rotate again +// +// it = border_facets.find(facet); +// if(it != border_facets.end()) +// c->set_surface_patch_index(j, it->second); +// else +// c->set_surface_patch_index(j, Surface_patch_index()); +// } } } } @@ -291,7 +294,7 @@ bool build_infinite_cells(Tr& tr, it->second.emplace_back(opp_c, 0); CGAL_assertion(it->second.size() == 2); - opp_c->set_surface_patch_index(0, c->surface_patch_index(i)); +// opp_c->set_surface_patch_index(0, c->surface_patch_index(i)); } #ifdef CGAL_TET_SOUP_TO_C3T3_DEBUG diff --git a/SMDS_3/include/CGAL/Simplicial_mesh_vertex_base_3.h b/SMDS_3/include/CGAL/Simplicial_mesh_vertex_base_3.h index 9238c4d9b710..0e7a1a481b7f 100644 --- a/SMDS_3/include/CGAL/Simplicial_mesh_vertex_base_3.h +++ b/SMDS_3/include/CGAL/Simplicial_mesh_vertex_base_3.h @@ -39,6 +39,8 @@ #include #include #include +#include +#include namespace CGAL { @@ -113,7 +115,10 @@ private : using Curve_index = CurveIndex; using Corner_index = CornerIndex; - using Index = Variant_with_no_duplicate_t; + using Index_variant = Variant_with_no_duplicate_t; + using Index = std::conditional_t == 1, + std::variant_alternative_t<0, Index_variant>, + Index_variant>; using FT = typename Gt::FT; diff --git a/STL_Extension/include/CGAL/Compact_container.h b/STL_Extension/include/CGAL/Compact_container.h index 1beb7796c328..ec04664d7ffb 100644 --- a/STL_Extension/include/CGAL/Compact_container.h +++ b/STL_Extension/include/CGAL/Compact_container.h @@ -1220,17 +1220,18 @@ namespace handle { } // namespace internal -template -class Output_rep > { +template +class Output_rep, Format_tag> { protected: using CC_iterator = CGAL::internal::CC_iterator; using Compact_container = typename CC_iterator::CC; using Time_stamper = typename Compact_container::Time_stamper; CC_iterator it; + CGAL_NO_UNIQUE_ADDRESS Format_tag format; public: - Output_rep( const CC_iterator it) : it(it) {} + Output_rep(const CC_iterator& it, Format_tag format = {}) : it(it), format(std::move(format)) {} std::ostream& operator()( std::ostream& out) const { - return (out << Time_stamper::display_id(it.operator->())); + return (out << IO::oformat(Time_stamper::display_id(it.operator->()), format)); } }; @@ -1257,7 +1258,7 @@ struct Output_rep, With_offset_tag> using Base = Output_rep; using Base::Base; - Output_rep(const CC_iterator it, With_offset_tag tag = {}) + Output_rep(const CC_iterator& it, With_offset_tag tag = {}) : Base(it), offset(tag.offset) {} std::ostream& operator()(std::ostream& out) const { @@ -1276,7 +1277,7 @@ struct Output_rep, With_point_tag> using Base::Base; - Output_rep(const CC_iterator it, With_point_tag tag = {}) + Output_rep(const CC_iterator& it, With_point_tag tag = {}) : Base(it, tag) {} std::ostream& operator()(std::ostream& out) const { diff --git a/Stream_support/include/CGAL/IO/Indenting_ostream.h b/Stream_support/include/CGAL/IO/Indenting_ostream.h index bff5f78a9858..d7aee11780a8 100644 --- a/Stream_support/include/CGAL/IO/Indenting_ostream.h +++ b/Stream_support/include/CGAL/IO/Indenting_ostream.h @@ -22,6 +22,9 @@ #include #include +#include +#include +#include #include #include #include @@ -234,6 +237,10 @@ class Basic_indenting_stream_guard , original_buf_(stream.rdbuf()) , indenting_buf_(*original_buf_, indent_string) { + if(indent_string.empty()) { + // No indentation, just use the original buffer + return; + } if(auto old_buf = dynamic_cast(original_buf_)) { original_indent_string_ = old_buf->indent_string(); old_buf->set_indent_string(original_indent_string_ + indent_string); @@ -300,11 +307,13 @@ using Indenting_wstream_guard = Basic_indenting_stream_guard; * This helper function creates `Basic_indenting_stream_guard` objects for * multiple streams at once. All streams will use the same indentation settings, * and their original streambufs will be automatically restored when the guards - * go out of scope. The guards are returned in a tuple. + * go out of scope. The guards are returned in a tuple. If no stream is + * provided, guards are created for `std::cout`, `std::cerr`, and `std::clog`. * * \tparam Streams The stream types (deduced from arguments) * \param spaces_per_level Number of spaces per indentation level - * \param streams The streams to apply indentation to + * \param streams The streams to apply indentation to. If omitted, the helper + * creates guards for `std::cout`, `std::cerr`, and `std::clog`. * \return A tuple of Basic_indenting_stream_guard objects * * \sa Basic_indenting_stream_guard @@ -316,10 +325,15 @@ using Indenting_wstream_guard = Basic_indenting_stream_guard; * std::cerr << "Indented error\n"; * \endcode */ + /// @{ template auto make_indenting_guards(int spaces_per_level, Streams&... streams) { return std::make_tuple(Basic_indenting_stream_guard(streams, spaces_per_level)...); } +inline auto make_indenting_guards(int spaces_per_level) { + return make_indenting_guards(spaces_per_level, std::cout, std::cerr, std::clog); +} +/// @} /** * \ingroup PkgStreamSupportRef @@ -327,19 +341,26 @@ auto make_indenting_guards(int spaces_per_level, Streams&... streams) { * \brief creates indenting guards for multiple streams with a custom indent string. * * This overload allows specifying a custom indentation string instead of a number - * of spaces. + * of spaces. If no stream is provided, guards are created for `std::cout`, + * `std::cerr`, and `std::clog`. * * \tparam Streams The stream types (deduced from arguments) * \param indent_string The indentation string to use - * \param streams The streams to apply indentation to + * \param streams The streams to apply indentation to. If omitted, the helper + * creates guards for `std::cout`, `std::cerr`, and `std::clog`. * \return A tuple of Basic_indenting_stream_guard objects * * \sa Basic_indenting_stream_guard */ + /// @{ template auto make_indenting_guards(const std::string& indent_string, Streams&... streams) { return std::make_tuple(Basic_indenting_stream_guard(streams, indent_string)...); } +inline auto make_indenting_guards(const std::string& indent_string) { + return make_indenting_guards(indent_string, std::cout, std::cerr, std::clog); +} +/// @} } // namespace IO } // namespace CGAL diff --git a/Stream_support/include/CGAL/IO/io.h b/Stream_support/include/CGAL/IO/io.h index 86041bfaec45..ac490e047221 100644 --- a/Stream_support/include/CGAL/IO/io.h +++ b/Stream_support/include/CGAL/IO/io.h @@ -18,6 +18,8 @@ #ifndef CGAL_IO_H #define CGAL_IO_H +#include + #include #include @@ -27,12 +29,15 @@ #include #include -#include #include +#include #include #include #include #include +#include +#include +#include #include namespace CGAL { @@ -82,6 +87,160 @@ is in `ASCII` mode. */ enum Mode {ASCII = 0, PRETTY, BINARY}; +/*! +\ingroup PkgStreamSupportRef + +returns the printing mode of the %IO stream `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode get_mode(std::ios& s) +{ + return static_cast(s.iword(Static::get_mode())); +} + +/*! +\ingroup PkgStreamSupportRef + +sets the mode of the %IO stream `s` to be the `ASCII` mode. +Returns the previous mode of `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode set_ascii_mode(std::ios& s) +{ + Mode m = get_mode(s); + s.iword(Static::get_mode()) = ASCII; + return m; +} + +/*! +\ingroup PkgStreamSupportRef + +sets the mode of the %IO stream `s` to be the `BINARY` mode. +Returns the previous mode of `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode set_binary_mode(std::ios& s) +{ + Mode m = get_mode(s); + s.iword(Static::get_mode()) = BINARY; + return m; +} + +/*! +\ingroup PkgStreamSupportRef + +sets the mode of the %IO stream `s` to be the `PRETTY` mode. +Returns the previous mode of `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode set_pretty_mode(std::ios& s) +{ + Mode m = get_mode(s); + s.iword(Static::get_mode()) = PRETTY; + return m; +} + +/*! +\ingroup PkgStreamSupportRef + +sets the printing mode of the %IO stream `s`. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline Mode set_mode(std::ios& s, Mode m) +{ + Mode old = get_mode(s); + s.iword(Static::get_mode()) = m; + return old; +} + +/*! +\ingroup PkgStreamSupportRef + +checks if the %IO stream `s` is in `PRETTY` mode. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_binary()` +*/ +inline bool is_pretty(std::ios& s) { return s.iword(Static::get_mode()) == PRETTY; } + +/*! +\ingroup PkgStreamSupportRef + +checks if the %IO stream `s` is in `ASCII` mode. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_binary()` +\sa `CGAL::IO::is_pretty()` +*/ +inline bool is_ascii(std::ios& s) { return s.iword(Static::get_mode()) == ASCII; } + +/*! +\ingroup PkgStreamSupportRef + +checks if the %IO stream `s` is in `BINARY` mode. + +\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink +\sa `CGAL::IO::set_mode()` +\sa `CGAL::IO::set_ascii_mode()` +\sa `CGAL::IO::set_binary_mode()` +\sa `CGAL::IO::set_pretty_mode()` +\sa `CGAL::IO::get_mode()` +\sa `CGAL::IO::is_ascii()` +\sa `CGAL::IO::is_pretty()` +*/ +inline bool is_binary(std::ios& s) { return s.iword(Static::get_mode()) == BINARY; } + } // namespace IO #ifdef DOXYGEN_RUNNING @@ -175,7 +334,7 @@ verify that a given type can be used with `oformat()`. Its value has to be for a type, the class template `Output_rep` defines `is_specialized` to the default value `false`. */ -template +template class Output_rep : public IO_rep_is_not_specialized { @@ -188,17 +347,46 @@ class Output_rep std::ostream& operator()( std::ostream& os) const { return (os << t); } }; +namespace IO { +/*! +\ingroup PkgStreamSupportRef + +Convenience function to construct an output representation (`Output_rep`) for type `T`. + +Generic IO for type `T`. +*/ +template +Output_rep oformat(const T& t) { return Output_rep(t); } + +/*! +\ingroup PkgStreamSupportRef + +Convenience function to construct an output representation (`Output_rep`) for type `T`. + +Generic IO for type `T` with formatting tag. +*/ +template +Output_rep oformat( const T& t, F format) { + if constexpr (std::is_constructible_v, const T&, F&&>) + return Output_rep(t, std::move(format)); + else + return Output_rep(t); +} + +} // namespace IO + template class Output_rep, F> { const std::optional& t; + CGAL_NO_UNIQUE_ADDRESS F format; public: - Output_rep( const std::optional& tt) : t(tt) {} + Output_rep( const std::optional& tt, F format = {}) : t(tt), format(std::move(format)) {} std::ostream& operator()( std::ostream& os) const { if (t==std::nullopt) return (os << "--"); - return (os << t.value()); + return (os << IO::oformat(t.value(), format)); } }; @@ -206,14 +394,45 @@ template class Output_rep, F> { const std::variant& t; + CGAL_NO_UNIQUE_ADDRESS F format; public: - Output_rep( const std::variant& tt) : t(tt) {} + Output_rep( const std::variant& tt, F format = {}) : t(tt), format(std::move(format)) {} std::ostream& operator()( std::ostream& os) const { - std::visit([&os](auto&& v) { os << v; }, t); + std::visit([&os, this](auto&& v) { os << IO::oformat(v, format); }, t); + return os; + } +}; + +template +class Output_rep::type>> + : public IO_rep_is_specialized +{ + const T& value; + CGAL_NO_UNIQUE_ADDRESS F format; + +public: + Output_rep( const T& value, F format = {}) : value(value), format(std::move(format)) {} + + std::ostream& operator()( std::ostream& os) const { + const auto mode = IO::get_mode(os); + const bool is_pretty = (mode != IO::BINARY && std::is_same_v) || mode == IO::PRETTY; + const char* sep = (mode == IO::BINARY) ? "" : (is_pretty ? ", " : " "); + if(is_pretty) os << "("; + print_tuple_impl(os, sep, std::make_index_sequence::value>{}); + if(is_pretty) os << ")"; + return os; + } + +protected: + template + std::ostream& print_tuple_impl(std::ostream& os, const char* sep, std::index_sequence) const + { + ((os << (I == 0 ? "" : sep) << IO::oformat(std::get(value), format)), ...); return os; } + }; template @@ -241,35 +460,6 @@ class Output_rep template std::ostream& operator<<( std::ostream& os, Output_rep rep) { return rep(os); } -namespace IO { - -/*! -\ingroup PkgStreamSupportRef - -Convenience function to construct an output representation (`Output_rep`) for type `T`. - -Generic IO for type `T`. -*/ -template -Output_rep oformat(const T& t) { return Output_rep(t); } - -/*! -\ingroup PkgStreamSupportRef - -Convenience function to construct an output representation (`Output_rep`) for type `T`. - -Generic IO for type `T` with formatting tag. -*/ -template -Output_rep oformat( const T& t, F format) { - if constexpr (std::is_constructible_v, const T&, F>) - return Output_rep(t, format); - else - return Output_rep(t); -} - -} // namespace IO - /*! \ingroup PkgStreamSupportRef @@ -289,6 +479,29 @@ class Input_rep std::istream& operator()( std::istream& is) const { return (is >> t); } }; +/*! \relates Input_rep + \brief stream input to the \c Input_rep calls its \c operator(). + +\brief \cgal defines input operators for classes that are derived +from the class `std::istream`. This allows to read from input streams +as `std::cin`, as well as from `std::istringstream` and `std::ifstream`. +The input operator is defined for all classes in the \cgal `Kernel`. +*/ +template +std::istream& operator>>( std::istream& is, Input_rep rep) { return rep(is); } + +namespace IO { + +/*! +\ingroup PkgStreamSupportRef + +The definition of this function is completely symmetric to `oformat()`. +*/ +template +Input_rep iformat( T& t) { return Input_rep(t); } + +} // namespace IO + template class Input_rep> { @@ -301,7 +514,7 @@ class Input_rep> //! perform the input, calls \c operator\>\> by default. std::istream& operator()( std::istream& is) const { T v; - if(is >> v) t = v; + if(is >> IO::iformat(v)) t = v; return is; } }; @@ -451,29 +664,6 @@ class Input_rep }; #endif -/*! \relates Input_rep - \brief stream input to the \c Input_rep calls its \c operator(). - -\brief \cgal defines input operators for classes that are derived -from the class `std::istream`. This allows to read from input streams -as `std::cin`, as well as from `std::istringstream` and `std::ifstream`. -The input operator is defined for all classes in the \cgal `Kernel`. -*/ -template -std::istream& operator>>( std::istream& is, Input_rep rep) { return rep(is); } - -namespace IO { - -/*! -\ingroup PkgStreamSupportRef - -The definition of this function is completely symmetric to `oformat()`. -*/ -template -Input_rep iformat( T& t) { return Input_rep(t); } - -} // namespace IO - template class Benchmark_rep { @@ -500,160 +690,6 @@ Benchmark_rep bmformat( const T& t) { return Benchmark_rep(t); } template Benchmark_rep bmformat( const T& t, F) { return Benchmark_rep(t); } -/*! -\ingroup PkgStreamSupportRef - -returns the printing mode of the %IO stream `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode get_mode(std::ios& s) -{ - return static_cast(s.iword(Static::get_mode())); -} - -/*! -\ingroup PkgStreamSupportRef - -sets the mode of the %IO stream `s` to be the `ASCII` mode. -Returns the previous mode of `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode set_ascii_mode(std::ios& s) -{ - Mode m = get_mode(s); - s.iword(Static::get_mode()) = ASCII; - return m; -} - -/*! -\ingroup PkgStreamSupportRef - -sets the mode of the %IO stream `s` to be the `BINARY` mode. -Returns the previous mode of `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode set_binary_mode(std::ios& s) -{ - Mode m = get_mode(s); - s.iword(Static::get_mode()) = BINARY; - return m; -} - -/*! -\ingroup PkgStreamSupportRef - -sets the mode of the %IO stream `s` to be the `PRETTY` mode. -Returns the previous mode of `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode set_pretty_mode(std::ios& s) -{ - Mode m = get_mode(s); - s.iword(Static::get_mode()) = PRETTY; - return m; -} - -/*! -\ingroup PkgStreamSupportRef - -sets the printing mode of the %IO stream `s`. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline Mode set_mode(std::ios& s, Mode m) -{ - Mode old = get_mode(s); - s.iword(Static::get_mode()) = m; - return old; -} - -/*! -\ingroup PkgStreamSupportRef - -checks if the %IO stream `s` is in `PRETTY` mode. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_binary()` -*/ -inline bool is_pretty(std::ios& s) { return s.iword(Static::get_mode()) == PRETTY; } - -/*! -\ingroup PkgStreamSupportRef - -checks if the %IO stream `s` is in `ASCII` mode. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_binary()` -\sa `CGAL::IO::is_pretty()` -*/ -inline bool is_ascii(std::ios& s) { return s.iword(Static::get_mode()) == ASCII; } - -/*! -\ingroup PkgStreamSupportRef - -checks if the %IO stream `s` is in `BINARY` mode. - -\link PkgStreamSupportEnumRef `CGAL::IO::Mode`\endlink -\sa `CGAL::IO::set_mode()` -\sa `CGAL::IO::set_ascii_mode()` -\sa `CGAL::IO::set_binary_mode()` -\sa `CGAL::IO::set_pretty_mode()` -\sa `CGAL::IO::get_mode()` -\sa `CGAL::IO::is_ascii()` -\sa `CGAL::IO::is_pretty()` -*/ -inline bool is_binary(std::ios& s) { return s.iword(Static::get_mode()) == BINARY; } - } // namespace IO template < class T > @@ -735,10 +771,10 @@ inline std::istream &operator>>(std::istream &is, Color& col) { case ASCII : is >> ir >> ig >> ib >> ia; - r = (unsigned char)ir; - g = (unsigned char)ig; - b = (unsigned char)ib; - a = (unsigned char)ia; + r = static_cast(ir); + g = static_cast(ig); + b = static_cast(ib); + a = static_cast(ia); break; case BINARY : read(is, r); diff --git a/Stream_support/include/CGAL/IO/io_tags.h b/Stream_support/include/CGAL/IO/io_tags.h index 2be296695313..3c794a9aa9cf 100644 --- a/Stream_support/include/CGAL/IO/io_tags.h +++ b/Stream_support/include/CGAL/IO/io_tags.h @@ -18,7 +18,6 @@ #ifndef CGAL_IO_TAGS_H #define CGAL_IO_TAGS_H -#include #include namespace CGAL { @@ -52,6 +51,7 @@ template<> struct Io_traits { typedef io_Read_write Io_tag; }; template<> struct Io_traits { typedef io_Read_write Io_tag; }; struct IO_manip_tag{}; +struct Pretty_tag{}; } //namespace CGAL diff --git a/TDS_3/include/CGAL/Triangulation_utils_3.h b/TDS_3/include/CGAL/Triangulation_utils_3.h index 58299cf071aa..f9032e5eaf4d 100644 --- a/TDS_3/include/CGAL/Triangulation_utils_3.h +++ b/TDS_3/include/CGAL/Triangulation_utils_3.h @@ -54,10 +54,6 @@ const int Triangulation_utils_base_3::ccw_map[3] = {1, 2, 0}; template < class T > const int Triangulation_utils_base_3::cw_map[3] = {2, 0, 1}; -// We derive from Triangulation_cw_ccw_2 because we still use cw() and ccw() -// in the 2D part of the code. Ideally, this should go away when we reuse -// T2D entirely. - struct Triangulation_utils_3 : public Triangulation_utils_base_3<> { diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h index 0b5c2f895332..5d198942b597 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/collapse_short_edges.h @@ -714,8 +714,8 @@ bool are_edge_lengths_valid(const typename C3t3::Edge& edge, } else if (collapse_type == TO_MIDPOINT) { - new_index = max_dimension_index(v0, v1); - new_dim = (std::max)(v0->in_dimension(), v1->in_dimension()); + new_index = max_dimension_index(v0, v1, c3t3); + new_dim = (std::max)(c3t3.in_dimension(v0), c3t3.in_dimension(v1)); #ifdef CGAL_AVERAGE_SIZING_AFTER_COLLAPSE sizing_at_new_pos = sizing_at_midpoint(edge, new_dim, new_index, sizing, c3t3, cell_selector); #else @@ -783,12 +783,12 @@ void merge_surface_patch_indices(const typename C3t3::Facet& f1, if (in_cx_f1 && !in_cx_f2) { typename C3t3::Surface_patch_index patch = c3t3.surface_patch_index(f1); - f2.first->set_surface_patch_index(f2.second, patch); + c3t3.set_surface_patch_index(f2, patch); } else if (in_cx_f2 && !in_cx_f1) { typename C3t3::Surface_patch_index patch = c3t3.surface_patch_index(f2); - f1.first->set_surface_patch_index(f1.second, patch); + c3t3.set_surface_patch_index(f1, patch); } else if(in_cx_f1 && in_cx_f2) { @@ -796,7 +796,7 @@ void merge_surface_patch_indices(const typename C3t3::Facet& f1, typename C3t3::Surface_patch_index patch = c3t3.surface_patch_index(f2); c3t3.remove_from_complex(f2); - f2.first->set_surface_patch_index(f2.second, patch); + c3t3.set_surface_patch_index(f2, patch); } } diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h index ed22929e6086..90dd50302b10 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/flip_edges.h @@ -68,17 +68,17 @@ void update_c3t3_facets(C3t3& c3t3, //on mirror facet const typename C3t3::Surface_patch_index patch = c3t3.surface_patch_index(mf); if (c3t3.is_in_complex(mf)) - f.first->set_surface_patch_index(f.second, patch); + c3t3.set_surface_patch_index(f, patch); else - f.first->set_surface_patch_index(f.second, Surface_patch_index()); + c3t3.set_surface_patch_index(f, Surface_patch_index()); } else { //we are inside the modified zone, c3t3 info is not valid anymore if (c3t3.is_in_complex(f) || c3t3.is_in_complex(mf)) { - f.first->set_surface_patch_index(f.second, Surface_patch_index()); - mf.first->set_surface_patch_index(mf.second, Surface_patch_index()); + c3t3.set_surface_patch_index(f, Surface_patch_index()); + c3t3.set_surface_patch_index(mf, Surface_patch_index()); } } } @@ -1114,15 +1114,15 @@ Sliver_removal_result find_best_flip(typename C3t3::Edge& edge, { vertices_around_edge.insert(vi); - if ( circ->first->subdomain_index() - != circ->first->neighbor(circ->second)->subdomain_index()) + if ( c3t3.subdomain_index(circ->first) + != c3t3.subdomain_index(circ->first->neighbor(circ->second))) { boundary_edge = true; boundary_vertices.insert(vi); } if ( tr.is_infinite(circ->first) - != tr.is_infinite(circ->first->neighbor(circ->second))) + != tr.is_infinite(circ->first->neighbor(circ->second))) { hull_edge = true; //hull_vertices.insert(vi); @@ -1225,7 +1225,7 @@ void collect_subdomains_on_boundary(const C3t3& c3t3, { for (auto v : c3t3.triangulation().vertices(c)) { - const int dim = v->in_dimension(); + const int dim = c3t3.in_dimension(v); if(dim >= 0 && dim < 3) vertices_subdomain_indices[v].insert(c->subdomain_index()); } @@ -1534,8 +1534,8 @@ Sliver_removal_result flip_on_surface(C3T3& c3t3, ch2->set_vertex(ch2->index(vh2), vh3); ch1->set_vertex(ch1->index(vh0), vh1); - c3t3.add_to_complex(ch0, ch0->index(vh4), patch); - c3t3.add_to_complex(ch3, ch3->index(vh4), patch); + c3t3.add_to_complex({ch0, ch0->index(vh4)}, patch); + c3t3.add_to_complex({ch3, ch3->index(vh4)}, patch); for (Cell_handle chi : cells_around_edge) { @@ -1578,8 +1578,8 @@ Sliver_removal_result flip_on_surface(C3T3& c3t3, ci->vertex(j)->set_cell(ci); } - c3t3.add_to_complex(ch0, ch0->index(vh4), patch); - c3t3.add_to_complex(ch3, ch3->index(vh4), patch); + c3t3.add_to_complex({ch0, ch0->index(vh4)}, patch); + c3t3.add_to_complex({ch3, ch3->index(vh4)}, patch); for (Cell_handle chi : cells_around_edge) { @@ -1658,8 +1658,8 @@ Sliver_removal_result flip_on_surface(C3T3& c3t3, ch0->set_vertex(ch0->index(vh5), vh1); ch1->set_vertex(ch1->index(vh4), vh3); - c3t3.add_to_complex(ch0, ch0->index(vh2), patch); - c3t3.add_to_complex(ch1, ch1->index(vh2), patch); + c3t3.add_to_complex({ch0, ch0->index(vh2)}, patch); + c3t3.add_to_complex({ch1, ch1->index(vh2)}, patch); for (Cell_handle chi : cells_around_edge) { @@ -1708,8 +1708,8 @@ Sliver_removal_result flip_on_surface(C3T3& c3t3, ce->vertex(j)->set_cell(ce); } - c3t3.add_to_complex(ch0, ch0->index(vh2), patch); - c3t3.add_to_complex(ch1, ch1->index(vh2), patch); + c3t3.add_to_complex({ch0, ch0->index(vh2)}, patch); + c3t3.add_to_complex({ch1, ch1->index(vh2)}, patch); for (Cell_handle chi : cells_around_edge) { @@ -1866,12 +1866,12 @@ std::size_t flipBoundaryEdges( CGAL_expensive_assertion_code(bool b =) tr.tds().is_facet(vh2, vh3, vh0, c, li, lj, lk); CGAL_expensive_assertion(b); - c3t3.add_to_complex(c, (6 - li - lj - lk), surfi); + c3t3.add_to_complex({c, (6 - li - lj - lk)}, surfi); CGAL_expensive_assertion_code(b = ) tr.tds().is_facet(vh2, vh3, vh1, c, li, lj, lk); CGAL_expensive_assertion(b); - c3t3.add_to_complex(c, (6 - li - lj - lk), surfi); + c3t3.add_to_complex({c, (6 - li - lj - lk)}, surfi); CGAL_expensive_assertion_code(std::size_t nbf_post = std::distance(c3t3.facets_in_complex_begin(), diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h index 00d0e16f0a1e..792ef5495d06 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/peel_slivers.h @@ -96,9 +96,9 @@ std::size_t peel_slivers(C3T3& c3t3, for (int i = 0; i < 4; ++i) { if (f_on_surface[i]) - c3t3.remove_from_complex(c, i); + c3t3.remove_from_complex({c, i}); else - c3t3.add_to_complex(c, i, patch.value()); + c3t3.add_to_complex({c, i}, patch.value()); } c3t3.remove_from_complex(c); diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h index cf73ab88891c..ea83703503cb 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/smooth_vertices.h @@ -137,7 +137,7 @@ class Tetrahedral_remeshing_smoother { CGAL_assertion(!m_flip_smooth_steps); reset_vertex_id_map(c3t3.triangulation()); - reset_free_vertices(c3t3.triangulation()); + reset_free_vertices(c3t3); // once this variable is set to true, // m_vertex_id becomes constant and @@ -169,8 +169,9 @@ class Tetrahedral_remeshing_smoother // this function can be used iff m_vertex_id // has already been initialized - void reset_free_vertices(const Tr& tr) + void reset_free_vertices(const C3t3& c3t3) { + const auto& tr = c3t3.triangulation(); // when flip-smooth steps start, // m_free_vertices should already be initialized // by the last smoothing step. @@ -192,7 +193,7 @@ class Tetrahedral_remeshing_smoother for (auto vi : tr.vertices(c)) { const std::size_t idi = vertex_id(vi); - const int dim = vi->in_dimension(); + const int dim = c3t3.in_dimension(vi); switch (dim) { @@ -671,8 +672,8 @@ class Tetrahedral_remeshing_smoother const Vertex_handle vh0 = e.first->vertex(e.second); const Vertex_handle vh1 = e.first->vertex(e.third); - CGAL_expensive_assertion(is_on_feature(vh0)); - CGAL_expensive_assertion(is_on_feature(vh1)); + CGAL_expensive_assertion(is_on_feature(c3t3, vh0)); + CGAL_expensive_assertion(is_on_feature(c3t3, vh1)); const std::size_t& i0 = vertex_id(vh0); const std::size_t& i1 = vertex_id(vh1); @@ -706,7 +707,7 @@ class Tetrahedral_remeshing_smoother { const std::size_t vid = vertex_id(v); - if (!is_free(vid) || !is_on_feature(v)) + if (!is_free(vid) || !is_on_feature(c3t3, v)) continue; const Point_3 current_pos = point(v->point()); @@ -792,8 +793,8 @@ std::size_t smooth_vertices_on_surfaces(C3t3& c3t3, const std::size_t& i0 = vertex_id(vh0); const std::size_t& i1 = vertex_id(vh1); - const bool vh0_moving = !is_on_feature(vh0) && is_free(i0); - const bool vh1_moving = !is_on_feature(vh1) && is_free(i1); + const bool vh0_moving = !is_on_feature(c3t3, vh0) && is_free(i0); + const bool vh1_moving = !is_on_feature(c3t3, vh1) && is_free(i1); if (!vh0_moving && !vh1_moving) continue; @@ -822,7 +823,7 @@ std::size_t smooth_vertices_on_surfaces(C3t3& c3t3, { const std::size_t vid = vertex_id(v); - if (!is_free(vid) || v->in_dimension() != 2) + if (!is_free(vid) || c3t3.in_dimension(v) != 2) continue; const std::size_t nb_neighbors = moves[vid].neighbors; @@ -835,7 +836,7 @@ std::size_t smooth_vertices_on_surfaces(C3t3& c3t3, CGAL_assertion(si != Surface_patch_index()); CGAL_expensive_assertion_code(auto siv = surface_patch_index(v, c3t3)); - CGAL_expensive_assertion(si == siv); + CGAL_expensive_assertion(si == siv.value()); if (nb_neighbors > 1) { @@ -1085,7 +1086,7 @@ std::size_t smooth_internal_vertices(C3t3& c3t3, reset_vertex_id_map(tr); //are vertices free to move? indices are in `vertex_id` - reset_free_vertices(tr); + reset_free_vertices(c3t3); //collect incident cells using Incident_cells_vector = boost::container::small_vector; @@ -1146,7 +1147,7 @@ std::size_t smooth_internal_vertices(C3t3& c3t3, #endif #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - c3t3.triangulation(), "c3t3_vertices_after_smoothing"); + c3t3, "c3t3_vertices_after_smoothing"); os_surf.close(); os_vol.close(); #endif diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h index 8a53ed2ea9e4..225b87971b4c 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/split_long_edges.h @@ -151,7 +151,8 @@ typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e, else if(nb_patches == 0) dimension = 3; else - CGAL_assertion(false);//e should be in complex + dimension = 1;//e is in complex +// CGAL_assertion(false);//e should be in complex } CGAL_assertion(dimension > 0); @@ -233,7 +234,7 @@ typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e, // insert midpoint Vertex_handle new_v = tr.tds().insert_in_edge(e); new_v->set_point(typename Tr::Point(m)); - new_v->set_dimension(dimension); + c3t3.set_dimension(new_v, dimension); // update c3t3 with subdomain and surface patch indices std::vector new_cells; @@ -254,16 +255,15 @@ typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e, const Facet_info v_and_opp_patch = facets_info.at(mfi); // facet opposite to new_v (status wrt c3t3 is unchanged) - new_cell->set_surface_patch_index(new_cell->index(new_v), - mfi.first->surface_patch_index(mfi.second)); + c3t3.set_surface_patch_index({new_cell, new_cell->index(new_v)}, + c3t3.surface_patch_index(mfi)); // new half-facet (added or not to c3t3 depending on the stored surface patch index) if (Surface_patch_index() == v_and_opp_patch.patch_index_) - new_cell->set_surface_patch_index(new_cell->index(v_and_opp_patch.opp_vertex_), - Surface_patch_index()); + c3t3.set_surface_patch_index({new_cell, new_cell->index(v_and_opp_patch.opp_vertex_)}, + Surface_patch_index()); else - c3t3.add_to_complex(new_cell, - new_cell->index(v_and_opp_patch.opp_vertex_), + c3t3.add_to_complex({new_cell, new_cell->index(v_and_opp_patch.opp_vertex_)}, v_and_opp_patch.patch_index_); // newly created internal facet @@ -272,7 +272,7 @@ typename C3t3::Vertex_handle split_edge(const typename C3t3::Edge& e, const Vertex_handle vi = new_cell->vertex(i); if (vi == v1 || vi == v2) { - new_cell->set_surface_patch_index(i, Surface_patch_index()); + c3t3.set_surface_patch_index({new_cell, i}, Surface_patch_index()); break; } } diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h index 187178c984ae..b13adfc128ac 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_adaptive_remeshing_impl.h @@ -16,7 +16,9 @@ #include #ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE_PROGRESS -#define CGAL_TETRAHEDRAL_REMESHING_VERBOSE +# ifndef CGAL_TETRAHEDRAL_REMESHING_VERBOSE +# define CGAL_TETRAHEDRAL_REMESHING_VERBOSE +# endif #endif #include @@ -70,14 +72,14 @@ template > class Adaptive_remesher { typedef Triangulation Tr; + typedef typename Tr::Geom_traits Gt; typedef typename Tr::Geom_traits::FT FT; - typedef CGAL::Mesh_complex_3_in_triangulation_3 C3t3; - typedef typename C3t3::Cell_handle Cell_handle; typedef typename C3t3::Vertex_handle Vertex_handle; typedef typename C3t3::Edge Edge; @@ -180,7 +182,7 @@ class Adaptive_remesher CGAL::Tetrahedral_remeshing::debug::dump_facets_in_complex(m_c3t3, "1-facets_in_complex_after_split.off"); CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "1-c3t3_vertices_after_split"); + m_c3t3, "1-c3t3_vertices_after_split"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, "1-edges_in_complex_after_split.polylines.txt"); @@ -202,7 +204,7 @@ class Adaptive_remesher CGAL_assertion(tr().tds().is_valid(true)); CGAL_assertion(debug::are_cell_orientations_valid(tr())); CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "2-c3t3_vertices_after_collapse"); + m_c3t3, "2-c3t3_vertices_after_collapse"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, "2-edges_in_complex_after_collapse.polylines.txt"); @@ -223,7 +225,7 @@ class Adaptive_remesher CGAL_assertion(tr().tds().is_valid(true)); CGAL_assertion(debug::are_cell_orientations_valid(tr())); CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "3-c3t3_vertices_after_flip"); + m_c3t3, "3-c3t3_vertices_after_flip"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, "3-edges_in_complex_after_flip.polylines.txt"); @@ -243,7 +245,7 @@ class Adaptive_remesher CGAL_assertion(tr().tds().is_valid(true)); CGAL_assertion(debug::are_cell_orientations_valid(tr())); CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "4-c3t3_vertices_after_smooth"); + m_c3t3, "4-c3t3_vertices_after_smooth"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, "4-edges_in_complex_after_smoothing.polylines.txt"); @@ -307,7 +309,7 @@ class Adaptive_remesher { //reset far points dimension for (Vertex_handle v : m_far_points) - v->set_dimension(-1); + m_c3t3.set_dimension(v, -1); m_c3t3_pbackup->swap(m_c3t3); } else @@ -331,7 +333,7 @@ class Adaptive_remesher #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "00-c3t3_vertices_before_init_"); + m_c3t3, "00-c3t3_vertices_before_init_"); #endif if (input_is_c3t3()) @@ -344,7 +346,7 @@ class Adaptive_remesher { if (get(m_cell_selector, cit)) { - const Subdomain_index index = cit->subdomain_index(); + const Subdomain_index index = m_c3t3.subdomain_index(cit); if (m_c3t3.is_in_complex(cit)) m_c3t3.remove_from_complex(cit); @@ -372,15 +374,15 @@ class Adaptive_remesher for (const Facet& f : tr().finite_facets()) { const Facet mf = tr().mirror_facet(f); - const Subdomain_index s1 = f.first->subdomain_index(); - const Subdomain_index s2 = mf.first->subdomain_index(); + const Subdomain_index s1 = m_c3t3.subdomain_index(f.first); + const Subdomain_index s2 = m_c3t3.subdomain_index(mf.first); if (s1 != s2 || get(fcmap, f) || get(fcmap, mf) || (input_is_c3t3() && m_c3t3.is_in_complex(f)) - || (!input_is_c3t3() && f.first->is_facet_on_surface(f.second))) + || (!input_is_c3t3() && m_c3t3.is_in_complex(f))) { - Surface_patch_index patch = f.first->surface_patch_index(f.second); + Surface_patch_index patch = m_c3t3.surface_patch_index(f); if(patch == Surface_patch_index()) make_surface_patch_index(s1, s2, patch); @@ -449,7 +451,7 @@ class Adaptive_remesher m_c3t3.add_to_complex(vit, ++corner_id); set_dimension(vit, 0); - vit->set_index(corner_id); + //vit->set_index(corner_id); #ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG ++nbv; @@ -471,7 +473,7 @@ class Adaptive_remesher std::cout << "\t Min dihedral angle = " << mdh << std::endl; CGAL::Tetrahedral_remeshing::debug::dump_vertices_by_dimension( - m_c3t3.triangulation(), "0-c3t3_vertices_after_init_"); + m_c3t3, "0-c3t3_vertices_after_init_"); CGAL::Tetrahedral_remeshing::debug::check_surface_patch_indices(m_c3t3); CGAL::Tetrahedral_remeshing::debug::count_far_points(m_c3t3); CGAL::Tetrahedral_remeshing::debug::dump_edges_in_complex(m_c3t3, @@ -483,7 +485,7 @@ class Adaptive_remesher { for (Vertex_handle v : tr().finite_vertex_handles()) { - if (v->in_dimension() == -1) + if (m_c3t3.in_dimension(v) == -1) m_far_points.push_back(v); } } @@ -491,7 +493,7 @@ class Adaptive_remesher private: bool dimension_is_modifiable(const Vertex_handle& v, const int new_dim) const { - const int vdim = v->in_dimension(); + const int vdim = m_c3t3.in_dimension(v); // feature edges and tip/endpoints vertices are kept switch (vdim) { @@ -510,7 +512,7 @@ class Adaptive_remesher void set_dimension(Vertex_handle v, const int new_dim) { if (dimension_is_modifiable(v, new_dim)) - v->set_dimension(new_dim); + m_c3t3.set_dimension(v, new_dim); } bool check_vertex_dimensions() @@ -519,7 +521,7 @@ class Adaptive_remesher { // dimension is -1 for Mesh_3 "far points" // for other vertices, it is in [0; 3] - if (vit->in_dimension() < -1 || vit->in_dimension() > 3) + if (m_c3t3.in_dimension(vit) < -1 || m_c3t3.in_dimension(vit) > 3) return false; } return true; @@ -554,7 +556,7 @@ class Adaptive_remesher = (std::numeric_limits::min)(); for (Cell_handle cit : tr().finite_cell_handles()) { - const Subdomain_index cid = cit->subdomain_index(); + const Subdomain_index cid = m_c3t3.subdomain_index(cit); if (cid > max_index && cid != Subdomain_index()) max_index = cid; } @@ -667,7 +669,9 @@ template + typename CurveIndex = int, + typename C3t3_or_CDT_3 + = CGAL::Mesh_complex_3_in_triangulation_3> struct Adaptive_remesher_type_generator { using Tr = Triangulation; @@ -711,7 +715,9 @@ struct Adaptive_remesher_type_generator >::type; using type = Adaptive_remesher< - Tr, SizingFunction, VCMap, ECMap, FCMap, SelectionFunctor, Visitor>; + Tr, SizingFunction, VCMap, ECMap, FCMap, SelectionFunctor, Visitor, + CornerIndex, CurveIndex, + C3t3_or_CDT_3>; }; }//end namespace internal diff --git a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h index 2cb6bef49ef1..1a4ee5d8ace7 100644 --- a/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h +++ b/Tetrahedral_remeshing/include/CGAL/Tetrahedral_remeshing/internal/tetrahedral_remeshing_helpers.h @@ -644,10 +644,10 @@ Facet canonical_facet(const Facet& f) return (c2 < c) ? std::make_pair(c2, c2->index(c)) : std::make_pair(c, i); } -template -bool is_on_feature(const VertexHandle v) +template +bool is_on_feature(const C3T3& c3t3, const VertexHandle v) { - return (v->in_dimension() == 1 || v->in_dimension() == 0); + return (c3t3.in_dimension(v) == 1 || c3t3.in_dimension(v) == 0); } template @@ -694,8 +694,8 @@ bool is_boundary(const C3T3& c3t3, << "\n\t in_complex = " << c3t3.is_in_complex(f) << "\n\t selector(f.first) = " << get(cell_selector, f.first) << "\n\t selector(mirror ) = " << get(cell_selector, mf.first) - << "\n\t subdomain(f.first)= " << f.first->subdomain_index() - << "\n\t subdomain(mirror) = " << mf.first->subdomain_index() + << "\n\t subdomain(f.first)= " << c3t3.subdomain_index(f.first) + << "\n\t subdomain(mirror) = " << c3t3.subdomain_index(mf.first) << std::endl; } @@ -825,21 +825,21 @@ surface_patch_index(const typename C3t3::Vertex_handle v, template void set_index(typename C3t3::Vertex_handle v, const C3t3& c3t3) { - switch (v->in_dimension()) + switch (c3t3.in_dimension(v)) { case 3: - v->set_index(v->cell()->subdomain_index()); + c3t3.set_index(v, c3t3.subdomain_index(v->cell())); break; case 2: CGAL_expensive_assertion(surface_patch_index(v, c3t3) != typename C3t3::Surface_patch_index()); - v->set_index(surface_patch_index(v, c3t3).value()); + c3t3.set_index(v, surface_patch_index(v, c3t3).value()); break; case 1: - v->set_index(typename C3t3::Curve_index(1)); + c3t3.set_index(v, typename C3t3::Curve_index(1)); break; case 0: - v->set_index(Mesh_3::internal::get_index(v->index())); + c3t3.set_index(v, Mesh_3::internal::get_index(c3t3.index(v))); break; case -1://far points from concurrent Mesh_3 break; @@ -895,8 +895,8 @@ OutputIterator incident_subdomains(const typename C3t3::Vertex_handle v, std::vector cells; c3t3.triangulation().incident_cells(v, std::back_inserter(cells)); - for (std::size_t i = 0; i < cells.size(); ++i) - *oit++ = cells[i]->subdomain_index(); + for (Cell_handle ci : cells) + *oit++ = c3t3.subdomain_index(ci); return oit; } @@ -912,7 +912,7 @@ OutputIterator incident_subdomains(const typename C3t3::Edge& e, Cell_circulator end = circ; do { - *oit++ = circ->subdomain_index(); + *oit++ = c3t3.subdomain_index(circ); } while (++circ != end); return oit; @@ -1179,27 +1179,27 @@ bool is_internal(const typename C3t3::Edge& edge, const C3t3& c3t3, CellSelector cell_selector) { - const typename C3t3::Vertex_handle vs = edge.first->vertex(edge.second); - const typename C3t3::Vertex_handle vt = edge.first->vertex(edge.third); - typedef typename C3t3::Triangulation::Cell_circulator Cell_circulator; Cell_circulator circ = c3t3.triangulation().incident_cells(edge); Cell_circulator done = circ; - const typename C3t3::Subdomain_index si = circ->subdomain_index(); + const typename C3t3::Subdomain_index si = c3t3.subdomain_index(circ); do { if (c3t3.triangulation().is_infinite(circ)) return false; - if (si != circ->subdomain_index()) + if (si != c3t3.subdomain_index(circ)) return false; if (!get(cell_selector, circ)) return false; - if (c3t3.is_in_complex( - circ, - CGAL::Triangulation_utils_3::next_around_edge(circ->index(vs), circ->index(vt)))) + + Cell_circulator ch = circ; + Cell_circulator next_circ = ++circ; + if(c3t3.is_in_complex(ch, ch->index(next_circ))) return false; - } while (++circ != done); + circ = next_circ; + } + while (circ != done); return true; } @@ -1338,7 +1338,7 @@ auto size_at_centroid(const typename C3t3::Cell_handle c, CGAL_assertion(c3t3.is_in_complex(c)); const auto cc = CGAL::centroid(c3t3.triangulation().tetrahedron(c)); - return sizing(cc, 3, c->subdomain_index()); + return sizing(cc, 3, c3t3.subdomain_index(c)); } template @@ -1417,9 +1417,9 @@ auto sizing_at_vertex(const Vertex_handle v, const C3t3& c3t3, const Cell_selector& cell_selector) { - auto size = sizing(point(v->point()), v->in_dimension(), v->index()); + auto size = sizing(point(v->point()), c3t3.in_dimension(v), c3t3.index(v)); - if(v->in_dimension() < 3 && size == 0) + if(c3t3.in_dimension(v) < 3 && size == 0) { std::vector cells; c3t3.triangulation().incident_cells(v, std::back_inserter(cells)); @@ -1451,8 +1451,8 @@ auto sizing_at_midpoint(const typename C3t3::Edge& e, { const auto [u, v] = make_vertex_pair(e); - const FT size_at_u = sizing(cp(u->point()), u->in_dimension(), u->index()); - const FT size_at_v = sizing(cp(v->point()), v->in_dimension(), v->index()); + const FT size_at_u = sizing(cp(u->point()), c3t3.in_dimension(u), c3t3.index(u)); + const FT size_at_v = sizing(cp(v->point()), c3t3.in_dimension(v), c3t3.index(v)); if (size_at_u == 0. || size_at_v == 0.) return average_sizing_in_incident_cells(e, sizing, c3t3, cell_selector); @@ -1515,23 +1515,26 @@ auto min_sizing_in_incident_cells(const typename C3t3::Edge& e, return size_at_uv; } -template +template auto -max_dimension_index(const Vertex_handle v0, const Vertex_handle v1) +max_dimension_index(const typename C3t3::Vertex_handle v0, + const typename C3t3::Vertex_handle v1, + const C3t3& c3t3) { - const int dim0 = v0->in_dimension(); - const int dim1 = v1->in_dimension(); + const int dim0 = c3t3.in_dimension(v0); + const int dim1 = c3t3.in_dimension(v1); - if (dim0 > dim1) return v0->index(); - else if (dim1 > dim0) return v1->index(); - else return v0->index(); //arbitrary choice, any of the two should be fine + if (dim0 > dim1) return c3t3.index(v0); + else if (dim1 > dim0) return c3t3.index(v1); + else return c3t3.index(v0); //arbitrary choice, any of the two should be fine } -template +template auto -max_dimension_index(const std::array& vs) +max_dimension_index(const std::array& vs, + const C3t3& c3t3) { - return max_dimension_index(vs[0], vs[1]); + return max_dimension_index(vs[0], vs[1], c3t3); } template @@ -1548,15 +1551,15 @@ approximate_edge_length(const typename Tr::Edge& e, const Tr& tr) return CGAL::approximate_sqrt(squared_edge_length(e, tr)); } -template -typename Tr::Cell::Subdomain_index +template +typename C3t3::Subdomain_index subdomain_index_at_point_3(const Pt& p, - const typename Tr::Cell_handle hint, - const Tr& tr) + const typename C3t3::Cell_handle hint, + const C3t3& c3t3) { - const typename Tr::Point tr_p(p); - const typename Tr::Cell_handle c = tr.locate(tr_p, hint); - return c->subdomain_index(); + const typename C3t3::Triangulation::Point tr_p(p); + const typename C3t3::Cell_handle c = c3t3.triangulation().locate(tr_p, hint); + return c3t3.subdomain_index(c); } template @@ -1584,11 +1587,11 @@ auto midpoint_with_info(const typename C3t3::Edge& e, const Point_3 midpoint_pt = midpt(cp(u->point()), cp(v->point())); const int midpoint_dim = boundary_edge - ? (std::max)(u->in_dimension(), v->in_dimension()) + ? (std::max)(c3t3.in_dimension(u), c3t3.in_dimension(v)) : 3; const Index midpoint_index = boundary_edge - ? max_dimension_index(c3t3.triangulation().vertices(e)) - : subdomain_index_at_point_3(midpoint_pt, e.first, c3t3.triangulation()); + ? max_dimension_index(c3t3.triangulation().vertices(e), c3t3) + : subdomain_index_at_point_3(midpoint_pt, e.first, c3t3); return Midpoint_with_info{midpoint_pt, midpoint_dim, midpoint_index}; } @@ -1613,8 +1616,8 @@ squared_upper_size_bound(const typename C3t3::Edge& e, const Vertex_handle u = e.first->vertex(e.second); const Vertex_handle v = e.first->vertex(e.third); - const FT size_at_u = sizing(cp(u->point()), u->in_dimension(), u->index()); - const FT size_at_v = sizing(cp(v->point()), v->in_dimension(), v->index()); + const FT size_at_u = sizing(cp(u->point()), c3t3.in_dimension(u), c3t3.index(u)); + const FT size_at_v = sizing(cp(v->point()), c3t3.in_dimension(v), c3t3.index(v)); // if e is on the boundary AND sizing at the boundary is set to 0, // we take the maximum size of the incident cells @@ -1670,8 +1673,8 @@ squared_lower_size_bound(const typename C3t3::Edge& e, const Vertex_handle u = e.first->vertex(e.second); const Vertex_handle v = e.first->vertex(e.third); - const FT size_at_u = sizing(point(u->point()), u->in_dimension(), u->index()); - const FT size_at_v = sizing(point(v->point()), v->in_dimension(), v->index()); + const FT size_at_u = sizing(point(u->point()), c3t3.in_dimension(u), c3t3.index(u)); + const FT size_at_v = sizing(point(v->point()), c3t3.in_dimension(v), c3t3.index(v)); // if e is on the boundary AND sizing at the boundary is set to 0, // we take the minimum size of the incident cells @@ -1870,8 +1873,8 @@ void get_edge_info(const typename C3t3::Edge& edge, } else { - const bool v0_on_feature = is_on_feature(v0); - const bool v1_on_feature = is_on_feature(v1); + const bool v0_on_feature = is_on_feature(c3t3, v0); + const bool v1_on_feature = is_on_feature(c3t3, v1); if (v0_on_feature && v1_on_feature) { if (c3t3.is_in_complex(edge)) { @@ -1977,7 +1980,7 @@ namespace internal if (Subdomain_index() != subdomain) c3t3.add_to_complex(c, subdomain); else - c->set_subdomain_index(Subdomain_index()); + c3t3.set_subdomain_index(c, Subdomain_index()); //update cell_selector property map put(cell_selector, c, selected); @@ -2095,9 +2098,9 @@ void check_surface_patch_indices(const C3t3& c3t3) typedef typename C3t3::Vertex_handle Vertex_handle; for (Vertex_handle v : c3t3.triangulation().finite_vertex_handles()) { - if (v->in_dimension() != 2) + if (c3t3.in_dimension(v) != 2) continue; - CGAL_expensive_assertion(surface_patch_index(v, c3t3) != typename C3t3::Surface_patch_index()); + CGAL_expensive_assertion(surface_patch_index(v, c3t3) != std::nullopt); } } @@ -2114,8 +2117,11 @@ void count_far_points(const C3t3& c3t3) } template -bool are_cell_orientations_valid(const Tr& tr) +bool are_cell_orientations_valid([[maybe_unused]] const Tr& tr) { +#ifdef CGAL_T3_ALLOW_NEGATIVE_VOLUME + return true; +#else typedef typename Tr::Geom_traits::Point_3 Point_3; typedef typename Tr::Facet Facet; @@ -2143,6 +2149,7 @@ bool are_cell_orientations_valid(const Tr& tr) dump_facets(facets, "cells_with_negative_volume.polylines.txt"); } return facets.empty(); +#endif } template @@ -2517,10 +2524,12 @@ void dump_cells_with_small_dihedral_angle(const Tr& tr, dump_cells_off(cells, tr, "bad_cells.off"); } -template -void dump_vertices_by_dimension(const Tr& tr, const char* prefix) +template +void dump_vertices_by_dimension(const C3t3& c3t3, const char* prefix) { - typedef typename Tr::Vertex_handle Vertex_handle; + using Tr = typename C3t3::Triangulation; + const Tr& tr = c3t3.triangulation(); + using Vertex_handle = typename Tr::Vertex_handle; std::vector< std::vector > vertices_per_dimension(4); std::size_t nb_far_points = 0; @@ -2529,14 +2538,14 @@ void dump_vertices_by_dimension(const Tr& tr, const char* prefix) vit != tr.finite_vertices_end(); ++vit) { - if (vit->in_dimension() == -1) + if (c3t3.in_dimension(vit) == -1) { ++nb_far_points; continue;//far point } - CGAL_assertion(vit->in_dimension() >= 0 && vit->in_dimension() < 4); + CGAL_assertion(c3t3.in_dimension(vit) >= 0 && c3t3.in_dimension(vit) < 4); - vertices_per_dimension[vit->in_dimension()].push_back(vit); + vertices_per_dimension[c3t3.in_dimension(vit)].push_back(vit); } for (int i = 0; i < 4; ++i) @@ -2591,20 +2600,21 @@ template void dump_medit(const C3t3& c3t3, const char* filename) { std::ofstream os(filename, std::ios::out); - c3t3.output_to_medit(os, true, true); + //CGAL::IO::write_MEDIT(os, c3t3.triangulation()); + //c3t3.output_to_medit(os, true, true); os.close(); } template void dump_c3t3(const C3t3& c3t3, const char* filename_no_extension) { - std::string filename_medit(filename_no_extension); - filename_medit.append(".mesh"); - dump_medit(c3t3, filename_medit.c_str()); - - std::string filename_binary(filename_no_extension); - filename_binary.append(".binary.cgal"); - dump_binary(c3t3, filename_binary.c_str()); +// std::string filename_medit(filename_no_extension); +// filename_medit.append(".mesh"); +// dump_medit(c3t3, filename_medit.c_str()); +// +// std::string filename_binary(filename_no_extension); +// filename_binary.append(".binary.cgal"); +// dump_binary(c3t3, filename_binary.c_str()); } diff --git a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h index c5f9fda2067a..4a4c8a236c15 100644 --- a/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h +++ b/Tetrahedral_remeshing/include/CGAL/tetrahedral_remeshing.h @@ -523,6 +523,115 @@ void tetrahedral_isotropic_remeshing( #endif } +//////////////////////////////////////////////////////// +/////// CONFORMING_CONSTRAINED_TRIANGULATION_3 ///////// +//////////////////////////////////////////////////////// + +template > +#endif + > +void tetrahedral_isotropic_remeshing(CDT_3& cdt3, + const SizingFunction& sizing, + const NamedParameters& np = parameters::default_values()) +{ + CGAL_expensive_assertion(cdt3.triangulation().tds().is_valid()); + + using parameters::choose_parameter; + using parameters::get_parameter; + + using Tr = typename CDT_3::Triangulation; + using Remesher_types = + Tetrahedral_remeshing::internal::Adaptive_remesher_type_generator; + + bool remesh_surfaces = choose_parameter(get_parameter(np, internal_np::remesh_boundaries), true); + bool protect = !remesh_surfaces; + std::size_t max_it = choose_parameter(get_parameter(np, internal_np::number_of_iterations), 1); + + bool smooth_constrained_edges = choose_parameter(get_parameter(np, internal_np::smooth_constrained_edges), false); + + auto cell_select = choose_parameter(get_parameter(np, internal_np::cell_selector), + typename Remesher_types::Default_Selection_functor()); + + auto vcmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), + typename Remesher_types::Default_VCMap{}); + + auto ecmap = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), + typename Remesher_types::Default_ECMap{}); + + auto fcmap = choose_parameter(get_parameter(np, internal_np::facet_is_constrained), + typename Remesher_types::Default_FCMap{}); + + // Advanced and non documented parameters + auto visitor = choose_parameter(get_parameter(np, internal_np::visitor), typename Remesher_types::Default_Visitor()); + + auto nb_extra_iterations = + choose_parameter(get_parameter(np, internal_np::nb_flip_smooth_iterations), std::size_t{3}); + +#ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE + std::cout << "Tetrahedral remeshing (" + << "nb_iter = " << max_it << ", " + << "protect = " << std::boolalpha << protect << ")" << std::endl; + + std::cout << "Init tetrahedral remeshing..."; + std::cout.flush(); +#endif + + using Remesher = typename Remesher_types::type; + Remesher remesher(cdt3, sizing, protect, vcmap, ecmap, fcmap, smooth_constrained_edges, cell_select, visitor); + +#ifdef CGAL_TETRAHEDRAL_REMESHING_VERBOSE + std::cout << "done." << std::endl; + Tetrahedral_remeshing::internal::compute_statistics(remesher.tr(), cell_select, "statistics_begin.txt"); +#endif + + // perform remeshing +#ifdef CGAL_TETRAHEDRAL_REMESHING_NO_EXTRA_ITERATIONS + nb_extra_iterations = 0; +#endif + + remesher.remesh(max_it, nb_extra_iterations); + +#ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG + const double angle_bound = 5.0; + Tetrahedral_remeshing::debug::dump_cells_with_small_dihedral_angle(cdt3.triangulation(), angle_bound, cell_select, + "bad_cells.mesh"); +#endif +#ifdef CGAL_TETRAHEDRAL_REMESHING_DEBUG + Tetrahedral_remeshing::internal::compute_statistics(cdt3.triangulation(), cell_select, "statistics_end.txt"); +#endif +} + +template > +#endif + > +void tetrahedral_isotropic_remeshing(CDT_3& cdt3, + const FT target_edge_length, + const NamedParameters& np = parameters::default_values()) +{ + using P = typename CDT_3::Triangulation::Geom_traits::Point_3; + using Index = int;//typename CDT_3::Triangulation::Vertex::Index; + using Sizing_FT = typename CDT_3::Triangulation::Geom_traits::FT; + + const Sizing_FT target_ft = static_cast(target_edge_length); + + tetrahedral_isotropic_remeshing(cdt3, + [target_ft](const P&, const int&, const Index&) + { return target_ft; }, + np); +} + + }//end namespace CGAL #endif //CGAL_TETRAHEDRAL_REMESHING_H diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index fea1aba15eec..ccaac325bcc2 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -310,6 +311,18 @@ class Triangulation_2 construct_point(const Point& p) const { return geom_traits().construct_point_2_object()(p); } + std::array vertices(Face_handle f) const { + return {f->vertex(0), f->vertex(1), f->vertex(2)}; + } + + std::array vertices(Edge e) const { + return {e.first->vertex(ccw(e.second)), e.first->vertex(cw(e.second))}; + } + + std::array vertices(Vertex_handle v) const { + return {v}; + } + const Point& point(Face_handle f, int i) const; const Point& point(Vertex_handle v) const; @@ -653,14 +666,19 @@ projection_traits_has_inexact_negative_orientation(const Point &p, const Point & template void fill_hole(Vertex_handle v, std::list & hole, OutputItFaces fit); - template - void fill_hole_delaunay(std::list & hole, OutputItFaces fit); - void make_hole(Vertex_handle v, std::list & hole, std::set &faces_set); public: void make_hole(Vertex_handle v, std::list & hole); + + // convenience function to get the hole + auto make_hole(Vertex_handle v) { + std::list hole; + make_hole(v, hole); + return hole; + } + // template // Vertex_handle star_hole( Point p, // EdgeIt edge_begin, @@ -2029,33 +2047,36 @@ fill_hole(Vertex_handle v, std::list & hole, OutputItFaces fit) *fit++ = newf; } -template -void -Triangulation_2:: -fill_hole_delaunay(std::list & first_hole) +template +OutputItFaces +fill_hole_delaunay(Triangulation_2& tr, + std::list::Edge>& first_hole, + OutputItFaces fit) { - typedef std::list Hole; - typedef std::list Hole_list; - - Face_handle f, ff, fn; - int i, ii, in; - Hole_list hole_list; + using Tr = Triangulation_2; + using Vertex_handle = typename Tr::Vertex_handle; + using Edge = typename Tr::Edge; + using Face_handle = typename Tr::Face_handle; - hole_list.push_front(first_hole); + auto create_face = [&](auto &&... args) { + Face_handle newf = tr.tds().create_face(std::forward(args)...); + *fit++ = newf; + return newf; + }; + using Hole = std::list; + std::list hole_list{1, first_hole}; while( ! hole_list.empty()) { Hole& hole = hole_list.front(); - typename Hole::iterator hit = hole.begin(); - // if the hole has only three edges, create the triangle if (hole.size() == 3) { - hit = hole.begin(); - f = (*hit).first; i = (*hit).second; - ff = (* ++hit).first; ii = (*hit).second; - fn = (* ++hit).first; in = (*hit).second; - create_face(f,i,ff,ii,fn,in); + auto hit = hole.begin(); + auto [f0, i0] = *hit; ++hit; + auto [f1, i1] = *hit; ++hit; + auto [f2, i2] = *hit; + create_face(f0,i0,f1,i1,f2,i2); hole_list.pop_front(); continue; } @@ -2065,212 +2086,99 @@ fill_hole_delaunay(std::list & first_hole) // and the new triangle adjacent to that edge // cut the hole and push it back + // two helper functions: + auto cw_vertex = [](Edge e) { return e.first->vertex( Tr::cw(e.second)); }; + auto ccw_vertex = [](Edge e) { return e.first->vertex(Tr::ccw(e.second)); }; + // first, ensure that a neighboring face // whose vertices on the hole boundary are finite // is the first of the hole - bool finite= false; - while (!finite){ - ff = (hole.front()).first; - ii = (hole.front()).second; - if ( is_infinite(ff->vertex(cw(ii))) || - is_infinite(ff->vertex(ccw(ii)))) { - hole.push_back(hole.front()); - hole.pop_front(); - } - else finite=true; + for (auto eit = hole.begin(); + tr.is_infinite(cw_vertex(*eit)) || tr.is_infinite(ccw_vertex(*eit)); + eit = hole.begin()) + { + hole.splice(hole.end(), hole, hole.begin()); // push the first element to the end of the list } // take the first neighboring face and pop it; - ff = (hole.front()).first; - ii =(hole.front()).second; + auto first_edge = hole.front(); hole.pop_front(); - Vertex_handle v0 = ff->vertex(cw(ii)); - Vertex_handle v1 = ff->vertex(ccw(ii)); - Vertex_handle v2 = infinite_vertex(); - Vertex_handle v3; - const Point& p0 = v0->point(); - const Point& p1 = v1->point(); - - typename Hole::iterator hdone = hole.end(); - hit = hole.begin(); - typename Hole::iterator cut_after(hit); - - // if tested vertex is c with respect to the vertex opposite - // to nullptr neighbor, - // stop at the before last face; - hdone--; - while( hit != hdone) { - fn = (*hit).first; - in = (*hit).second; - Vertex_handle vv = fn->vertex(ccw(in)); - if (is_infinite(vv)) { - if(is_infinite(v2)) cut_after = hit; - } - else { // vv is a finite vertex - const Point & p = vv->point(); - if (orientation(p0,p1,p) == COUNTERCLOCKWISE) { - if (is_infinite(v2)) { v2=vv; v3=vv; cut_after=hit;} - else{ - // - if (this->side_of_oriented_circle(p0,p1,v3->point(),p,true) == ON_POSITIVE_SIDE){ - v2=vv; v3=vv; cut_after=hit;} - } - } - } - ++hit; - } + const Vertex_handle v0 = cw_vertex(first_edge); + const Vertex_handle v1 = ccw_vertex(first_edge); - // create new triangle and update adjacency relations - Face_handle newf; + // search in `hole` an edge `cut_after`, and its vertex `v2==ccw_vertex(*cut_after)` + // such that the triangle (v0,v1,v2) is a Delaunay triangle + const auto [cut_after, v2] = std::invoke([&]() { + const auto hole_end = hole.cend(); + const auto hole_last_one = std::prev(hole_end); - //update the hole and push back in the Hole_List stack - // if v2 belongs to the neighbor following or preceding *f - // the hole remain a single hole - // otherwise it is split in two holes + auto cut_after = hole_end; + Vertex_handle v2 = tr.infinite_vertex(); - fn = (hole.front()).first; - in = (hole.front()).second; - if (fn->has_vertex(v2, i) && i == fn->ccw(in)) { - newf = create_face(ff,ii,fn,in); - hole.pop_front(); - hole.push_front(Edge( newf,1)); - } - else{ - fn = (hole.back()).first; - in = (hole.back()).second; - if (fn->has_vertex(v2, i) && i== fn->cw(in)) { - newf = create_face(fn,in,ff,ii); - hole.pop_back(); - hole.push_back(Edge(newf,1)); - } - else { - // split the hole in two holes - newf = create_face(ff,ii,v2); - Hole new_hole; - ++cut_after; - while( hole.begin() != cut_after ) - { - new_hole.push_back(hole.front()); - hole.pop_front(); - } + const auto& p0 = v0->point(); + const auto& p1 = v1->point(); - hole.push_front(Edge( newf,1)); - new_hole.push_front(Edge( newf,0)); - hole_list.push_front(new_hole); + for(auto hole_edge_it = hole.cbegin(); hole_edge_it != hole_last_one; ++hole_edge_it) + { + const Vertex_handle vv = ccw_vertex(*hole_edge_it); + if (tr.is_infinite(vv)) { + if(tr.is_infinite(v2)) cut_after = hole_edge_it; + } + else { // vv is a finite vertex + const auto& p = vv->point(); + if (tr.orientation(p0,p1,p) == COUNTERCLOCKWISE) { + if (tr.is_infinite(v2) || + tr.side_of_oriented_circle(p0,p1,v2->point(),p,true) == ON_POSITIVE_SIDE) { + cut_after = hole_edge_it; v2 = vv; + } + } + } } + return std::make_pair(cut_after, v2); + }); + CGAL_assertion(cut_after != hole.cend()); + + // create new triangle (v0, v1, v2) and update adjacency relations + + // Update the hole and push back in the Hole_List stack. + // If v2 belongs to the neighbor following or preceding `first_edge`: + // - then the hole remain a single hole, + // - otherwise it is split in two holes + // Note that `first_edge`, that was the `front()` of the hole, has + // already been popped from it. + const auto& [ff, ii] = first_edge; + if (auto [fn, in] = hole.front(); fn->vertex(Tr::ccw(in)) == v2) { + Face_handle newf = create_face(ff,ii,fn,in); + hole.front() = Edge(newf,1); } - } + else if (auto [fn, in] = hole.back(); fn->vertex(Tr::cw(in)) == v2) { + Face_handle newf = create_face(fn,in,ff,ii); + hole.back() = Edge(newf,1); + } + else { + Face_handle newf = create_face(ff,ii,v2); + + // split the hole in two holes: + // - transfer the range [hole.begin(), cut_after] (`cut_after` included) to `new_hole` + // - `hole` keeps the rest: [cut_after+1, hole.end()( + Hole new_hole; + new_hole.splice(new_hole.end(), hole, hole.begin(), std::next(cut_after)); + new_hole.push_front(Edge(newf,0)); // close the new hole with the edge (newf, 0) + hole_list.push_front(new_hole); // and push it in the stack + + hole.push_front(Edge(newf,1)); // close the hole with the edge (newf, 1) + } + } // while( ! hole_list.empty()) + return fit; } template < class Gt, class Tds > -template void Triangulation_2:: -fill_hole_delaunay(std::list & first_hole, OutputItFaces fit) +fill_hole_delaunay(std::list & first_hole) { - typedef typename Gt::Side_of_oriented_circle_2 In_circle; - typedef std::list Hole; - typedef std::list Hole_list; - - In_circle in_circle = geom_traits().side_of_oriented_circle_2_object(); - - Face_handle f, ff, fn; - int i, ii, in; - Hole_list hole_list; - hole_list.push_front(first_hole); - - while(!hole_list.empty()) { - Hole& hole = hole_list.front(); - typename Hole::iterator hit = hole.begin(); - - if (hole.size() == 3) { - hit = hole.begin(); - f = (*hit).first; i = (*hit).second; - ff = (* ++hit).first; ii = (*hit).second; - fn = (* ++hit).first; in = (*hit).second; - Face_handle newf = create_face(f,i,ff,ii,fn,in); - *fit++ = newf; - hole_list.pop_front(); - continue; - } - - bool finite= false; - while (!finite){ - ff = (hole.front()).first; - ii = (hole.front()).second; - if ( is_infinite(ff->vertex(cw(ii))) || - is_infinite(ff->vertex(ccw(ii)))) { - hole.push_back(hole.front()); - hole.pop_front(); - } else finite=true; - } - - ff = (hole.front()).first; - ii =(hole.front()).second; - hole.pop_front(); - - Vertex_handle v0 = ff->vertex(cw(ii)); - Vertex_handle v1 = ff->vertex(ccw(ii)); - Vertex_handle v2 = infinite_vertex(); - const Point& p0 = v0->point(); - const Point& p1 = v1->point(); - - typename Hole::iterator hdone = hole.end(); - hit = hole.begin(); - typename Hole::iterator cut_after(hit); - - hdone--; - while( hit != hdone) { - fn = (*hit).first; - in = (*hit).second; - Vertex_handle vv = fn->vertex(ccw(in)); - if (is_infinite(vv)) { - if(is_infinite(v2)) cut_after = hit; - } else { // vv is a finite vertex - const Point & p = vv->point(); - if (orientation(p0,p1,p) == CGAL::COUNTERCLOCKWISE) { - if (is_infinite(v2)) { v2 = vv; cut_after = hit;} - else{ - if (in_circle(p0,p1,v2->point(),p) == CGAL::ON_POSITIVE_SIDE){ - v2 = vv; cut_after = hit; - } - } - } - } - ++hit; - } - - Face_handle newf; - - fn = (hole.front()).first; - in = (hole.front()).second; - if (fn->has_vertex(v2, i) && i == fn->ccw(in)) { - newf = create_face(ff,ii,fn,in); - hole.pop_front(); - hole.push_front(Edge( newf,1)); - } else { - fn = (hole.back()).first; - in = (hole.back()).second; - if (fn->has_vertex(v2, i) && i== fn->cw(in)) { - newf = create_face(fn,in,ff,ii); - hole.pop_back(); - hole.push_back(Edge(newf,1)); - } else { - newf = create_face(ff,ii,v2); - Hole new_hole; - ++cut_after; - while( hole.begin() != cut_after ) { - new_hole.push_back(hole.front()); - hole.pop_front(); - } - hole.push_front(Edge(newf, 1)); - new_hole.push_front(Edge(newf, 0)); - hole_list.push_front(new_hole); - } - } - *fit++ = newf; - } + CGAL::fill_hole_delaunay(*this, first_hole, CGAL::Emptyset_iterator{}); } template diff --git a/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h b/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h index 2d0a2fc9c4cc..446c5b61f458 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h @@ -578,6 +578,9 @@ class Polyline_constraint_hierarchy_2 // Query bool is_subconstraint(T va, T vb) const; + bool is_valid_constraint(Constraint_id cid) const { + return priv.constraints_set.find(cid) != priv.constraints_set.end(); + } Vertex_it vertices_in_constraint_begin(Constraint_id cid) const { return cid.begin(); } diff --git a/Triangulation_3/include/CGAL/Triangulation_3.h b/Triangulation_3/include/CGAL/Triangulation_3.h index 58aea9d0c9dd..5321eed713fd 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3.h +++ b/Triangulation_3/include/CGAL/Triangulation_3.h @@ -1660,16 +1660,48 @@ class Triangulation_3 } protected: - typedef Facet Edge_2D; - typedef std::array Vertex_triple; - typedef typename Base::template Vertex_triple_Facet_map_generator< - Vertex_triple, Facet>::type Vertex_triple_Facet_map; - typedef typename Base::template Vertex_handle_unique_hash_map_generator< - Vertex_handle>::type Vertex_handle_unique_hash_map; + using Edge_2D = Facet; + using Vertex_triple = std::array; + + using Vertex_triple_Facet_map = typename Base::template Vertex_triple_Facet_map_generator::type; + using Vertex_handle_unique_hash_map = typename Base::template Vertex_handle_unique_hash_map_generator::type; static Vertex_triple make_vertex_triple(const Facet& f); static void make_canonical_oriented_triple(Vertex_triple& t); + static Vertex_triple make_canonical_oriented_triple(Vertex_triple&& t) { + make_canonical_oriented_triple(t); + return t; + } + + static Vertex_triple make_canonical_oriented_triple(const Facet& f) { + Vertex_triple t = make_vertex_triple(f); + make_canonical_oriented_triple(t); + return t; + } + + static Vertex_triple make_canonical_oriented_triple(Vertex_handle v0, Vertex_handle v1, Vertex_handle v2) { + Vertex_triple t{v0, v1, v2}; + make_canonical_oriented_triple(t); + return t; + } + + template + static Vertex_triple apply_map(Map& m, const Vertex_triple& t) { + return Vertex_triple{m[t[0]], m[t[1]], m[t[2]]}; + } + + template + static Vertex_triple apply_functor(Functor& f, const Triple& t) { + const auto& [t0, t1, t2] = t; + return Vertex_triple{f(t0), f(t1), f(t2)}; + } + + template + static Vertex_triple make_canonical_oriented_mapped_triple(Map& m, const Facet& f) { + return make_canonical_oriented_triple(apply_map(m, make_vertex_triple(f))); + } + template < class VertexRemover > VertexRemover& make_hole_2D(Vertex_handle v, std::list& hole, VertexRemover& remover); @@ -4959,19 +4991,17 @@ create_hole_outer_map(Vertex_handle v, const std::vector& incident_ CGAL_expensive_precondition(! test_dim_down(v)); Vertex_triple_Facet_map outer_map; - for(auto cit = incident_cells.begin(), end = incident_cells.end(); - cit != end; ++cit) - { - int indv = (*cit)->index(v); - Cell_handle opp_cit = (*cit)->neighbor(indv); - Facet f(opp_cit, opp_cit->index(*cit)); - Vertex_triple vt = make_vertex_triple(f); - make_canonical_oriented_triple(vt); + for(auto ch: incident_cells) + { + int indv = ch->index(v); + Cell_handle opp_cit = ch->neighbor(indv); + Facet f(opp_cit, opp_cit->index(ch)); + Vertex_triple vt = make_canonical_oriented_triple(f); outer_map[vt] = f; for(int i=0; i<4; i++) { if(i != indv) - (*cit)->vertex(i)->set_cell(opp_cit); + ch->vertex(i)->set_cell(opp_cit); } } return outer_map; @@ -4988,9 +5018,10 @@ create_triangulation_inner_map(const Triangulation& t, auto create_triangulation_inner_map_aux = [&](const auto& cells_range) { for(auto ch : cells_range) { for(unsigned int index = 0; index < 4; index++) { - Facet f = std::pair(ch, index); - Vertex_triple vt_aux = make_vertex_triple(f); - Vertex_triple vt{vmap[vt_aux[0]], vmap[vt_aux[2]], vmap[vt_aux[1]]}; + const Facet f{ch, index}; + Vertex_triple vt = apply_map(vmap, make_vertex_triple(f)); + using std::swap; + swap(vt[1], vt[2]); // reverse the orientation of the triplet make_canonical_oriented_triple(vt); inner_map[vt] = f; } @@ -5136,8 +5167,7 @@ copy_triangulation_into_hole(const Vertex_handle_unique_hash_map& vmap, if(index != i_i) { Facet f{new_ch, index}; - Vertex_triple vt = make_vertex_triple(f); - make_canonical_oriented_triple(vt); + Vertex_triple vt = make_canonical_oriented_triple(f); std::swap(vt[1], vt[2]); typename Vertex_triple_Facet_map::iterator oit2 = outer_map.find(vt); @@ -6112,13 +6142,7 @@ _make_big_hole_3D(Vertex_handle v, vstates[v0] = PROCESSED; } - int i1 = vertex_triple_index(k, 0); - int i2 = vertex_triple_index(k, 1); - int i3 = vertex_triple_index(k, 2); - - Vertex_handle v1 = c->vertex(i1); - Vertex_handle v2 = c->vertex(i2); - Vertex_handle v3 = c->vertex(i3); + auto [v1, v2, v3] = this->vertices(Facet{c, k}); Cell_handle opp_cit = c->neighbor(k); int opp_i = tds().mirror_index(c, k); @@ -6161,8 +6185,7 @@ _make_big_hole_3D(Vertex_handle v, } Facet f(opp_cit, opp_i); - Vertex_triple vt = make_vertex_triple(f); - make_canonical_oriented_triple(vt); + Vertex_triple vt = make_canonical_oriented_triple(f); outer_map[vt] = f; v1->set_cell(opp_cit); v2->set_cell(opp_cit);