From 1ce108778fbc16fab8cd731e206ceeed4af3ab14 Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 22 Jan 2025 15:10:10 +0100 Subject: [PATCH] rewrite/refactor a lot of the code --- STL_Extension/include/CGAL/Has_timestamp.h | 3 + Stream_support/include/CGAL/IO/io.h | 13 + Stream_support/include/CGAL/IO/io_tags.h | 2 +- .../CGAL/Constrained_triangulation_plus_2.h | 146 +++---- .../Polyline_constraint_hierarchy_2.h | 390 +++++++++--------- .../test/Triangulation_2/issue_4025.cpp | 143 ++++--- 6 files changed, 363 insertions(+), 334 deletions(-) diff --git a/STL_Extension/include/CGAL/Has_timestamp.h b/STL_Extension/include/CGAL/Has_timestamp.h index ad608a4f6b6..3fe442576cc 100644 --- a/STL_Extension/include/CGAL/Has_timestamp.h +++ b/STL_Extension/include/CGAL/Has_timestamp.h @@ -36,6 +36,9 @@ namespace internal { // when T does not have a Has_timestamp tag {}; + template + constexpr bool has_timestamp_v = Has_timestamp::value; + } // end namespace internal } // end namespace CGAL diff --git a/Stream_support/include/CGAL/IO/io.h b/Stream_support/include/CGAL/IO/io.h index 3f8202d8325..631dfb6e5b7 100644 --- a/Stream_support/include/CGAL/IO/io.h +++ b/Stream_support/include/CGAL/IO/io.h @@ -216,6 +216,19 @@ public: } }; +template +class Output_rep +{ + Func f; + +public: + Output_rep(Func f) : f(f) {} + std::ostream& operator()(std::ostream& os) const + { + return f(os); + } +}; + /*! \relates Output_rep \brief stream output of the \c Output_rep calls its \c operator(). diff --git a/Stream_support/include/CGAL/IO/io_tags.h b/Stream_support/include/CGAL/IO/io_tags.h index 0d9f53a4ef7..2be29669531 100644 --- a/Stream_support/include/CGAL/IO/io_tags.h +++ b/Stream_support/include/CGAL/IO/io_tags.h @@ -51,7 +51,7 @@ template<> struct Io_traits { typedef io_Read_write Io_tag; }; template<> struct Io_traits { typedef io_Read_write Io_tag; }; template<> struct Io_traits { typedef io_Read_write Io_tag; }; - +struct IO_manip_tag{}; } //namespace CGAL diff --git a/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h b/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h index 1d4cd0dcbfb..043320f6689 100644 --- a/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h +++ b/Triangulation_2/include/CGAL/Constrained_triangulation_plus_2.h @@ -378,57 +378,69 @@ public: } + + // Removes pos from the constraint cid. + // Returns the iterator to vertex that was just after pos (or end()) + // Writes the modified faces to out template Vertices_in_constraint_iterator remove_vertex_from_constraint(Constraint_id cid, Vertices_in_constraint_iterator pos, OutputIterator out) { if(pos == vertices_in_constraint_begin(cid)){ - ++pos; - Constraint_id aux = hierarchy.split2(cid,pos); + // cid is [P, A, ..., B] -> split to aux=[P, A] and cid=[A...B] + Constraint_id aux = hierarchy.split2(cid, std::next(pos)); remove_constraint(aux, out); - return pos; + return vertices_in_constraint_begin(cid); } - Vertices_in_constraint_iterator it = vertices_in_constraint_end(cid); - it--; - if(pos == it){ - --pos; - Constraint_id aux = hierarchy.split(cid, pos); + if(pos == std::prev(vertices_in_constraint_end(cid))){ + // cid is [A, ..., B, P] -> split to cid=[A...B] and aux=[B,P] + Constraint_id aux = hierarchy.split(cid, std::prev(pos)); remove_constraint(aux, out); return vertices_in_constraint_end(cid); } - Vertices_in_constraint_iterator pp = pos; - --pp; - Vertex_handle a = *pp; - pp = pos; - ++pp; - Vertex_handle b = *pp; - --it; - Vertices_in_constraint_iterator beg = vertices_in_constraint_begin(cid); - ++beg; - Face_container fc(*this); + const auto second_vertex_of_cid = std::next(vertices_in_constraint_begin(cid)); + const auto next_to_last_vertex_of_cid = std::prev(vertices_in_constraint_end(cid), 2); Constraint_id head = nullptr, tail = nullptr; - if(pos != beg){ + if(pos != second_vertex_of_cid){ + // cid is [A, ..., B, P, C, ..., D] + // split to: + // head = [A...B] and, + // cid = [B, P, C...D] // split off head - --pos; - head = hierarchy.split2(cid, pos); - ++pos; + head = hierarchy.split2(cid, std::prev(pos)); } - if(pos != it){ + if(pos != next_to_last_vertex_of_cid){ + // cid is now [B, P, C, ..., D] + // split to: + // cid = [B, P, C] and, + // tail = [C...D] // split off tail - ++pos; - tail = hierarchy.split(cid,pos); + tail = hierarchy.split(cid,std::next(pos)); } - Constraint_id aux = insert_constraint(a, b, std::back_inserter(fc)); - pos = vertices_in_constraint_end(aux); - --pos; - --pos; // this is not necessarily == vertices_in_constraint_begin(aux); + // now: + // cid is [B, P, C] + // head is null or [A...B] + // tail is null or [C...D] + // Let create insert [C,D] and conditionnaly concatenate head and tail, + // and return the iterator to C + + Vertex_handle b = *std::prev(pos); + Vertex_handle c = *std::next(pos); + + Face_container fc(*this); + Constraint_id aux = insert_constraint(b, c, std::back_inserter(fc)); + + auto pos_before_c = std::prev(vertices_in_constraint_end(aux), 2); + // `pos_before_c` is not necessarily == vertices_in_constraint_begin(aux) + // there might have been intersecting constraints + hierarchy.swap(cid, aux); - remove_constraint(aux, std::back_inserter(fc)); + remove_constraint(aux, std::back_inserter(fc)); // removes [B, P, C] if(head != nullptr){ hierarchy.concatenate2(head, cid); @@ -438,8 +450,9 @@ public: hierarchy.concatenate(cid, tail); } fc.write_faces(out); - ++pos; // we went one too far back because the last vertex gets removed by concatenate - return pos; + + // we went one too far back because the last vertex `c` gets removed by concatenate + return std::next(pos_before_c); } // Inserts vh before pos @@ -461,61 +474,56 @@ public: // Insertion after the last vertex if(pos == vertices_in_constraint_end(cid)){ //std::cout << "insertion after last vertex" << std::endl; - pos--; - Constraint_id tail = insert_constraint(*pos, vh, out); - pos = vertices_in_constraint_end(tail); - --pos; + Constraint_id tail = insert_constraint(*std::prev(pos), vh, out); + auto returned_pos = std::prev(vertices_in_constraint_end(tail)); hierarchy.concatenate(cid, tail); - return pos; + return returned_pos; } + Vertices_in_constraint_iterator pred = std::prev(pos); + Vertices_in_constraint_iterator last = std::prev(vertices_in_constraint_end(cid)); + Vertex_handle a = *pred; Vertex_handle b = *pos; - --pos; - Vertex_handle a = *pos; - ++pos; + + // cid is [..., A, B, ...] and M=*vh will be inserted between A and B + Face_container fc(*this); - Vertices_in_constraint_iterator beg = vertices_in_constraint_begin(cid), vcit; - ++beg; - vcit = beg; - ++beg; + Constraint_id aux1 = insert_constraint(a, vh, std::back_inserter(fc)); + Constraint_id aux2 = insert_constraint(vh, b, std::back_inserter(fc)); + auto returned_pos = vertices_in_constraint_begin(aux2); + concatenate(aux1, aux2); + // here: + // aux1 is [A, M, B] + // aux2 is empty + // and returned_pos is the iterator to M + + const auto second_vertex_of_cid = std::next(vertices_in_constraint_begin(cid)); // If the constraint consists only of a segment, and we want to insert - // in the middle - if((pos == vcit) && (beg == vertices_in_constraint_end(cid))){ + // in the middle: cid is just the segment [A, B] + if((pos == second_vertex_of_cid) && (second_vertex_of_cid == last)){ //std::cout << "insertion in constraint which is a segment" << std::endl; - Constraint_id aux1 = insert_constraint(a, vh, std::back_inserter(fc)); - Constraint_id aux2 = insert_constraint(vh, b, std::back_inserter(fc)); - pos = vertices_in_constraint_begin(aux2); - concatenate(aux1, aux2); hierarchy.swap(cid, aux1); remove_constraint(aux1, std::back_inserter(fc)); fc.write_faces(out); - return pos; - + return returned_pos; } + Constraint_id head = nullptr, tail = nullptr; - Vertices_in_constraint_iterator bit = vertices_in_constraint_begin(cid); - Vertices_in_constraint_iterator pred = pos; - --pred; - ++bit; - if(pos != bit){ + if(pos != second_vertex_of_cid){ //std::cout << "split head" << std::endl; head = split(cid, pred); std::swap(head,cid); // split2 does the job pred = vertices_in_constraint_begin(cid); - pos = pred; - ++pos; + pos = std::next(pred); } - Vertices_in_constraint_iterator eit = vertices_in_constraint_end(cid); - --eit; - if(pos != eit){ + // head is now [..., A] or null + // cid is now [A, B, ...] + if(pos != last){ //std::cout << "split tail" << std::endl; tail = split(cid, pos); } - - // make the new constraint - Constraint_id aux1 = insert_constraint(a, vh, std::back_inserter(fc)); - Constraint_id aux2 = insert_constraint(vh, b, std::back_inserter(fc)); - pos = vertices_in_constraint_begin(aux2); - concatenate(aux1, aux2); + // head is now [..., A] or null + // cid is now [A, B] + // tail is now [B, ...] or null if(head != nullptr){ //std::cout << "concatenate head" << std::endl; @@ -988,7 +996,7 @@ insert_subconstraint(Vertex_handle vaa, //to debug public: - void print_hierarchy() { hierarchy.print(); } + void print_hierarchy(std::ostream& os = std::cout) { hierarchy.print(os); } //template member functions public: 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 43925c10b52..1f8d85b04ee 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 @@ -28,6 +28,8 @@ #include #include #include +#include +#include #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS # include @@ -62,7 +64,6 @@ private: private: Vertex_handle vertex_; Point point_; - public: bool input_; }; @@ -213,6 +214,7 @@ public: typedef typename Constraints_set::iterator Constraint_iterator; typedef const Constraints_set& Constraints; typedef typename Sc_to_c_map::const_iterator Sc_iterator; + typedef typename Sc_to_c_map::iterator Sc_it; typedef Sc_iterator Subconstraint_and_contexts_iterator; typedef const Sc_to_c_map& Subconstraints_and_contexts; @@ -447,7 +449,53 @@ public: void swap(Polyline_constraint_hierarchy_2& ch); private: + template + void for_context_lists_of_all_subconstraints(Constraint_id cid, const F& f) + { + auto vl = cid.vl_ptr(); + for(Vertex_it it = vl->skip_begin(), succ = it, end = vl->skip_end(); ++succ != end; ++it) { + auto scit = sc_to_c_map.find(sorted_pair(*it, *succ)); + CGAL_assertion(scit != sc_to_c_map.end()); + Context_list* hcl = scit->second; + f(hcl, it, scit); + } + } + + static void replace_first_in_context_list(Context_list* hcl, Constraint_id old_id, Constraint_id new_id) + { + // std::find_if is a sort of std::for_each with a break + [[maybe_unused]] auto it = std::find_if(hcl->begin(), hcl->end(), [&](Context& ctxt) { + if(ctxt.enclosing == old_id) { + ctxt.enclosing = new_id; + return true; + } + return false; + }); + } + + static void update_first_context_position(Context_list* hcl, Constraint_id id, Vertex_it new_pos) + { + [[maybe_unused]] auto it = std::find_if(hcl->begin(), hcl->end(), [&](Context& ctxt) { + if(ctxt.enclosing == id) { + ctxt.pos = new_pos; + return true; + } + return false; + }); + } + + static void remove_first_in_context_list(Context_list* hcl, Constraint_id id) + { + auto it = std::find_if(hcl->begin(), hcl->end(), [&](Context& ctxt) { + return ctxt.enclosing == id; + }); + if(it != hcl->end()) { + hcl->erase(it); + } + } + Constraint_id new_constraint_id() const { + // TODO: handle ids auto id = number_of_constraints() == 0 ? 0 : constraints_set.rbegin()->id + 1; return Constraint_id(new Vertex_list, id); } @@ -464,7 +512,7 @@ private: //to_debug public: - void print() const; + void print(std::ostream& os = std::cout) const; }; template @@ -645,29 +693,18 @@ template void Polyline_constraint_hierarchy_2:: swap(Constraint_id constr_a, Constraint_id constr_b) { - auto substitute_enclosing_in_vertex_list = [this](Vertex_list_ptr vl, Constraint_id old_id, Constraint_id new_id) { - // We have to look at all subconstraints - for(Vertex_it it = vl->skip_begin(), succ = it, end = vl->skip_end(); ++succ != end; ++it) { - typename Sc_to_c_map::iterator scit = this->sc_to_c_map.find(sorted_pair(*it, *succ)); - CGAL_assertion(scit != this->sc_to_c_map.end()); - Context_list* hcl = scit->second; - - // and replace the context of the constraint - for(Context_iterator ctit = hcl->begin(); ctit != hcl->end(); ctit++) { - if(ctit->enclosing == old_id) { - ctit->enclosing = new_id; - break; - } - } - } + auto substitute_enclosing_in_vertex_list = [this](Constraint_id cid, Constraint_id old_id, Constraint_id new_id) { + for_context_lists_of_all_subconstraints(cid, [&](Context_list* hcl, Vertex_it, Sc_it) { + replace_first_in_context_list(hcl, old_id, new_id); + }); }; Vertex_list_ptr constr_a_vl = constr_a.vl_ptr(); Vertex_list_ptr constr_b_vl = constr_b.vl_ptr(); - substitute_enclosing_in_vertex_list(constr_a_vl, constr_a, nullptr); - substitute_enclosing_in_vertex_list(constr_b_vl, constr_b, constr_a); - substitute_enclosing_in_vertex_list(constr_a_vl, nullptr, constr_b); + substitute_enclosing_in_vertex_list(constr_a, constr_a, nullptr); + substitute_enclosing_in_vertex_list(constr_b, constr_b, constr_a); + substitute_enclosing_in_vertex_list(constr_a, nullptr, constr_b); constr_a_vl->swap(*constr_b_vl); } @@ -675,33 +712,22 @@ swap(Constraint_id constr_a, Constraint_id constr_b) { template void Polyline_constraint_hierarchy_2:: -remove_constraint(Constraint_id cid){ +remove_constraint(Constraint_id cid) +{ constraints_set.erase(cid); - // We have to look at all subconstraints - for(Vertex_it it = cid.begin(), succ = it, end = cid.end(); - ++succ != end; - ++it){ - typename Sc_to_c_map::iterator scit = sc_to_c_map.find(sorted_pair(*it,*succ)); - CGAL_assertion(scit != sc_to_c_map.end()); - Context_list* hcl = scit->second; + for_context_lists_of_all_subconstraints(cid, [&](Context_list* hcl, Vertex_it, Sc_it scit) { + remove_first_in_context_list(hcl, cid); - // and remove the context of the constraint - for(Context_iterator ctit=hcl->begin(); ctit != hcl->end(); ctit++) { - if(ctit->enclosing == cid){ - hcl->erase(ctit); - break; - } - } // If the constraint passes several times through the same subconstraint, - // the above loop maybe removes them in the wrong order + // the above call to `remove_first_in_context_list` may remove them in the wrong order. // If this was the only context in the list, delete the context list - if(hcl->empty()){ + if(hcl->empty()) { sc_to_c_map.erase(scit); delete hcl; } - } + }); delete cid.vl_ptr(); } @@ -802,53 +828,31 @@ template typename Polyline_constraint_hierarchy_2::Constraint_id Polyline_constraint_hierarchy_2::concatenate(Constraint_id constr_a, Constraint_id constr_b) { + // constr_a is [A, ..., M] + // constr_b is [M, ..., B] + // we want: + // constr_a = [A, ..., M, ..., B] + // constr_b = [] + // std::cerr << std::format("concatenate({}, {}) ", constr_a.id, constr_b.id) << std::endl; Vertex_list_ptr constr_a_vl = constr_a.vl_ptr(); Vertex_list_ptr constr_b_vl = constr_b.vl_ptr(); - constraints_set.erase(constr_a); constraints_set.erase(constr_b); - // We have to look at all subconstraints - for(Vertex_it it = constr_b_vl->skip_begin(), succ = it, end = constr_b_vl->skip_end(); - ++succ != end; - ++it){ - typename Sc_to_c_map::iterator scit = sc_to_c_map.find(sorted_pair(*it,*succ)); - CGAL_assertion(scit != sc_to_c_map.end()); - Context_list* hcl = scit->second; - // and replace the context of the constraint - for(Context_iterator ctit=hcl->begin(); ctit != hcl->end(); ctit++) { - if(ctit->enclosing == constr_b){ - ctit->enclosing = constr_a; - break; - } - } - } + for_context_lists_of_all_subconstraints(constr_b, [&](Context_list* hcl, Vertex_it, Sc_it) { + replace_first_in_context_list(hcl, constr_b, constr_a); + }); + // now we really concatenate the vertex lists // Note that all iterators pointing into constr_a remain valid. // This concerns user code, as well as the data member "pos" of the Context class + CGAL_assertion(constr_a_vl->back().vertex() == constr_b_vl->front().vertex()); constr_a_vl->pop_back(); // because it is the same as constr_b_vl.front() - Vertex_it back_it = constr_a_vl->skip_end(); - --back_it; - constr_a_vl->splice(constr_a_vl->skip_end(), *(constr_b_vl), constr_b_vl->skip_begin(), constr_b_vl->skip_end()); + constr_a_vl->splice(constr_a_vl->skip_end(), *constr_b_vl, constr_b_vl->skip_begin(), constr_b_vl->skip_end()); - // Note that for VC8 with iterator debugging the iterators pointing into constr_b - // are NOT valid So we have to update them - for(Vertex_it it = back_it, succ = it, end = constr_a_vl->skip_end(); - ++succ != end; - ++it){ - typename Sc_to_c_map::iterator scit = sc_to_c_map.find(sorted_pair(*it,*succ)); - CGAL_assertion(scit != sc_to_c_map.end()); - Context_list* hcl = scit->second; - - // and update pos in the context of the constraint - for(Context_iterator ctit=hcl->begin(); ctit != hcl->end(); ctit++) { - if(ctit->enclosing == constr_a){ - ctit->pos = it; - break; - } - } - } - constraints_set.insert(constr_a); + for_context_lists_of_all_subconstraints(constr_a, [&](Context_list* hcl, Vertex_it it, Sc_it) { + update_first_context_position(hcl, constr_a, it); + }); delete constr_b_vl; return constr_a; @@ -858,54 +862,34 @@ template typename Polyline_constraint_hierarchy_2::Constraint_id Polyline_constraint_hierarchy_2::concatenate2(Constraint_id constr_a, Constraint_id constr_b) { + // constr_a is [A, ..., M] + // constr_b is [M, ..., B] + // we want: + // constr_a = + // constr_b = [A, ..., M, ..., B] + Vertex_list_ptr constr_a_vl = constr_a.vl_ptr(); Vertex_list_ptr constr_b_vl = constr_b.vl_ptr(); - constraints_set.erase(constr_a); - constraints_set.erase(constr_b); - // We have to look at all subconstraints - for(Vertex_it it = constr_a_vl->skip_begin(), succ = it, end = constr_a_vl->skip_end(); - ++succ != end; - ++it){ - typename Sc_to_c_map::iterator scit = sc_to_c_map.find(sorted_pair(*it,*succ)); - CGAL_assertion(scit != sc_to_c_map.end()); - Context_list* hcl = scit->second; + constraints_set.erase(constr_a); // DIFF + + for_context_lists_of_all_subconstraints(constr_a, [&](Context_list* hcl, Vertex_it, Sc_it) { // DIFF + replace_first_in_context_list(hcl, constr_a, constr_b); // DIFF + }); - // and replace the context of the constraint - for(Context_iterator ctit=hcl->begin(); ctit != hcl->end(); ctit++) { - if(ctit->enclosing == constr_a){ - ctit->enclosing = constr_b; - break; - } - } - } // now we really concatenate the vertex lists // Note that all iterators pointing into constr_b remain valid. + CGAL_assertion(constr_a_vl->back().vertex() == constr_b_vl->front().vertex()); constr_a_vl->pop_back(); // because it is the same as constr_b_vl.front() - Vertex_it back_it = constr_b_vl->skip_begin(); - - constr_b_vl->splice(constr_b_vl->skip_begin(), *(constr_a_vl), constr_a_vl->skip_begin(), constr_a_vl->skip_end()); + constr_b_vl->splice(constr_b_vl->skip_begin(), *constr_a_vl, constr_a_vl->skip_begin(), constr_a_vl->skip_end()); // DIFF // Note that for VC8 with iterator debugging the iterators pointing into constr_a // are NOT valid So we have to update them - for(Vertex_it it = constr_b_vl->skip_begin(), succ = it, end = back_it; - ++succ != end; - ++it){ - typename Sc_to_c_map::iterator scit = sc_to_c_map.find(sorted_pair(*it,*succ)); - CGAL_assertion(scit != sc_to_c_map.end()); - Context_list* hcl = scit->second; + for_context_lists_of_all_subconstraints(constr_b /*DIFF*/, [&](Context_list* hcl, Vertex_it it, Sc_it) { + update_first_context_position(hcl, constr_b, it); // DIFF + }); - // and update pos in the context of the constraint - for(Context_iterator ctit=hcl->begin(); ctit != hcl->end(); ctit++) { - if(ctit->enclosing == constr_b){ - ctit->pos = it; - break; - } - } - } - constraints_set.insert(constr_b); - - delete constr_a_vl; - return constr_b; + delete constr_a_vl; // DIFF + return constr_b; // DIFF } @@ -916,35 +900,35 @@ template typename Polyline_constraint_hierarchy_2::Constraint_id Polyline_constraint_hierarchy_2::split(Constraint_id constr, Vertex_it vcit) { - Constraint_id new_constr = new_constraint_id(); - constraints_set.erase(constr); - Vertex_list_ptr new_vl = new_constr.vl_ptr(); - Vertex_list_ptr constr_vl = constr.vl_ptr(); - new_vl->splice(new_vl->skip_end(), *(constr_vl), vcit.base(), constr_vl->skip_end()); - constr_vl->push_back(new_vl->front()); // Duplicate the common vertex - Vertex_it vit = new_vl->skip_begin(); - vit.input() = true; - vit = constr_vl->skip_end(); - --vit; - vit.input() = true; - constraints_set.insert(constr); - constraints_set.insert(new_constr); - // We have to look at all subconstraints - for(Vertex_it it = new_vl->skip_begin(), succ = it, end = new_vl->skip_end(); - ++succ != end; - ++it){ - typename Sc_to_c_map::iterator scit = sc_to_c_map.find(sorted_pair(*it,*succ)); - CGAL_assertion(scit != sc_to_c_map.end()); - Context_list* hcl = scit->second; + // constr is [A, ..., B], vcit points to M in [A, ..., B] + + Constraint_id new_constr = new_constraint_id(); + constraints_set.insert(new_constr); + + Vertex_list_ptr constr_vl = constr.vl_ptr(); + Vertex_list_ptr new_vl = new_constr.vl_ptr(); + + vcit.input() = true; + auto middle_node = Node(*vcit, true); + + // Let's split, that way: + // constr = [A, ..., M] + // new_constr = [M, ..., B] + + // The splice does: + // constr = [A, ..., M[ (without M) + // new_constr = [M, ..., B] + new_vl->splice(new_vl->skip_end(), *constr_vl, vcit.base(), constr_vl->skip_end()); + constr_vl->push_back(middle_node); // add M to the end of constr + + CGAL_assertion(vcit.base() == new_vl->skip_begin()); + CGAL_assertion(new_vl->front().input() == true); + CGAL_assertion(constr_vl->back().input() == true); + + for_context_lists_of_all_subconstraints(new_constr, [&](Context_list* hcl, Vertex_it, Sc_it) { + replace_first_in_context_list(hcl, constr, new_constr); + }); - // and replace the context of the constraint - for(Context_iterator ctit=hcl->begin(); ctit != hcl->end(); ctit++) { - if(ctit->enclosing == constr){ - ctit->enclosing = new_constr; - break; - } - } - } return new_constr; } @@ -952,35 +936,35 @@ template typename Polyline_constraint_hierarchy_2::Constraint_id Polyline_constraint_hierarchy_2::split2(Constraint_id constr, Vertex_it vcit) { - Constraint_id new_constr = new_constraint_id(); - constraints_set.erase(constr); - Vertex_list_ptr new_vl = new_constr.vl_ptr(); - Vertex_list_ptr constr_vl = constr.vl_ptr(); - new_vl->splice(new_vl->skip_end(), *constr_vl, constr_vl->skip_begin(), vcit.base()); - new_vl->push_back(constr_vl->front()); // Duplicate the common vertex - Vertex_it vit = new_vl->skip_end(); - --vit; - vit.input() = true; - vit = constr_vl->skip_begin(); - vit.input() = true; - constraints_set.insert(constr); - constraints_set.insert(new_constr); - // We have to look at all subconstraints - for(Vertex_it it = new_vl->skip_begin(), succ = it, end = new_vl->skip_end(); - ++succ != end; - ++it){ - typename Sc_to_c_map::iterator scit = sc_to_c_map.find(sorted_pair(*it,*succ)); - CGAL_assertion(scit != sc_to_c_map.end()); - Context_list* hcl = scit->second; + // constr is [A, ..., B], vcit points to M in [A, ..., B] + + Constraint_id new_constr = new_constraint_id(); + constraints_set.insert(new_constr); + + Vertex_list_ptr constr_vl = constr.vl_ptr(); + Vertex_list_ptr new_vl = new_constr.vl_ptr(); + + vcit.input() = true; + auto middle_node = Node(*vcit, true); + + // Let's split, that way: + // constr = [M, ..., B] + // new_constr = [A, ..., M] + + // The splice does: + // constr = [M, ..., B] + // new_constr = [A, ..., M[ (without M) + new_vl->splice(new_vl->skip_end(), *constr_vl, constr_vl->skip_begin(), vcit.base()); + new_vl->push_back(middle_node); // add M to the end of new_constr + + CGAL_assertion(vcit.base() == constr_vl->skip_begin()); + CGAL_assertion(constr_vl->front().input() == true); + CGAL_assertion(new_vl->back().input() == true); + + for_context_lists_of_all_subconstraints(new_constr, [&](Context_list* hcl, Vertex_it, Sc_it) { + replace_first_in_context_list(hcl, constr, new_constr); + }); - // and replace the context of the constraint - for(Context_iterator ctit=hcl->begin(); ctit != hcl->end(); ctit++) { - if(ctit->enclosing == constr){ - ctit->enclosing = new_constr; - break; - } - } - } return new_constr; } @@ -1244,9 +1228,9 @@ oriented_end(T va, T vb, T& vc) const template void Polyline_constraint_hierarchy_2:: -print() const +print(std::ostream& os) const { - std::map vertex_num_map; + std::map vertex_num_map; int num = 0; for(const auto& cid : constraints()) { for (const auto& node : cid.elements()){ @@ -1254,51 +1238,51 @@ print() const } } - struct Vertex_num { - std::map& vertex_num_map; - int operator[](T v) const { -#ifndef CGAL_CDT_2_DEBUG_INTERSECTIONS - auto it = vertex_num_map.find(v); - if(it == vertex_num_map.end()) return -1; - return it->second; -#else - return v->time_stamp(); -#endif - } - } vertex_num{vertex_num_map}; -// typename std::map::iterator vnit = vertex_num.begin(); -// for(; vnit != vertex_num.end(); vnit++) { -// vnit->second = ++num; -// std::cerr << "vertex num " << num << " " << vnit->first->point() -// << std::endl; -// } + auto disp_vertex = [&vertex_num_map](Vertex_handle v) { + return CGAL::IO::oformat( + [v, &vertex_num_map](auto& os) -> decltype(os)& { + constexpr bool star_v_has_timestamp = + internal::has_timestamp_v>; + if constexpr(star_v_has_timestamp) { + return os << '#' << v->time_stamp(); + } else { + auto it = vertex_num_map.find(v); + auto n = (it == vertex_num_map.end()) ? -1 : it->second; + return os << n; + } + }, + IO_manip_tag{}); + }; + os << "# number of constraints: " << number_of_constraints() << std::endl; for(const auto& cid : constraints()) { - std::cout << std::endl; - std::cout << "constraint(" << cid.id << ") "; - std::cout << cid.vl_ptr(); - std::cout << " subconstraints "; + os << "constraint(" << cid.id << ") "; + os << cid.vl_ptr(); + os << "\n vertex list "; for(const auto& node : cid.elements()) { - std::cout << vertex_num[node.vertex()] << " "; + os << disp_vertex(node.vertex()) << " "; } - std::cout << ": "; + os << "\n corresponding points: "; for(const auto& node : cid.elements()) { - std::cout << node.point() << " "; + os << node.point() << " "; } + os << std::endl; } - std::cout << std::endl; + os << std::endl; + os << "# number of subconstraints: " << sc_to_c_map.size() << std::endl; for(const auto& subconstraint : subconstraints()) { - std::cout << "subconstraint "; - std::cout << vertex_num[subconstraint.first] << " " << vertex_num[subconstraint.second]; + os << "subconstraint ("; + os << disp_vertex(subconstraint.first) << ", " + << disp_vertex(subconstraint.second) << ")"; Context_iterator cb, ce; get_contexts(subconstraint.first, subconstraint.second, cb, ce); - std::cout << " enclosing "; + os << " enclosing: "; for(; cb != ce; cb++) { - std::cout << "(" << cb->id().id << ") " << cb->id().vl_ptr(); - std::cout << " "; + os << "(cid " << cb->id().id << ") " << cb->id().vl_ptr(); + os << ", pos: " << std::distance(cb->vertices_begin(), cb->pos) << " "; } - std::cout << std::endl; + os << std::endl; } return; } diff --git a/Triangulation_2/test/Triangulation_2/issue_4025.cpp b/Triangulation_2/test/Triangulation_2/issue_4025.cpp index b56894bd16c..41a39a4a142 100644 --- a/Triangulation_2/test/Triangulation_2/issue_4025.cpp +++ b/Triangulation_2/test/Triangulation_2/issue_4025.cpp @@ -1,89 +1,110 @@ -#include -#include +// This code was first submitted in an issue: +// https://github.com/CGAL/cgal/issues/4025 +// and then rewrote a lot, keeping the observed behavior. + +#include #include #include +#include -typedef CGAL::Exact_predicates_inexact_constructions_kernel K; -typedef CGAL::Polygon_2 Polygon_2; -typedef CGAL::Exact_intersections_tag Itag_; -typedef CGAL::Constrained_Delaunay_triangulation_2 CDT; -typedef CGAL::Constrained_triangulation_plus_2 CDTP; +#include +#include +#include -typedef CDTP::Point Point; -typedef CDTP::Constraint_id Cid; -typedef CDTP::Vertex_handle Vertex_handle; -typedef CDTP::Constraint_id Constraint_id; -typedef CDTP::Vertices_in_constraint_iterator Vertices_in_constraint_iterator; +using K = CGAL::Exact_predicates_inexact_constructions_kernel; +using Polygon_2 = CGAL::Polygon_2; +using Itag_ = CGAL::Exact_intersections_tag; +using Vb = CGAL::Base_with_time_stamp>; +using Cb = CGAL::Base_with_time_stamp>; +using Tds = CGAL::Triangulation_data_structure_2; +using CDT = CGAL::Constrained_Delaunay_triangulation_2; +using CDTP = CGAL::Constrained_triangulation_plus_2; -int countVertex(CDTP &cdtp, CDTP::Constraint_id id) +using Point = CDTP::Point; +using Vertex_handle = CDTP::Vertex_handle; +using Constraint_id = CDTP::Constraint_id; +using Vertices_in_constraint_iterator = CDTP::Vertices_in_constraint_iterator; + +auto nb_of_vertices(CDTP &cdtp, Constraint_id id) { - Vertices_in_constraint_iterator v=cdtp.vertices_in_constraint_begin(id); - - int count=0; - while(v!=cdtp.vertices_in_constraint_end(id)) - { - count++; - v++; - } - - return count; + return static_cast(std::distance(cdtp.vertices_in_constraint_begin(id), + cdtp.vertices_in_constraint_end(id))); } +auto value_check_expected = + [](auto&& value, [[maybe_unused]] const auto& expected) -> decltype(auto) + { + assert(value == expected); + return std::forward(value); + }; + +auto oformat = + [](Vertex_handle vh) + { + return CGAL::IO::oformat(vh, CGAL::With_point_tag{}); + }; int main() { CDTP cdtp; - std::list pointsListCollinear; + auto print_cdtp = [&cdtp](std::string_view msg) + { + std::cout << msg << std::endl; + cdtp.print_hierarchy(std::cout); + }; - pointsListCollinear.push_back(Point(0,0)); - pointsListCollinear.push_back(Point(0,1)); - pointsListCollinear.push_back(Point(0,2)); - pointsListCollinear.push_back(Point(0,3)); - pointsListCollinear.push_back(Point(0,4)); - pointsListCollinear.push_back(Point(0,5)); + const std::array collinear_points = { + Point(0,0), Point(0,1), Point(0,2), Point(0,3), Point(0,4), Point(0,5) + }; - std::list pointsListNoCollinear; + const std::array non_collinear_points = { + Point(1,0), Point(2,1), Point(4,2), Point(2,3), Point(4,4), Point(1,5) + }; - pointsListNoCollinear.push_back(Point(1,0)); - pointsListNoCollinear.push_back(Point(2,1)); - pointsListNoCollinear.push_back(Point(4,2)); - pointsListNoCollinear.push_back(Point(2,3)); - pointsListNoCollinear.push_back(Point(4,4)); - pointsListNoCollinear.push_back(Point(1,5)); + Constraint_id collinear_cid = cdtp.insert_constraint(collinear_points.begin(), collinear_points.end()); + Constraint_id non_collinear_cid = cdtp.insert_constraint(non_collinear_points.begin(), non_collinear_points.end()); + print_cdtp("Initial state"); - Constraint_id ctIdCollinear=cdtp.insert_constraint(pointsListCollinear.begin(),pointsListCollinear.end()); - Constraint_id ctIdNoCollinear=cdtp.insert_constraint(pointsListNoCollinear.begin(),pointsListNoCollinear.end()); + Vertices_in_constraint_iterator vertex_it = std::next(cdtp.vertices_in_constraint_begin(collinear_cid), 2); + [[maybe_unused]] auto next_it = std::next(vertex_it); + std::cout << "\n-> attempt to remove vertex " << oformat(*vertex_it) << std::endl; + vertex_it = cdtp.remove_vertex_from_constraint(collinear_cid, vertex_it); + std::cout << " cdtp.remove_vertex_from_constraint(collinear_cid, vertex_it) returned the vertex " + << oformat(*vertex_it) << std::endl; + assert(vertex_it == next_it); + print_cdtp("\nAfter removing third vertex from the collinear constraint"); - //******************************* attempt with the collinear constraint - Vertices_in_constraint_iterator vertexToRemoveCollinear=cdtp.vertices_in_constraint_begin(ctIdCollinear); - vertexToRemoveCollinear++; - vertexToRemoveCollinear++; + // The first constraint (ID `collinear_cid`) is collinear. `cdtp.remove_vertex_from_constraint` + // cannot remove the third vertex from it, because it is collinear with the triangulation vertex + // with the point (0, 2). + std::cout << "\nnumber of subconstraints: " + << value_check_expected(cdtp.number_of_subconstraints(), 10U) << std::endl; + std::cout << "number of constraints: " + << value_check_expected(cdtp.number_of_constraints(), 2U) << std::endl; + std::cout << "number of vertex in collinear constraint: " + << value_check_expected(nb_of_vertices(cdtp, collinear_cid), 6U) << std::endl; - std::cout<<"attempt to remove vertex "<<(*vertexToRemoveCollinear)->point().x()<<" , "<<(*vertexToRemoveCollinear)->point().y() < attempt to remove vertex " << oformat(*vertex_it) << std::endl; + vertex_it = cdtp.remove_vertex_from_constraint(non_collinear_cid, vertex_it); + std::cout << " cdtp.remove_vertex_from_constraint(non_collinear_cid, vertex_it) returned the vertex " + << oformat(*vertex_it) << std::endl; + assert(vertex_it == next_it); - std::cout<<"number of subconstraints "< 5, expected 4 - std::cout<<"number of constraints "< 1 - std::cout<<"number of vertex in constraint "< 6, expected 5 + print_cdtp("\nAfter removing third vertex from the non-collinear constraint"); - - //******************************* attempt with the collinear constraint - Vertices_in_constraint_iterator vertexToRemoveNoCollinear=cdtp.vertices_in_constraint_begin(ctIdNoCollinear); - vertexToRemoveNoCollinear++; - vertexToRemoveNoCollinear++; - - std::cout<<"attempt to remove vertex "<<(*vertexToRemoveNoCollinear)->point().x()<<" , "<<(*vertexToRemoveNoCollinear)->point().y() << std::endl; - cdtp.remove_vertex_from_constraint(ctIdNoCollinear,vertexToRemoveNoCollinear); - - std::cout<<"number of subconstraints "< 4, ok - std::cout<<"number of constraints "< 1 - std::cout<<"number of vertex in constraint "< 5, ok + std::cout << "number of subconstraints: " + << value_check_expected(cdtp.number_of_subconstraints(), 9U) << std::endl; + std::cout << "number of constraints: " + << value_check_expected(cdtp.number_of_constraints(), 2U) << std::endl; + std::cout << "number of vertex in collinear constraint: " + << value_check_expected(nb_of_vertices(cdtp, non_collinear_cid), 5U) << std::endl; return 0; - }