// 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_PATH_ON_SURFACE_H #define CGAL_PATH_ON_SURFACE_H 1 #include #include #include #include namespace CGAL { template class Path_on_surface { public: typedef Map_ Map; typedef typename Map::Dart_handle Dart_handle; typedef typename Map::Dart_const_handle Dart_const_handle; typedef Path_on_surface Self; Path_on_surface(const Map& amap) : m_map(amap), m_is_closed(false) {} void swap(Self& p2) { assert(&m_map==&(p2.m_map)); m_path.swap(p2.m_path); std::swap(m_is_closed, p2.m_is_closed); } // @return true iff the path is empty bool is_empty() const { return m_path.empty(); } std::size_t length() const { return m_path.size(); } // @return true iff the path is closed (update after each path modification). bool is_closed() const { return m_is_closed; } const Map& get_map() const { return m_map; } void clear() { m_path.clear(); } std::size_t next_index(std::size_t i) const { return (is_closed() && i==m_path.size()-1?0:i+1); } std::size_t prev_index(std::size_t i) const { return (is_closed() && i==0?m_path.size()-1:i-1); } Dart_const_handle get_ith_dart(std::size_t i) const { assert(i<=m_path.size()); return m_path[(i==m_path.size()?0:i)]; } Dart_const_handle operator[] (std::size_t i) const { assert(i<=m_path.size()); return m_path[((is_closed() && i==m_path.size())?0:i)]; } Dart_const_handle back() const { assert(!is_empty()); return m_path.back(); } void push_back(Dart_const_handle dh) { assert(dh!=NULL && dh!=m_map.null_dart_handle); assert((is_empty() || CGAL::template belong_to_same_cell (m_map, m_map.other_extremity(back()), dh))); m_path.push_back(dh); update_is_closed(); } // @return true iff the path is valid; i.e. a sequence of edges two by // two adjacent. bool is_valid() const { for (unsigned int i=1; i(m_map, m_path[i], pend)) { return false; } } if (is_closed()) { Dart_const_handle pend=m_map.other_extremity(m_path[m_path.size()-1]); if (pend==Map::null_handle) { return false; } if (!CGAL::template belong_to_same_cell(m_map, pend, m_path[0])) { return false; } } return true; } // Update m_is_closed to true iff the path is closed (i.e. the second // extremity of the last dart of the path is the same vertex than the one // of the first dart of the path). void update_is_closed() { if (is_empty()) { m_is_closed=false; } // or true by vacuity ? if (!is_valid()) { m_is_closed=false; } // Interest ?? Dart_const_handle pend=m_map.other_extremity(back()); if (pend==Map::null_handle) { m_is_closed=false; } m_is_closed=CGAL::belong_to_same_cell(m_map, m_path[0], pend); } // @return true iff the path does not pass twice through a same edge // or a same vertex. bool is_simple() const { typename Map::size_type markvertex=m_map.get_new_mark(); typename Map::size_type markedge=m_map.get_new_mark(); bool res=true; unsigned int i=0; for (i=0; res && i(m_path[i], markvertex); CGAL::mark_cell(m_path[i], markedge); } i=0; while(m_map.number_of_marked_darts(markedge)>0) { assert(i(m_path[i], markvertex); CGAL::unmark_cell(m_path[i], markedge); ++i; } m_map.free_mark(markvertex); m_map.free_mark(markedge); return res; } /// @return the turn between dart number i and dart number i+1. /// (turn is position of the second edge in the cyclic ordering of /// edges starting from the first edge around the second extremity /// of the first dart) std::size_t next_positive_turn(std::size_t i) const { assert(is_valid()); assert(i(d1)!=d2) { ++res; d1=m_map.template beta<1, 2>(d1); } // std::cout<<"next_positive_turn="<(m_path[i]); Dart_const_handle d2=m_map.template beta<2>(get_ith_dart(i+1)); // Work also for the last dart for cycles assert(d1!=d2); std::size_t res=1; while (m_map.template beta<0>(d1)!=d2) { ++res; d1=m_map.template beta<0, 2>(d1); } // std::cout<<"next_negative_turn="<=length()-1) { return begin; } // begin is the before last dart while ((positive && next_positive_turn(end)==2) || (!positive && next_negative_turn(end)==2)) { end=next_index(end); } if ((positive && next_positive_turn(end)==1) || (!positive && next_negative_turn(end)==1)) // We are on the end of a bracket { end=next_index(end); } else { end=begin; } return end; } void transform_positive_bracket(std::size_t begin, std::size_t end, Self& new_path) { // 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=(next_index(begin)!=end? m_map.template beta<0>(get_ith_dart(begin)): m_map.template beta<1,2,0>(get_ith_dart(end))); Dart_const_handle d2=(next_index(begin)!=end? m_map.template beta<2,0,2>(get_ith_dart(end)): m_map.template beta<0,0,2>(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(std::size_t begin, std::size_t end, Self& new_path) { // 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=(next_index(begin)!=end? m_map.template beta<2,1>(get_ith_dart(begin)): m_map.template beta<2,0,2,1>(get_ith_dart(end))); Dart_const_handle d2=(next_index(begin)!=end? m_map.template beta<1>(get_ith_dart(end)): m_map.template beta<2,1,1>(get_ith_dart(begin))); new_path.push_back(d1); CGAL::extend_straight_positive_until(new_path, d2); } void transform_bracket(std::size_t begin, std::size_t end, Self& new_path, bool positive) { if (positive) { transform_positive_bracket(begin, end, new_path); } else { transform_negative_bracket(begin, end, new_path); } } // copy all darts starting from begin and going to the dart before end // from this path to new_path. void copy_rest_of_path(std::size_t begin, std::size_t end, Self& new_path) { assert(end<=length()); assert(begin<=end); while(begin!=end) { new_path.push_back(get_ith_dart(begin)); ++begin; } } bool bracket_flattening_one_step() { if (is_empty()) return false; Self new_path(m_map); bool positive=false; std::size_t begin, end; std::size_t lastturn=m_path.size()-(is_closed()?0:1); for (begin=0; begin(m_path[next_index(i)])) { i+=2; res=true; } else { new_path.push_back(m_path[i]); // We copy this dart ++i; } } if (i==m_path.size()-1) { new_path.push_back(m_path[m_path.size()-1]); } // we copy the last dart swap(new_path); return res; } // Simplify the path by removing all spurs bool remove_spurs() { bool res=false; while(remove_spurs_one_step()) { res=true; } return res; } // Simplify the path by removing all possible brackets and spurs void simplify() { bool modified=false; do { modified=bracket_flattening_one_step(); if (!modified) { modified=remove_spurs_one_step(); } } while(modified); } bool find_l_shape(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==m_path.size()-1 && !is_closed()) { return false; } // begin is the before last dart while (next_negative_turn(end)==2 && end!=begin) { end=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=next_index(end); } else { return false; } while (next_negative_turn(end)==2 && end!=begin) { end=next_index(end); } return true; } void push_l_shape(std::size_t begin, std::size_t middle, std::size_t end, Self& new_path) { Dart_const_handle d1; // TODO SPECIAL CASE 7: do not push this dart d1=m_map.template beta<2,1>(get_ith_dart(begin)); new_path.push_back(d1); if (begin!=middle) { CGAL::extend_uturn_positive(new_path, 1); d1=m_map.template beta<2,1,1>(get_ith_dart(middle)); CGAL::extend_straight_positive_until(new_path, d1); if (next_index(middle)!=end) { CGAL::extend_uturn_positive(new_path, 3); } else { CGAL::extend_straight_positive(new_path, 1); } } if (next_index(middle)!=end) { d1=m_map.template beta<2,0,2,1>(get_ith_dart(end)); CGAL::extend_straight_positive_until(new_path, d1); if (begin!=end) // TODO CHECK THIS TEST : CASE 7 { CGAL::extend_uturn_positive(new_path, 1); } } } void push_l_shape_cycle_2() { Dart_const_handle d1= m_map.template beta<2,1,1>(get_ith_dart(0)); clear(); push_back(d1); CGAL::extend_straight_positive_until(*this, d1); } bool right_push_one_step() { std::size_t begin, middle, end; std::size_t lastturn=m_path.size()-(is_closed()?0:1); std::size_t next_turn, next_next_turn; bool prev2=false; for (middle=0; middle compute_positive_turns() const { std::vector res; if (is_empty()) return res; std::size_t i; for (i=0; i(m_path[i+1])) { res.push_back(0); } else { res.push_back(next_positive_turn(i)); } } if (is_closed()) { if (m_path[i]==m_map.template beta<2>(m_path[0])) { res.push_back(0); } else { res.push_back(next_positive_turn(i)); } } return res; } std::vector compute_negative_turns() const { std::vector res; if (is_empty()) return res; std::size_t i; for (i=0; i(m_path[i+1])) { res.push_back(0); } else { res.push_back(next_negative_turn(i)); } } if (is_closed()) { if (m_path[i]==m_map.template beta<2>(m_path[0])) { res.push_back(0); } else { res.push_back(next_negative_turn(i)); } } return res; } std::vector compute_turns(bool positive) const { return (positive?compute_positive_turns():compute_negative_turns()); } bool same_turns_from(const char* turns, const std::vector& resplus, const std::vector& resmoins, std::size_t start) const { assert(start==0 || start>nb; if ((nb>=0 && resplus[start]!=nb) || (nb<0 && resmoins[start]!=-nb)) { return false; } ++start; if (start==resplus.size()) { start=0; } } iss>>nb; if (iss.good()) { return false; } // There are more elements in turns than in res return true; } bool same_turns(const char* turns) const { std::vector resplus=compute_positive_turns(); std::vector resmoins=compute_negative_turns(); if (!is_closed()) { return same_turns_from(turns, resplus, resmoins, 0); } for (std::size_t start=0; start res=compute_positive_turns(); for (std::size_t i=0; i res=compute_negative_turns(); for (std::size_t i=0; i new_path(m_path.size()); for (std::size_t i=0; i(m_path[i]); } new_path.swap(m_path); } protected: const Map& m_map; // The underlying map std::vector m_path; // The sequence of darts bool m_is_closed; // True iff the path is a cycle }; } // namespace CGAL #endif // CGAL_PATH_ON_SURFACE_H // // EOF //