// Copyright (c) 2012 INRIA Sophia-Antipolis (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). // // $URL$ // $Id$ // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // // Author(s) : Andreas Fabri, Mariette Yvinec #ifndef CGAL_CONSTRAINED_TRIANGULATION_PLUS_2_H #define CGAL_CONSTRAINED_TRIANGULATION_PLUS_2_H #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CGAL { // Comparison functor that compares two Vertex_handle. // Used as 'Compare' functor for the constraint hierarchy. template < class Tr > class Ctp_2_compare_vertex_handles { const Tr* tr_p; public: Ctp_2_compare_vertex_handles(const Tr* tr_p) : tr_p(tr_p) {} using Vertex_handle = typename Tr::Vertex_handle; bool operator()(const Vertex_handle& va, const Vertex_handle& vb) const { return tr_p->compare_xy(va->point(), vb->point()) == SMALLER; } }; // end class template Ctp_2_compare_vertex_handles template using Ctp_2_point_type = typename Tr::Geom_traits::Point_2; template using Ctp_2_hierarchy_type = Polyline_constraint_hierarchy_2, Ctp_2_point_type>; // Tr the base triangulation class // Tr has to be Constrained or Constrained_Delaunay with Constrained_triangulation_plus_vertex_base template < class Tr_> class Constrained_triangulation_plus_2 : public Tr_ , protected Ctp_2_hierarchy_type { public: using Self = Constrained_triangulation_plus_2; using Base = Tr_; using Constraint_hierarchy = Ctp_2_hierarchy_type; static_assert(CGAL::is_nothrow_movable_v); protected: const auto& hierarchy() const { return static_cast(*this); } auto& hierarchy() { return static_cast(*this); } private: using Tr = Tr_; template class Face_container { using Vertex_handle = typename CDT::Vertex_handle; using Face_handle = typename CDT::Face_handle; using Array = std::array; std::vector faces; CDT& cdt; public: using value_type = Face_handle; Face_container(CDT& cdt_ ) : cdt(cdt_) {} void push_back(Face_handle fh) { faces.push_back(Array{fh->vertex(0), fh->vertex(1), fh->vertex(2)}); } template void write_faces(OutputIterator out) { for(auto [v0, v1, v2] : make_range(faces.rbegin(), faces.rend())) { Face_handle fh; if(cdt.is_face(v0, v1, v2, fh)) { *out++ = fh; } } } }; public: // type aliases (aka type defs) using Triangulation = Tr; using Intersection_tag = typename Tr::Intersection_tag; // using-declarations of types or member functions from the two bases using Triangulation::vertices_begin; using Triangulation::vertices_end; using Triangulation::is_infinite; using Triangulation::number_of_vertices; using typename Triangulation::Edge; using typename Triangulation::Vertex; #if defined(BOOST_MSVC) && (BOOST_MSVC < 1920) using Vertex_handle = typename Triangulation::Vertex_handle; // workaround for VC++ 19.16 (from MSVC 2017) #else using typename Triangulation::Vertex_handle; #endif using typename Triangulation::Face_handle; using typename Triangulation::Face_circulator; using typename Triangulation::Vertex_iterator; using typename Triangulation::Vertex_circulator; using typename Triangulation::Locate_type; using typename Triangulation::Line_face_circulator; using typename Triangulation::Geom_traits; using typename Triangulation::Constraint; using typename Triangulation::size_type; using typename Triangulation::List_edges; using typename Triangulation::List_faces; using typename Triangulation::List_vertices; using typename Triangulation::List_constraints; using typename Triangulation::Constrained_edges_iterator; using typename Constraint_hierarchy::Context; using typename Constraint_hierarchy::Context_iterator; using typename Constraint_hierarchy::Contexts; using typename Constraint_hierarchy::Constraint_iterator; using typename Constraint_hierarchy::Constraints; using typename Constraint_hierarchy::Subconstraint_iterator; using typename Constraint_hierarchy::Subconstraints; using typename Constraint_hierarchy::Subconstraint_and_contexts_iterator; using typename Constraint_hierarchy::Subconstraints_and_contexts; using typename Constraint_hierarchy::Constraint_id; using typename Constraint_hierarchy::Vertex_handle_compare; #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS using Triangulation::display_vertex; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS using Point = typename Triangulation::Geom_traits::Point_2; using Segment = typename Triangulation::Geom_traits::Segment_2; // Tag to mark the presence of a hierarchy of constraints using Constraint_hierarchy_tag = Tag_true; //Tag to distinguish Delaunay from regular triangulations using Weighted_tag = Tag_false; // Tag to distinguish periodic triangulations from others using Periodic_tag = Tag_false; // for user interface with the constraint hierarchy using Vertices_in_constraint_iterator = typename Constraint_hierarchy::Vertex_it; using Vertices_in_constraint = Iterator_range; using Points_in_constraint_iterator = typename Constraint_hierarchy::Point_it; using Points_in_constraint = Iterator_range; using Subconstraint = std::pair; using Triangulation::geom_traits; using Triangulation::cw; using Triangulation::ccw; using Triangulation::incident_faces; public: Constraint_hierarchy& hierarchy_ref() { return *this; } Constrained_triangulation_plus_2(const Geom_traits& gt=Geom_traits()) : Triangulation(gt) , Constraint_hierarchy(Vertex_handle_compare(this)) { } Constrained_triangulation_plus_2(const Constrained_triangulation_plus_2& ctp) : Constrained_triangulation_plus_2(ctp.geom_traits()) { copy_triangulation(ctp);} Constrained_triangulation_plus_2(Constrained_triangulation_plus_2&& other_ctp) noexcept : Triangulation(std::move(other_ctp)) , Constraint_hierarchy(std::move(other_ctp.hierarchy_ref()), Vertex_handle_compare(this)) { // The hierarchy is moved, so the vertex handles are still valid. // The triangulation is moved, so the vertex handles are still valid. } ~Constrained_triangulation_plus_2() override {} Constrained_triangulation_plus_2 & operator=(const Constrained_triangulation_plus_2& ctp) { copy_triangulation(ctp); return *this; } Constrained_triangulation_plus_2& operator=(Constrained_triangulation_plus_2&& other_ctp) noexcept { if (this != &other_ctp) { static_cast(*this) = std::move(other_ctp); static_cast(*this) = Constraint_hierarchy(std::move(other_ctp.hierarchy_ref()), Vertex_handle_compare(this)); } return *this; } template Constrained_triangulation_plus_2(InputIterator first, InputIterator last, const Geom_traits& gt=Geom_traits() ) : Constrained_triangulation_plus_2(gt) { insert_constraints(first, last); CGAL_postcondition( this->is_valid() ); } Constrained_triangulation_plus_2(const std::list > &constraints, const Geom_traits& gt=Geom_traits() ) : Constrained_triangulation_plus_2(gt) { insert_constraints(constraints.begin(), constraints.end()); CGAL_postcondition( this->is_valid() ); } //Helping void clear() { Base::clear(); hierarchy().clear(); } void copy_triangulation(const Constrained_triangulation_plus_2 &ctp); void swap(Constrained_triangulation_plus_2 &ctp); // INSERTION Vertex_handle insert(const Point& a, Face_handle start = Face_handle() ); Vertex_handle insert(const Point& p, Locate_type lt, Face_handle loc, int li ); Constraint_id insert_constraint(const Point& a, const Point& b) { Vertex_handle va= insert(a); // If the segment is "short" it is a good idea to start the next insertion // close to point a // Otherwise, to start here is as good as elsewhere Vertex_handle vb = insert(b, va->face()); return insert_constraint(va, vb); } Constraint_id insert_constraint(const Constraint& c) { return insert_constraint(c.first, c.second); } Constraint_id insert_constraint(Vertex_handle va, Vertex_handle vb) { #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::insert_constraint( " << display_vertex(va) << " , " << display_vertex(vb) << " )\n"; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS // protects against inserting a zero length constraint if(va == vb){ return Constraint_id(nullptr); } // protects against inserting twice the same constraint Constraint_id cid = hierarchy().insert_constraint_old_API(va, vb); if (va != vb && (cid != Constraint_id(nullptr)) ) insert_subconstraint(va,vb); return cid; } template < class InputIterator> Constraint_id insert_constraint(InputIterator first, InputIterator last, bool close=false) { return insert_constraint_seq_impl(first, last, close); } template Constraint_id insert_constraint(const Range& r) { return insert_constraint_seq_impl(r.begin(), r.end(), false); } template < class PolygonTraits_2, class Container> Constraint_id insert_constraint(const Polygon_2& polygon) { return insert_constraint_seq_impl(polygon.vertices_begin(), polygon.vertices_end(), true); } /* template size_type insert_constraints(InputIterator first, InputIterator last) { size_type n = 0; for(; first != last; ++first) { if(insert_constraint(*first)) ++n; } return n; } */ void split_subconstraint_graph_into_constraints(const std::function& is_terminal = std::function()) { internal::CTP2_graph_visitor visitor(*this); if (is_terminal) CGAL::split_graph_into_polylines (internal::CTP2_subconstraint_graph(*this), visitor, [&is_terminal](Vertex_handle vh, const internal::CTP2_subconstraint_graph&) -> bool { return is_terminal(vh); }); else CGAL::split_graph_into_polylines (internal::CTP2_subconstraint_graph(*this), visitor); } Vertex_handle push_back(const Point& p) { return insert(p); } Constraint_id push_back(const Constraint& c) { return insert_constraint(c.first, c.second); } // for backward compatibility // not const Point&, because otherwise VC6/7 messes it up with // the insert that takes an iterator range Constraint_id insert(Point a, Point b) { return insert_constraint(a, b); } Constraint_id insert(Vertex_handle va, Vertex_handle vb) { return insert_constraint(va,vb); } template std::size_t insert_constraints(PointIterator points_first, PointIterator points_beyond, IndicesIterator indices_first, IndicesIterator indices_beyond) { std::vector points(points_first, points_beyond); return internal::insert_constraints(*this,points, indices_first, indices_beyond); } template std::size_t insert_constraints(ConstraintIterator first, ConstraintIterator beyond) { return internal::insert_constraints(*this,first,beyond); } Vertices_in_constraint_iterator insert_vertex_in_constraint(Constraint_id cid, Vertices_in_constraint_iterator pos, Vertex_handle vh) { return insert_vertex_in_constraint(cid, pos, vh, Emptyset_iterator()); } Vertices_in_constraint_iterator remove_vertex_from_constraint(Constraint_id cid, Vertices_in_constraint_iterator pos) { return remove_vertex_from_constraint(cid, pos, Emptyset_iterator()); } // 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)){ // cid is [P, A, ..., B] -> split to aux=[P, A] and cid=[A...B] Constraint_id aux = hierarchy().split_head(cid, std::next(pos)); remove_constraint(aux, out); return vertices_in_constraint_begin(cid); } 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_tail(cid, std::prev(pos)); remove_constraint(aux, out); return vertices_in_constraint_end(cid); } 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 != 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 head = hierarchy().split_head(cid, std::prev(pos)); } 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 tail = hierarchy().split_tail(cid,std::next(pos)); } // now: // cid is [B, P, C] // head is null or [A...B] // tail is null or [C...D] // Let create insert [C,D] and conditionally 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 bc = insert_constraint(b, c, std::back_inserter(fc)); auto pos_before_c = std::prev(vertices_in_constraint_end(bc), 2); // `pos_before_c` is not necessarily == vertices_in_constraint_begin(bc) // there might have been intersecting constraints hierarchy().swap(cid, bc); remove_constraint(bc, std::back_inserter(fc)); // removes [B, P, C] // now cid is [B, C] if(head != nullptr){ hierarchy().prepend(std::move(head), cid); } if(tail != nullptr){ hierarchy().concatenate(cid, std::move(tail)); } fc.write_faces(out); // we went one too far back because the last vertex `c` gets removed by concatenate/prepend return std::next(pos_before_c); } // Inserts vh before pos // Returns an iterator pointing on the newly inserted vertex // Writes the modified faces to out template Vertices_in_constraint_iterator insert_vertex_in_constraint(Constraint_id cid, Vertices_in_constraint_iterator pos, Vertex_handle v, OutputIterator out) { // Insertion before the first vertex if(pos == vertices_in_constraint_begin(cid)){ //std::cout << "insertion before first vertex" << std::endl; Constraint_id head = insert_constraint(v, *pos, out); hierarchy().prepend(std::move(head), cid); return vertices_in_constraint_begin(cid); } // Insertion after the last vertex if(pos == vertices_in_constraint_end(cid)){ //std::cout << "insertion after last vertex" << std::endl; Constraint_id tail = insert_constraint(*std::prev(pos), v, out); auto new_pos = std::prev(vertices_in_constraint_end(tail)); hierarchy().concatenate(cid, std::move(tail)); CGAL_assertion(v == *new_pos); return new_pos; } Vertices_in_constraint_iterator pred = std::prev(pos); Vertices_in_constraint_iterator latest_vertex = std::prev(vertices_in_constraint_end(cid)); Vertex_handle a = *pred; Vertex_handle b = *pos; if(v == a || v == b){ return pos; } // cid is [..., A, B, ...] and V=*v will be inserted between A and B Face_container fc(*this); Constraint_id a_v_b = insert_constraint(a, v, std::back_inserter(fc)); Constraint_id aux = insert_constraint(v, b, std::back_inserter(fc)); auto new_pos = vertices_in_constraint_begin(aux); concatenate(a_v_b, std::move(aux)); // here: // a_v_b is [A,.. V,.. B] // aux is empty // and new_pos is the iterator to V CGAL_assertion(v == *new_pos); CGAL_assertion(std::distance(vertices_in_constraint_begin(a_v_b), new_pos) > 0 && std::distance(new_pos, vertices_in_constraint_end(a_v_b)) > 0); // new_pos still points to something in a_v_b. In general a_v_b should only have three vertices, // but there might have been intersectiong constraints or vertices. 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: cid is just the segment [A, B] if((pos == second_vertex_of_cid) && (second_vertex_of_cid == latest_vertex)){ //std::cout << "insertion in constraint which is a segment" << std::endl; hierarchy().swap(cid, a_v_b); remove_constraint(a_v_b, std::back_inserter(fc)); fc.write_faces(out); return new_pos; } Constraint_id head = nullptr, tail = nullptr; if(pos != second_vertex_of_cid){ //std::cout << "split head" << std::endl; head = hierarchy().split_head(cid, pred); pred = vertices_in_constraint_begin(cid); pos = std::next(pred); } // head is now [..., A] or null // cid is now [A, B, ...] CGAL_assertion(*pred == a); CGAL_assertion(*pos == b); if(pos != latest_vertex){ //std::cout << "split tail" << std::endl; tail = hierarchy().split_tail(cid, pos); } // 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; hierarchy().concatenate(head, std::move(a_v_b)); hierarchy().swap(cid, head); remove_constraint(head, std::back_inserter(fc)); } else { hierarchy().swap(cid, a_v_b); remove_constraint(a_v_b, std::back_inserter(fc)); } // cid is now [..., A, V, B] // head is now null empty // a_v_b is now empty if(tail != nullptr){ //std::cout << "concatenate tail" << std::endl; concatenate(cid, std::move(tail)); } fc.write_faces(out); return new_pos; } template < class InputIterator, class OutputIterator> Constraint_id insert_constraint(InputIterator first, InputIterator last, OutputIterator out) { Face_handle hint; Face_container fc(*this); std::vector vertices; for(;first!= last; first++){ Vertex_handle vh = insert(*first, hint); hint = vh->face(); // no duplicates if(vertices.empty() || (vertices.back() != vh)){ vertices.push_back(vh); } } int n = vertices.size(); if(n == 1){ return nullptr; } Constraint_id ca = hierarchy().insert_constraint(vertices[0],vertices[1]); insert_subconstraint(vertices[0],vertices[1], std::back_inserter(fc)); if(n>2){ for(int j=1; jfixed() = true; // Vertices_in_constraint_iterator end = std::prev(vertices_in_constraint_end(ca)); // end->fixed() = true; fc.write_faces(out); return ca; } private: template < class InputIterator> Constraint_id insert_constraint_seq_impl(InputIterator first, InputIterator last, bool is_polygon) { Face_handle hint; std::vector vertices; for(;first!= last; first++){ Vertex_handle vh = insert(*first, hint); hint = vh->face(); // no duplicates if(vertices.empty() || (vertices.back() != vh)){ vertices.push_back(vh); } } if(is_polygon && (vertices.size()>1) && (vertices.front() != vertices.back())){ vertices.push_back(vertices.front()); } std::size_t n = vertices.size(); if(n == 1){ return Constraint_id{}; } CGAL_assertion(n >= 2); Constraint_id ca = hierarchy().insert_constraint(vertices[0],vertices[1]); insert_subconstraint(vertices[0],vertices[1]); if(n>2){ for(std::size_t j=1; jfixed() = true; // vertices.back()->fixed() = true; return ca; } public: auto& file_output(std::ostream& os) const { os << static_cast(*this); Unique_hash_map V(0, number_of_vertices()); int inum = 0; for(auto vh : this->finite_vertex_handles()) { V[vh] = inum++; } for(auto cid : constraints()) { os << cid.size(); for(Vertex_handle vh : vertices_in_constraint(cid)) { os << " " << V[vh]; } os << std::endl; } return os; } friend std::ostream& operator<<(std::ostream& os, const Constrained_triangulation_plus_2& ctp) { return ctp.file_output(os); } auto& file_input(std::istream& is) { is >> static_cast(*this); std::vector vertices(number_of_vertices()); auto [first, last] = this->finite_vertex_handles(); std::copy(first, last, vertices.data()); size_type n, id, id_next; while(is >> n) { is >> id >> id_next; Constraint_id cid = insert_constraint(vertices[id], vertices[id_next]); for(size_type i = 2; i < n; ++i) { id = id_next; is >> id_next; Constraint_id cid2 = insert_constraint(vertices[id], vertices[id_next]); cid = concatenate(cid, std::move(cid2)); } } return is; } friend std::istream& operator>>(std::istream& is, Constrained_triangulation_plus_2& ctp) { return ctp.file_input(is); } template typename Constrained_triangulation_plus_2::Constraint_id insert_constraint(Vertex_handle va, Vertex_handle vb, OutputIterator out) { // protects against inserting a zero length constraint if(va == vb){ return Constraint_id(); } // protects against inserting twice the same constraint Constraint_id cid = hierarchy().insert_constraint(va, vb); if (va != vb && (cid != nullptr) ) insert_subconstraint(va,vb,out); for(auto vh : vertices_in_constraint(cid)) { out = insert_incident_faces(vh, out); } return cid; } Vertex_handle intersect(Face_handle f, int i, Vertex_handle vaa, Vertex_handle vbb) override; Vertex_handle intersect(Face_handle f, int i, Vertex_handle vaa, Vertex_handle vbb, No_constraint_intersection_tag); Vertex_handle intersect(Face_handle f, int i, Vertex_handle vaa, Vertex_handle vbb, No_constraint_intersection_requiring_constructions_tag); Vertex_handle intersect(Face_handle f, int i, Vertex_handle vaa, Vertex_handle vbb, Exact_intersections_tag); Vertex_handle intersect(Face_handle f, int i, Vertex_handle vaa, Vertex_handle vbb, Exact_predicates_tag); // REMOVAL template void remove_constraint(Constraint_id cid, OutputIterator out) { std::vector vertices(hierarchy().vertices_in_constraint_begin(cid), hierarchy().vertices_in_constraint_end(cid)); hierarchy().remove_constraint(cid); for(auto it = vertices.begin(), succ = it; ++succ != vertices.end(); ++it){ if(! is_subconstraint(*it, *succ)){ // this checks whether other constraints pass Face_handle fh; int i = -1; Triangulation::is_edge(*it, *succ, fh, i); CGAL_assertion(i != -1); Triangulation::remove_constrained_edge(fh,i, out); // this does also flipping if necessary. } } } void remove_constraint(Constraint_id cid) { remove_constraint(cid, Emptyset_iterator()); } void simplify(Vertices_in_constraint_iterator v) { Vertices_in_constraint_iterator u = std::prev(v); Vertices_in_constraint_iterator w = std::next(v); bool unew = (*u != *w); hierarchy().simplify(u,v,w); Triangulation::remove_incident_constraints(*v); Triangulation::remove(*v); if(unew){ Triangulation::insert_constraint(*u, *w); } } using Constraint_hierarchy::remove_points_without_corresponding_vertex; // CONCATENATE AND SPLIT // concatenates two constraints using Constraint_hierarchy::concatenate; // split a constraint in two constraints, so that vcit becomes the first // vertex of the new constraint // returns the new constraint Constraint_id split(Constraint_id first, Vertices_in_constraint_iterator vcit) { return hierarchy().split_tail(first, vcit); } // Query of the constraint hierarchy using Constraint_hierarchy::constraints_begin; using Constraint_hierarchy::constraints_end; using Constraint_hierarchy::constraints; using Constraint_hierarchy::subconstraints_begin; using Constraint_hierarchy::subconstraints_end; using Constraint_hierarchy::subconstraints; using Constraint_hierarchy::subconstraints_and_contexts_begin; using Constraint_hierarchy::subconstraints_and_contexts_end; using Constraint_hierarchy::subconstraints_and_contexts; using Constraint_hierarchy::context; using Constraint_hierarchy::number_of_enclosing_constraints; using Constraint_hierarchy::is_subconstraint; using Constraint_hierarchy::contexts_begin; using Constraint_hierarchy::contexts_end; using Constraint_hierarchy::contexts; using Constraint_hierarchy::vertices_in_constraint_begin; using Constraint_hierarchy::vertices_in_constraint_end; using Constraint_hierarchy::vertices_in_constraint; using Constraint_hierarchy::points_in_constraint_begin; using Constraint_hierarchy::points_in_constraint_end; Points_in_constraint points_in_constraint(Constraint_id cid) const { return Points_in_constraint(points_in_constraint_begin(cid), points_in_constraint_end(cid)); } using Constraint_hierarchy::number_of_constraints; using Constraint_hierarchy::number_of_subconstraints; using Constraint_hierarchy::split_constraint; protected: template OutputItertator insert_incident_faces(Vertex_handle vh, OutputItertator out) { Face_circulator fc = incident_faces(vh), done = fc; if(fc != nullptr) { do { Face_handle fh = fc; *out++ = fh; } while(++fc != done); } return out; } void insert_subconstraint(Vertex_handle vaa, Vertex_handle vbb) { insert_subconstraint(vaa,vbb,Emptyset_iterator()); } template void insert_subconstraint(Vertex_handle vaa, Vertex_handle vbb, OutputItertator out) // insert the subconstraint [vaa vbb] // it will eventually be split into several subconstraints { #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::insert_subconstraint( " << display_vertex(vaa) << " , " << display_vertex(vbb) << " )\n"; internal::Indentation_level::Exit_guard exit_guard = CGAL::internal::cdt_2_indent_level.open_new_scope(); std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::insert_constraint stack push [va, vb] ( " << display_vertex(vaa) << " , " << display_vertex(vbb) << " )\n"; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS std::stack > stack; stack.push(std::make_pair(vaa,vbb)); while(! stack.empty()){ auto [vaa, vbb] = stack.top(); stack.pop(); CGAL_precondition( vaa != vbb); #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::insert_subconstraint, stack pop=( " << display_vertex(vaa) << " , " << display_vertex(vbb) << " ) remaining stack size: " << stack.size() << '\n'; CGAL_assertion(this->is_valid()); #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS Vertex_handle vi; Face_handle fr; int i; if(this->includes_edge(vaa,vbb,vi,fr,i)) { #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::insert_subconstraint, the segment ( " << display_vertex(vaa) << " , " << display_vertex(vbb) << " ) is an edge with #" << vi->time_stamp() << "= " << vi->point() << '\n'; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS this->mark_constraint(fr,i); if (vi != vbb) { hierarchy().split_constraint(vaa,vbb,vi); #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::insert_constraint (includes_edge) stack push [vi, vbb] ( " << display_vertex(vi) << " , " << display_vertex(vbb) << " )\n"; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS stack.push(std::make_pair(vi,vbb)); } continue; } List_faces intersected_faces; List_edges conflict_boundary_ab, conflict_boundary_ba; bool intersection = this->find_intersected_faces( vaa, vbb, intersected_faces, conflict_boundary_ab, conflict_boundary_ba, vi); if ( intersection) { if (vi != vaa && vi != vbb) { hierarchy().split_constraint(vaa,vbb,vi); #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::insert_constraint stack push [vaa, vi] ( " << display_vertex(vaa) << " , " << display_vertex(vi) << " )\n"; std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::insert_constraint stack push [vi, vbb] ( " << display_vertex(vi) << " , " << display_vertex(vbb) << " )\n"; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS stack.push(std::make_pair(vaa,vi)); stack.push(std::make_pair(vi,vbb)); } else { #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::insert_constraint stack push [vaa, vbb]( " << display_vertex(vaa) << " , " << display_vertex(vbb) << " )\n"; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS stack.push(std::make_pair(vaa,vbb)); } continue; } //no intersection List_edges edges(conflict_boundary_ab); std::copy(conflict_boundary_ba.begin(), conflict_boundary_ba.end(), std::back_inserter(edges)); // edges may contain mirror edges. They no longer exist after triangulate_hole // so we have to remove them before calling get_bounded_faces if(! edges.empty()){ boost::container::flat_set faces(intersected_faces.begin(), intersected_faces.end()); for(typename List_edges::iterator it = edges.begin(); it!= edges.end();){ if(faces.find(it->first) != faces.end()){ typename List_edges::iterator it2 = it; ++it; edges.erase(it2); }else { ++it; } } } this->triangulate_hole(intersected_faces, conflict_boundary_ab, conflict_boundary_ba); this->get_bounded_faces(edges.begin(), edges.end(), out); if (vi != vbb) { hierarchy().split_constraint(vaa,vbb,vi); stack.push(std::make_pair(vi,vbb)); } } } //to debug public: void print_hierarchy(std::ostream& os = std::cout) { hierarchy().print(os); } //template member functions public: template < class InputIterator > #if defined(_MSC_VER) std::ptrdiff_t insert(InputIterator first, InputIterator last, int i = 0) #else std::ptrdiff_t insert(InputIterator first, InputIterator last) #endif { #if defined(_MSC_VER) CGAL_USE(i); #endif size_type n = this->number_of_vertices(); std::vector points (first, last); spatial_sort (points.begin(), points.end(), geom_traits()); Face_handle hint; for (const auto& p : points) hint = insert (p, hint)->face(); return this->number_of_vertices() - n; } }; template void Constrained_triangulation_plus_2:: copy_triangulation(const Constrained_triangulation_plus_2 &ctp) { Base::copy_triangulation(ctp); //the following assumes that the triangulation and its copy // iterate on their vertices in the same order CGAL::unordered_flat_map vmap; Vertex_iterator vit = ctp.vertices_begin(); Vertex_iterator vvit = this->vertices_begin(); for( ; vit != ctp.vertices_end(); ++vit, ++vvit) { CGAL_assertion(vit->point() == vvit->point()); vmap[vit] = vvit; } hierarchy().copy(ctp.hierarchy(), vmap); } template void Constrained_triangulation_plus_2:: swap(Constrained_triangulation_plus_2 &ctp) { Base::swap(ctp); hierarchy().swap(ctp.hierarchy()); } template < class Tr > inline typename Constrained_triangulation_plus_2::Vertex_handle Constrained_triangulation_plus_2:: insert(const Point& a, Face_handle start) { Locate_type lt; int li; Face_handle loc = this->locate(a, lt, li, start); return insert(a,lt,loc,li); } template < class Tr> typename Constrained_triangulation_plus_2::Vertex_handle Constrained_triangulation_plus_2:: insert(const Point& a, Locate_type lt, Face_handle loc, int li) { Vertex_handle v1, v2; bool insert_in_constrained_edge = false; if ( lt == Triangulation::EDGE && loc->is_constrained(li) ) { if(std::is_same::value) throw typename Tr::Intersection_of_constraints_exception(); insert_in_constrained_edge = true; v1=loc->vertex(ccw(li)); //endpoint of the constraint v2=loc->vertex(cw(li)); // endpoint of the constraint } Vertex_handle va = Triangulation::insert(a,lt,loc,li); // update the hierarchy if (insert_in_constrained_edge) { #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << " CT_plus_2::insert(" << a << ") = #" << va->time_stamp() << " insert in constrained edge: #" << v1->time_stamp() << "= " << v1->point() << " , #" << v2->time_stamp() << "= " << v2->point() << std::endl; #endif hierarchy().split_constraint(v1,v2,va); } return va; } template typename Constrained_triangulation_plus_2:: Vertex_handle Constrained_triangulation_plus_2:: intersect(Face_handle f, int i, Vertex_handle vaa, Vertex_handle vbb) { return intersect(f, i, vaa, vbb, Intersection_tag()); } template typename Constrained_triangulation_plus_2:: Vertex_handle Constrained_triangulation_plus_2:: intersect(Face_handle, int, Vertex_handle, Vertex_handle, No_constraint_intersection_tag) { throw typename Tr::Intersection_of_constraints_exception(); return Vertex_handle(); } template typename Constrained_triangulation_plus_2:: Vertex_handle Constrained_triangulation_plus_2:: intersect(Face_handle, int, Vertex_handle, Vertex_handle, No_constraint_intersection_requiring_constructions_tag) { throw typename Tr::Intersection_of_constraints_exception(); return Vertex_handle(); } template typename Constrained_triangulation_plus_2:: Vertex_handle Constrained_triangulation_plus_2:: intersect(Face_handle f, int i, Vertex_handle vaa, Vertex_handle vbb, Exact_intersections_tag) // compute the intersection of the constraint edge (f,i) // with the subconstraint (vaa,vbb) being inserted // insert the intersection point // (the constraint edge (f,i) will be split in hierarchy by insert) // and return the Vertex_handle of the new Vertex { const Vertex_handle vcc = f->vertex(cw(i)); const Vertex_handle vdd = f->vertex(ccw(i)); const auto [vc, vd] = hierarchy().enclosing_constraint(vcc, vdd); const auto [va, vb] = hierarchy().enclosing_constraint(vaa, vbb); CGAL_assertion(vc != vd); CGAL_assertion(va != vb); const Point& pa = va->point(); const Point& pb = vb->point(); const Point& pc = vc->point(); const Point& pd = vd->point(); #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::intersect segment ( " << display_vertex(va) << " , " << display_vertex(vb) << " ) with edge ( #"<< vc->time_stamp() << "= " << vc->point() << " , " << display_vertex(vd) << " , Exact_intersections_tag)\n"; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS Point pi(ORIGIN); // initialize although we are sure that it will be // set by the intersection, but to quiet a warning Intersection_tag itag = Intersection_tag(); CGAL_assertion_code( bool ok = ) intersection(geom_traits(), pa, pb, pc, pd, pi, itag ); CGAL_assertion(ok); Vertex_handle vi = insert(pi, Triangulation::EDGE, f, i); #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::intersect, `vi` is ( " << display_vertex(vi) << " )\n"; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS return vi; } template typename Constrained_triangulation_plus_2::Vertex_handle Constrained_triangulation_plus_2:: intersect(Face_handle f, int i, Vertex_handle vaa, Vertex_handle vbb, Exact_predicates_tag itag) { Vertex_handle vcc, vdd; vcc = f->vertex(cw(i)); vdd = f->vertex(ccw(i)); const Point& pa = vaa->point(); const Point& pb = vbb->point(); const Point& pc = vcc->point(); const Point& pd = vdd->point(); #ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS std::cerr << CGAL::internal::cdt_2_indent_level << "CT_plus_2::intersect segment ( " << display_vertex(vaa) << " , " << display_vertex(vbb) << " ) with edge ( #"<< vcc->time_stamp() << "= " << vcc->point() << " , " << display_vertex(vdd) << " , Exact_predicates_tag)\n"; #endif // CGAL_CDT_2_DEBUG_INTERSECTIONS Vertex_handle vi = Triangulation::insert_intersection( f, i, vaa, vbb, vcc, vdd, pa, pb, pc, pd, itag); // vi == vc or vi == vd may happen even if intersection==true // due to approximate construction of the intersection if (vi != vcc && vi != vdd) { hierarchy().split_constraint(vcc,vdd,vi); insert_subconstraint(vcc,vi); insert_subconstraint(vi, vdd); } else { insert_subconstraint(vcc,vdd); } return vi; } } //namespace CGAL #include #endif //CGAL_CONSTRAINED_TRIANGULATION_PLUS_2_H