// Copyright (c) 2017 CNRS and LIRIS' Establishments (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org); you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation; either version 3 of the License, // or (at your option) any later version. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ // SPDX-License-Identifier: LGPL-3.0+ // // Author(s) : Guillaume Damiand // #ifndef CGAL_COMBINATORIAL_MAP_FUNCTIONALITIES_H #define CGAL_COMBINATORIAL_MAP_FUNCTIONALITIES_H 1 #include #include #include #include #include #include #include namespace CGAL { template class Combinatorial_map_tools { public: typedef typename Map::Dart_handle Dart_handle; typedef typename Map::Dart_const_handle Dart_const_handle; typedef CGAL::Union_find UFTree; typedef typename UFTree::handle UFTree_handle; typedef boost::unordered_map > TPaths; typedef boost::unordered_map TDartIds; Combinatorial_map_tools(Map& amap) : m_original_map(amap) { if (!m_map.is_without_boundary(1)) { std::cerr<<"ERROR: the given amap has 1-boundaries; " <<"such a surface is not possible to process here." < origin_to_copy; // We copy the original map, while keeping a mapping between darts. m_map.copy(m_original_map, &origin_to_copy); // The mapping between darts of the copy into darts of the original map. boost::unordered_map copy_to_origin; for (auto it=origin_to_copy.begin(); it!=origin_to_copy.end(); ++it) { copy_to_origin[it->second]=it->first; } #ifdef COMPUTE_TIME t2.stop(); std::cout<<"[TIME] Copy map: "<(dh1); std::size_t id=0; for(; dh1!=NULL; dh1=(dh1==dh2?NULL:dh2)) // We have two vertices to process { Dart_handle cur_dh=dh1; do { m_dart_ids[cur_dh]=id++; cur_dh=m_map.template beta<2, 1>(cur_dh); } while(cur_dh!=dh1); } } #ifdef COMPUTE_TIME t2.stop(); std::cout<<"[TIME] Label darts: "<(dh2))<(dh2); } while(dh2!=it); } } m_map.free_mark(marktemp); m_map.display_darts(std::cout); #endif #ifdef COMPUTE_TIME t.stop(); std::cout<<"[TIME] Total time for computation of reduced map: "< transform_original_path_into_quad_surface (const Path_on_surface& path) { Path_on_surface res(m_map); if (path.is_empty()) return res; for (std::size_t i=0; i& vertices) { uftrees.clear(); vertices.clear(); typename Map::size_type treated=m_map.get_new_mark(); for (typename Map::Dart_range::iterator it=m_map.darts().begin(), itend=m_map.darts().end(); it!=itend; ++it) { if (!m_map.is_marked(it, treated)) { UFTree_handle newuf=uftrees.make_set(it); for (typename Map::template Dart_of_cell_basic_range<0>::iterator itv=m_map.template darts_of_cell_basic<0>(it, treated).begin(), itvend=m_map.template darts_of_cell_basic<0>(it, treated).end(); itv!=itvend; ++itv) { vertices[itv]=newuf; m_map.mark(itv, treated); } } } m_map.free_mark(treated); } void initialize_faces(UFTree& uftrees, boost::unordered_map& faces) { uftrees.clear(); faces.clear(); typename Map::size_type treated=m_map.get_new_mark(); for (typename Map::Dart_range::iterator it=m_map.darts().begin(), itend=m_map.darts().end(); it!=itend; ++it) { if (!m_map.is_marked(it, treated)) { UFTree_handle newuf=uftrees.make_set(it); Dart_handle cur=it; do { faces[cur]=newuf; m_map.mark(cur, treated); cur=m_map.template beta<1>(cur); } while (cur!=it); } } m_map.free_mark(treated); } UFTree_handle get_uftree(const UFTree& uftrees, const boost::unordered_map& mapdhtouf, Dart_const_handle dh) { assert(dh!=NULL); assert(mapdhtouf.find(dh)!=mapdhtouf.end()); return uftrees.find(mapdhtouf.find(dh)->second); } // Mark the edge containing adart in the given map. void mark_edge(const Map& amap, Dart_const_handle adart, std::size_t amark) { amap.mark(amap.template beta<2>(adart), amark); amap.mark(adart, amark); } // Erase the edge given by adart (which belongs to the map m_map) from the // associative array copy_to_origin, and erase the corresponding edge // (which belongs to the map m_original_map) from the array origin_to_copy void erase_edge_from_associative_arrays (Dart_handle adart, boost::unordered_map& origin_to_copy, boost::unordered_map& copy_to_origin) { origin_to_copy.erase(m_original_map.template beta<2>(copy_to_origin[adart])); origin_to_copy.erase(copy_to_origin[adart]); copy_to_origin.erase(m_map.template beta<2>(adart)); copy_to_origin.erase(adart); } // Step 1) Transform m_map into an equivalent surface having only one // vertex. All edges contracted during this step belong to the spanning // tree T, and thus corresponding edges in m_original_map are marked. void surface_simplification_in_one_vertex (boost::unordered_map& origin_to_copy, boost::unordered_map& copy_to_origin) { UFTree uftrees; // uftree of vertices; one tree for each vertex, // contains one dart of the vertex boost::unordered_map vertices; initialize_vertices(uftrees, vertices); m_map.set_automatic_attributes_management(false); for (typename Map::Dart_range::iterator it=m_map.darts().begin(), itend=m_map.darts().end(); it!=itend; ++it) { if (m_map.is_dart_used(it) && get_uftree(uftrees, vertices, it)!= get_uftree(uftrees, vertices, m_map.template beta<2>(it))) { mark_edge(m_original_map, copy_to_origin[it], m_mark_T); erase_edge_from_associative_arrays(it, origin_to_copy, copy_to_origin); uftrees.unify_sets(get_uftree(uftrees, vertices, it), get_uftree(uftrees, vertices, m_map.template beta<2>(it))); //m_map.template contract_cell<1>(it); Dart_handle d1=it, d2=m_map.template beta<2>(it); m_map.template link_beta<1>(m_map.template beta<0>(d1), m_map.template beta<1>(d1)); m_map.template link_beta<1>(m_map.template beta<0>(d2), m_map.template beta<1>(d2)); m_map.erase_dart(d1); m_map.erase_dart(d2); } } m_map.set_automatic_attributes_management(true); } // Step 2) Compute, for each edge of m_original_map not in the spanning // tree T, the pair of darts of the edge in m_copy. This pair of edges // will be updated later (in surface_simplification_in_one_face() and in // surface_quadrangulate() ) void compute_length_two_paths (const boost::unordered_map& origin_to_copy) { paths.clear(); for (typename Map::Dart_range::const_iterator it=m_original_map.darts().begin(), itend=m_original_map.darts().end(); it!=itend; ++it) { if (!m_original_map.is_marked(it, m_mark_T)) { assert(!m_original_map.template is_free<2>(it)); if (it(it)) { paths[it]=std::make_pair (origin_to_copy.at(it), m_map.template beta<2>(origin_to_copy.at(it))); assert(paths[it].first!=paths[it].second); assert(paths[it].first==m_map.template beta<2>(paths[it].second)); } } } #ifdef CGAL_TRACE_CMAP_TOOLS std::cout<<"Number of darts in paths: "<& origin_to_copy, boost::unordered_map& copy_to_origin) { UFTree uftrees; // uftree of faces; one tree for each face, // contains one dart of the face boost::unordered_map faces; initialize_faces(uftrees, faces); m_map.set_automatic_attributes_management(false); typename Map::size_type toremove=m_map.get_new_mark(); Dart_handle currentdart=NULL, oppositedart=NULL; for (typename Map::Dart_range::iterator it=m_map.darts().begin(), itend=m_map.darts().end(); it!=itend; ++it) { currentdart=it; assert (!m_map.template is_free<2>(currentdart)); // TODO later oppositedart=m_map.template beta<2>(currentdart); if (currentdart(currentdart)!=oppositedart); assert(m_map.template beta<1>(currentdart)!=oppositedart); uftrees.unify_sets(get_uftree(uftrees, faces, currentdart), get_uftree(uftrees, faces, oppositedart)); m_map.mark(currentdart, toremove); m_map.mark(oppositedart, toremove); mark_edge(m_original_map, copy_to_origin[currentdart], m_mark_L); } } } /* m_map.display_characteristics(std::cout) << ", valid=" << m_map.is_valid() << std::endl; m_map.display_darts(std::cout)<(it); } } } m_map.set_automatic_attributes_management(true); m_map.free_mark(toremove); } // Step 4) quadrangulate the surface. void surface_quadrangulate() { // Here the map has only one face and one vertex. typename Map::size_type oldedges=m_map.get_new_mark(); m_map.negate_mark(oldedges); // now all edges are marked // 1) We insert a vertex in the face (note that all points have the // same geometry). New edges created by the operation are not marked. m_map.insert_point_in_cell_2(m_map.darts().begin(), m_map.point(m_map.darts().begin())); // m_map.display_darts(std::cout); // 2) We update the pair of darts // std::cout<<"************************************************"<& p=itp->second; //std::cout<<"Pair: "<(p.first); p.second=m_map.template beta<0>(p.second); //std::cout<<" -> "<(p.second)); } // 3) We remove all the old edges. for (typename Map::Dart_range::iterator it=m_map.darts().begin(), itend=m_map.darts().end(); it!=itend; ++it) { if (m_map.is_dart_used(it) && m_map.is_marked(it, oldedges)) { m_map.template remove_cell<1>(it); } } m_map.free_mark(oldedges); } /// Version1: quadratic in number of darts in the pathes void update_length_two_paths_before_edge_removals_v1(typename Map::size_type toremove) { // std::cout<<"************************************************"<& p=itp->second; //std::cout<<"Pair: "<(p.first); Dart_const_handle initdart=p.first; if (!m_map.is_marked(p.first, toremove)) { p.second=m_map.template beta<1>(p.first); initdart=p.second; while (m_map.is_marked(p.second, toremove)) { p.second=m_map.template beta<2, 1>(p.second); assert(p.second!=initdart); } } else { while (m_map.is_marked(p.first, toremove)) { p.first=m_map.template beta<2, 1>(p.first); //std::cout<(p.second); initdart=p.second; while (m_map.is_marked(p.second, toremove)) { p.second=m_map.template beta<2, 1>(p.second); //std::cout< "<& copy_to_origin) { // std::cout<<"************************************************"<(it)) { // Surviving dart => belongs to the border of the face std::pair& p=paths[it]; Dart_handle initdart=m_map.darts().iterator_to(const_cast(*(p.first))); Dart_handle initdart2=m_map.template beta<2>(initdart); assert(!m_map.is_marked(initdart, toremove)); assert(!m_map.is_marked(initdart2, toremove)); // 1) We update the dart associated with p.second p.second=m_map.template beta<1>(initdart); while (m_map.is_marked(p.second, toremove)) { p.second=m_map.template beta<2, 1>(p.second); } // 2) We do the same loop, linking all the inner darts with p.second initdart=m_map.template beta<1>(initdart); while (m_map.is_marked(initdart, toremove)) { assert(copy_to_origin.count(initdart)==1); Dart_const_handle d1=copy_to_origin.find(initdart)->second; Dart_const_handle d2=m_original_map.template beta<2>(d1); if (d1(initdart); } // 3) We do the same loop but starting from initdart2 initdart2=m_map.template beta<1>(initdart2); Dart_handle enddart2=initdart2; while (m_map.is_marked(enddart2, toremove)) { enddart2=m_map.template beta<2, 1>(enddart2); } while (m_map.is_marked(initdart2, toremove)) { assert(copy_to_origin.count(initdart2)==1); Dart_const_handle d1=copy_to_origin.find(initdart2)->second; Dart_const_handle d2=m_original_map.template beta<2>(d1); if (d1(initdart2); } } } } /// @return true iff the edge containing adart is associated with a path. /// (used for debug purpose because we are suppose to be able to /// test this by using directly the mark m_mark_T). bool is_edge_has_path(Dart_const_handle adart) const { Dart_const_handle opposite=m_original_map.template beta<2>(adart); if (adart& get_pair_of_darts (Dart_const_handle adart) { assert(!m_original_map.is_marked(adart, m_mark_T)); assert(is_edge_has_path(adart)); Dart_const_handle opposite=m_original_map.template beta<2>(adart); if (adartsecond; } return paths.find(opposite)->second; } Dart_const_handle get_first_dart_of_the_path(Dart_const_handle adart) const { assert(!m_original_map.is_marked(adart, m_mark_T)); assert(is_edge_has_path(adart)); Dart_const_handle opposite=m_original_map.template beta<2>(adart); if (adart& p=paths.find(adart)->second; return p.first; } const std::pair& p=paths.find(opposite)->second; return m_map.template beta<2>(p.second); } Dart_const_handle get_second_dart_of_the_path(Dart_const_handle adart) const { assert(!m_original_map.is_marked(adart, m_mark_T)); assert(is_edge_has_path(adart)); Dart_const_handle opposite=m_original_map.template beta<2>(adart); if (adart& p=paths.find(adart)->second; return p.second; } const std::pair& p=paths.find(opposite)->second; return m_map.template beta<2>(p.first); } /// Test if paths are valid, i.e.: /// 1) all the darts of m_original_map that do not belong to T are /// associated with a pair of darts; /// 2) all the darts of the paths belong to m_map; /// 3) the origin of the second dart of the pair is the extremity of the /// first dart. /// 4) all the darts of m_map are not free (both for beta 1 and 2) /// 5) The two darts in a pair are different bool are_paths_valid() const { bool res=true; for (auto it=m_original_map.darts().begin(), itend=m_original_map.darts().end(); it!=itend; ++it) { if (!m_original_map.is_marked(it, m_mark_T)) { if (!is_edge_has_path(it)) { std::cout<<"ERROR: an edge that does not belong to the spanning tree" <<" T has no associated path."<second.first)) { std::cout<<"ERROR: first dart in paths does not exist anymore in m_map." <second.first)) { std::cout<<"ERROR: first dart in paths does not belong to m_map." <second.second)) { std::cout<<"ERROR: second dart in paths does not exist anymore in m_map." <second.second)) { std::cout<<"ERROR: second dart in paths does not belong to m_map." <second.first==it->second.second) { std::cout<<"ERROR: two darts in the same pair are equal." <& path, std::size_t i) const { // OLD return path.next_positive_turn(i); Dart_const_handle d1=path.get_ith_dart(i); Dart_const_handle d2=path.get_next_dart(i); assert(d1!=d2); std::size_t id1=m_dart_ids.at(m_map.template beta<2>(d1)); std::size_t id2=m_dart_ids.at(d2); if (id1>=m_number_of_edges) { id1-=m_number_of_edges; // id of the first dart in its own vertex assert(id2>=m_number_of_edges); id2-=m_number_of_edges; // id of the second dart in its own vertex } // TODO check with Francis what to do when id1==id2. // I think for positive return m_number_of_edges // and for negative return 0. (But I am not sure...) std::size_t res=(id1& path, std::size_t i) const { // OLD return path.next_negative_turn(i); Dart_const_handle d1=path.get_ith_dart(i); Dart_const_handle d2=path.get_next_dart(i); /* Dart_const_handle d1=m_map.template beta<2>(path.get_ith_dart(i)); Dart_const_handle d2=m_map.template beta<2>(path.get_next_dart(i));*/ assert(d1!=d2); std::size_t id1=m_dart_ids.at(m_map.template beta<2>(d1)); std::size_t id2=m_dart_ids.at(d2); if (id1>=m_number_of_edges) { id1-=m_number_of_edges; // id of the first dart in its own vertex assert(id2>=m_number_of_edges); id2-=m_number_of_edges; // id of the second dart in its own vertex } // TODO check with Francis what to do when id1==id2. // I think for positive return m_number_of_edges // and for negative return 0. (But I am not sure...) std::size_t res=(id1<=id2?m_number_of_edges-id2+id1: id1-id2); // std::size_t tempodebug=path.next_negative_turn(i); assert(res==path.next_negative_turn(i)); return res; } std::size_t find_end_of_braket(const Path_on_surface& path, std::size_t begin, bool positive) const { assert((positive && next_positive_turn(path, begin)==1) || (!positive && next_negative_turn(path, begin)==1)); std::size_t end=path.next_index(begin); if (!path.is_closed() && end>=path.length()-1) { return begin; } // begin is the before last dart while ((positive && next_positive_turn(path, end)==2) || (!positive && next_negative_turn(path, end)==2)) { end=path.next_index(end); } if ((positive && next_positive_turn(path, end)==1) || (!positive && next_negative_turn(path, end)==1)) // We are on the end of a bracket { end=path.next_index(end); } else { end=begin; } return end; } void transform_positive_bracket(Path_on_surface& path, std::size_t begin, std::size_t end, Path_on_surface& new_path) const { // There is a special case for (1 2^r). In this case, we need to ignore // the two darts begin and end Dart_const_handle d1=(path.next_index(begin)!=end? m_map.template beta<0>(path.get_ith_dart(begin)): m_map.template beta<1,2,0>(path.get_ith_dart(end))); Dart_const_handle d2=(path.next_index(begin)!=end? m_map.template beta<2,0,2>(path.get_ith_dart(end)): m_map.template beta<0,0,2>(path.get_ith_dart(begin))); new_path.push_back(m_map.template beta<2>(d1)); CGAL::extend_straight_negative_until(new_path, d2); } void transform_negative_bracket(Path_on_surface& path, std::size_t begin, std::size_t end, Path_on_surface& new_path) const { // There is a special case for (-1 -2^r). In this case, we need to ignore // the two darts begin and end Dart_const_handle d1=(path.next_index(begin)!=end? m_map.template beta<2,1>(path.get_ith_dart(begin)): m_map.template beta<2,0,2,1>(path.get_ith_dart(end))); Dart_const_handle d2=(path.next_index(begin)!=end? m_map.template beta<1>(path.get_ith_dart(end)): m_map.template beta<2,1,1>(path.get_ith_dart(begin))); new_path.push_back(d1); CGAL::extend_straight_positive_until(new_path, d2); } void transform_bracket(Path_on_surface& path, std::size_t begin, std::size_t end, Path_on_surface& new_path, bool positive) const { if (positive) { transform_positive_bracket(path, begin, end, new_path); } else { transform_negative_bracket(path, begin, end, new_path); } } bool bracket_flattening_one_step(Path_on_surface& path) const { if (path.is_empty()) return false; #ifndef NDEBUG bool is_even=path.length()%2; #endif // NDEBUG Path_on_surface new_path(m_map); bool positive=false; std::size_t begin, end; std::size_t lastturn=path.length()-(path.is_closed()?0:1); for (begin=0; begin& path) const { bool res=false; while(bracket_flattening_one_step(path)) { res=true; } return res; } bool remove_spurs_one_step(Path_on_surface& path) const { if (path.is_empty()) return false; bool res=false; std::size_t i; std::size_t lastturn=path.length()-(path.is_closed()?0:1); for (i=0; !res && i(path.get_next_dart(i))) { res=true; } } if (!res) { return false; } #ifndef NDEBUG bool is_even=path.length()%2; #endif // NDEBUG --i; // Because we did a ++ before to leave the loop // Here there is a spur at position i in the path Path_on_surface new_path(m_map); // Special case, the spur is between last dart of the path and the first dart if (path.is_closed() && i==path.length()-1) { path.copy_rest_of_path(1, path.length()-1, new_path); // copy path between 1 and m_path.length()-2 } else { // Otherwise copy darts before the spur if (i>0) { path.copy_rest_of_path(0, i, new_path); } // copy path between 0 and i-1 // and the darts after if (i+2& path) const { bool res=false; while(remove_spurs_one_step(path)) { res=true; } return res; } // Simplify the path by removing all possible brackets and spurs void simplify(Path_on_surface& path) const { bool modified=false; do { modified=bracket_flattening_one_step(path); if (!modified) { modified=remove_spurs_one_step(path); } } while(modified); } bool find_l_shape(const Path_on_surface& path, std::size_t begin, std::size_t& middle, std::size_t& end) const { assert(next_negative_turn(begin)==1 || next_negative_turn(begin)==2); end=begin+1; if (end==path.length()-1 && !path.is_closed()) { return false; } // begin is the before last dart while (next_negative_turn(end)==2 && end!=begin) { end=path.next_index(end); } if (begin==end) { // Case of a path having only 2 turns return true; } if (next_negative_turn(end)==1) { middle=end; end=path.next_index(end); } else { return false; } while (next_negative_turn(end)==2 && end!=begin) { end=path.next_index(end); } return true; } void push_l_shape(Path_on_surface& path, std::size_t begin, std::size_t middle, std::size_t end, Path_on_surface& new_path, bool case_seven) const { Dart_const_handle d1; if (!case_seven) { d1=m_map.template beta<2,1>(path.get_ith_dart(begin)); } else { d1=m_map.template beta<2,1,2,0>(path.get_ith_dart(begin)); } new_path.push_back(d1); if (begin!=middle) { if (!case_seven) { CGAL::extend_uturn_positive(new_path, 1); } d1=m_map.template beta<2,1,1>(path.get_ith_dart(middle)); CGAL::extend_straight_positive_until(new_path, d1); if (path.next_index(middle)!=end) { CGAL::extend_uturn_positive(new_path, 3); } else { CGAL::extend_straight_positive(new_path, 1); } } if (path.next_index(middle)!=end) { d1=m_map.template beta<2,0,2,1>(path.get_ith_dart(end)); CGAL::extend_straight_positive_until(new_path, d1); if (!case_seven) { CGAL::extend_uturn_positive(new_path, 1); } else { CGAL::extend_straight_positive(new_path, 1); } } if (begin==middle && path.next_index(middle)==end) { // TODO: check if we need to do also something for !case_seven ? // if (case_seven) { CGAL::extend_uturn_positive(new_path, 1); } /* else { assert(false); } // We think (?) that this case is not possible */ } } void push_l_shape_cycle_2(Path_on_surface& path) const { Dart_const_handle d1= m_map.template beta<2,1,1>(path.get_ith_dart(0)); path.clear(); path.push_back(d1); CGAL::extend_straight_positive(path, 1); CGAL::extend_straight_positive_until(path, d1); } bool push_l_shape_2darts(Path_on_surface& path) const { Dart_const_handle d1=NULL; if (next_negative_turn(path, 0)==1) d1=m_map.template beta<2,1>(path.get_ith_dart(0)); else if (next_negative_turn(path, 1)==1) d1=m_map.template beta<2,1>(path.get_ith_dart(1)); else return false; path.clear(); path.push_back(d1); CGAL::extend_uturn_positive(path, 1); //path.push_back(m_map.template beta<1>(d1)); return true; } bool right_push_one_step(Path_on_surface& path) const { if (path.is_empty()) { return false; } if (path.length()==2) { return push_l_shape_2darts(path); } #ifndef NDEBUG bool is_even=path.length()%2; #endif // NDEBUG std::size_t begin, middle, end; std::size_t lastturn=path.length()-(path.is_closed()?0:1); std::size_t next_turn; std::size_t val_x=0; // value of turn before the beginning of a l-shape bool prev2=false; for (middle=0; middle0) { val_x=next_negative_turn(path, path.prev_index(begin)); } // And here now we can push the path Path_on_surface new_path(m_map); if (end& path) const { bool res=false; while(right_push_one_step(path)) { res=true; /*std::cout<<"PUSH "; display(); display_pos_and_neg_turns(); std::cout<& path) const { if (!path.is_closed()) { return; } #ifdef COMPUTE_TIME CGAL::Timer t; t.start(); #endif // COMPUTE_TIME /* std::cout<<"##########################################"<(d0), d2=beta<1, 2>(d1)... /// The same for darts between n/2...n-1 for the second vertex /// Thanks to these ids, we can compute in constant time the positive and /// negative turns between two consecutive darts std::size_t m_number_of_edges; // number of edges in the tranformed map (==number of darts / 2) }; } // namespace CGAL #endif // CGAL_COMBINATORIAL_MAP_FUNCTIONALITIES_H // // EOF //