1 // Copyright (c) 2019 CNRS and LIRIS' Establishments (France).
2 // All rights reserved.
3 //
4 // This file is part of CGAL (www.cgal.org).
5 //
6 // $URL: https://github.com/CGAL/cgal/blob/v5.3/Surface_mesh_topology/include/CGAL/Surface_mesh_topology/internal/Path_on_surface_with_rle.h $
7 // $Id: Path_on_surface_with_rle.h 11f2b92 2021-02-25T13:43:38+01:00 Sebastien Loriot
8 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
9 //
10 // Author(s)     : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr>
11 //
12 #ifndef CGAL_PATH_ON_SURFACE_WITH_RLE_H
13 #define CGAL_PATH_ON_SURFACE_WITH_RLE_H 1
14 
15 #include <CGAL/license/Surface_mesh_topology.h>
16 
17 #include <list>
18 #include <utility>
19 #include <iostream>
20 #include <iterator>
21 #include <vector>
22 #include <set>
23 #include <CGAL/assertions.h>
24 #include <unordered_map>
25 #include <unordered_set>
26 
27 namespace CGAL {
28 namespace Surface_mesh_topology {
29 
30 template<typename Map_>
31 class Path_on_surface;
32 
33 template<typename Mesh_>
34 class Minimal_quadrangulation;
35 
36 namespace internal {
37 
38 // A flat is a sequence of darts given by its two extremities: begin and end,
39 // with +2 turns (if length>0) or -2 turns (if length<0).
40 // length==0 => begin==end.
41 template<typename Map_>
42 class CFlat
43 {
44   typedef Map_                            Map;
45   typedef typename Map::Dart_const_handle Dart_const_handle;
46   typedef CFlat<Map>                      Self;
47 
48 public:
CFlat(Dart_const_handle dh)49   CFlat(Dart_const_handle dh) : begin(dh), end(dh), length(0)
50   {}
51 
CFlat(Dart_const_handle dh1,Dart_const_handle dh2,int l)52   CFlat(Dart_const_handle dh1, Dart_const_handle dh2, int l) :
53     begin(dh1), end(dh2), length(l)
54   {}
55 
56   bool operator==(const Self& other) const
57   { return begin==other.begin && end==other.end && length==other.length; }
58 
59   bool operator!=(const Self& other) const
60   { return !(operator==(other)); }
61 
62   friend std::ostream& operator<< (std::ostream& os, const Self& flat)
63   {
64     os<<"["<<&*(flat.begin)<<", "<<&*(flat.end)<<" (l="<<flat.length<<")]";
65     return os;
66   }
67 
68   Dart_const_handle begin, end;
69   int length; // Length of the flat, positive flat if >0, negative flat if <0
70 };
71 
72 // Small wrapper allowing to use directly a combinatorial map as if it is a
73 // minimal quadrangulation.
74 template<class Map_>
75 class Light_MQ  // MQ for minimal quadrangulation
76 {
77 public:
78   typedef Map_                              Local_map;
79   typedef Map_                              Mesh;
80   typedef typename Map_::Dart_const_handle  Dart_const_handle;
81 
Light_MQ(const Local_map & m)82   Light_MQ(const Local_map& m): m_map(m)
83   {}
84   Light_MQ(const Light_MQ&) = default;
85 
get_local_map()86   const Local_map& get_local_map() const
87   { return m_map; }
88 
positive_turn(Dart_const_handle d1,Dart_const_handle d2)89   std::size_t positive_turn(Dart_const_handle d1, Dart_const_handle d2) const
90   { return m_map.positive_turn(d1, d2); }
91 
negative_turn(Dart_const_handle d1,Dart_const_handle d2)92   std::size_t negative_turn(Dart_const_handle d1, Dart_const_handle d2) const
93   { return m_map.negative_turn(d1, d2); }
94 
95 protected:
96   const Local_map& m_map;
97 };
98 
99 template<typename MQ> // MQ for minimal quadrangulation
100 class Path_on_surface_with_rle
101 {
102 public:
103   typedef Path_on_surface_with_rle<MQ>                       Self;
104   typedef typename MQ::Local_map                             Map;
105   typedef typename MQ::Mesh                                  Mesh;
106   typedef typename Map::Dart_handle                          Dart_handle;
107   typedef typename Map::Dart_const_handle                    Dart_const_handle;
108   typedef CFlat<Map>                                         Flat;
109   typedef std::list<Flat>                                    List_of_flats;
110   typedef typename List_of_flats::iterator                   List_iterator;
111   // TODO typedef typename List_of_dart_length::const_iterator List_const_iterator;
112 
113   friend class Path_on_surface<Map>;
114 
115   struct List_iterator_hash
116   {
operatorList_iterator_hash117     std::size_t operator() (const List_iterator& lit) const
118     {
119       return std::hash<const void*>()(&*lit);
120     }
121   };
122 
123   typedef std::unordered_set<List_iterator, List_iterator_hash> Set_of_it;
124 
125 #ifdef CGAL_PWRLE_TURN_V2
126     typedef std::unordered_map<Dart_const_handle, std::size_t> TDartIds;
127 #endif //CGAL_PWRLE_TURN_V2
128 
129   /// Constructor
Path_on_surface_with_rle(const MQ & aMQ,const TDartIds & darts_ids)130   Path_on_surface_with_rle(const MQ& aMQ
131                          #ifdef CGAL_PWRLE_TURN_V2
132                            , const TDartIds & darts_ids
133                          #endif //CGAL_PWRLE_TURN_V2
134                            )
135     : m_MQ(aMQ),
136       m_is_closed(false),
137       m_length(0),
138       m_use_only_positive(false),
139       m_use_only_negative(false)
140   #ifdef CGAL_PWRLE_TURN_V2
141     , m_darts_ids(darts_ids)
142   #endif //CGAL_PWRLE_TURN_V2
143   {}
144 
145   Path_on_surface_with_rle(const Self&) = default;
146 
147   /// Creates a Path_on_surface_with_rle from a Path_on_surface.
148   /// If use_only_positive, consider only positive flats and not negative ones.
149   /// If use_only_negative, consider only negative flats and not positive ones.
150   /// If both are false, consider both positive and negative flats.
151   /// Both cannot be true at the same time.
152   /// Note that for a minimal surface of genus>=2, we cannot have both -2 and
153   /// +2 as turn, and thus these parameters are useless.
154   /// However, this case can occured for our unit tests on the cube, this is
155   /// the reason of these parameters.
156   Path_on_surface_with_rle(const MQ& aMQ, const Path_on_surface<Map>& apath,
157                            bool use_only_positive=false,
158                            bool use_only_negative=false)
m_MQ(aMQ)159   : m_MQ(aMQ),
160     m_is_closed(apath.is_closed()),
161     m_length(0),
162     m_use_only_positive(use_only_positive),
163     m_use_only_negative(use_only_negative)
164   {
165     if (apath.is_empty()) return;
166 
167     std::size_t i=0, starti=0;
168     bool positive_flat=false;
169     bool negative_flat=false;
170 
171     if (apath.is_closed())
172     {
173       if (!use_only_negative && apath.prev_positive_turn(i)==2)
174       { positive_flat=true; negative_flat=false; }
175       else if (!use_only_positive && apath.prev_negative_turn(i)==2)
176       { positive_flat=false; negative_flat=true; }
177 
178       while ((positive_flat && apath.prev_positive_turn(i)==2) ||
179              (negative_flat && apath.prev_negative_turn(i)==2))
180       {
181         i=apath.prev_index(i);
182         if (i==0) // Case of a closed path, made of only one flat part.
183         {
184           m_path.push_back(Flat(apath.real_front(), apath.real_back(),
185                                 (positive_flat?(static_cast<int>(apath.length()-1)):
186                                  -(static_cast<int>(apath.length()-1)))));
187           m_length=apath.length();
188           CGAL_assertion(is_valid());
189           return;
190         }
191       }
192       // Here i is the first dart of a flat
193     }
194 
195     starti=i;
196     do
197     {
198       // Here dart i is the beginning of a flat part (maybe of length 0)
199         push_back(apath.get_ith_real_dart(i), false);
200         i=apath.next_index(i);
201     }
202     while(i<apath.length() && i!=starti);
203 
204     CGAL_assertion(is_valid(true));
205   }
206 
swap(Self & p2)207   void swap(Self& p2)
208   {
209     if (this!=&p2)
210     {
211       CGAL_assertion(&get_map()==&(p2.get_map()));
212 #ifdef CGAL_PWRLE_TURN_V2
213       CGAL_assertion(&m_darts_ids==&(p2.m_darts_ids));
214 #endif // CGAL_PWRLE_TURN_V2
215       m_path.swap(p2.m_path);
216       std::swap(m_is_closed, p2.m_is_closed);
217       std::swap(m_length, p2.m_length);
218       std::swap(m_use_only_negative, p2.m_use_only_negative);
219       std::swap(m_use_only_positive, p2.m_use_only_positive);
220     }
221   }
222 
223   Self& operator=(const Self& other)
224   {
225     CGAL_assertion(&get_map()==&(other.get_map()));
226 #ifdef CGAL_PWRLE_TURN_V2
227     CGAL_assertion(&m_darts_ids==&(other.m_darts_ids));
228 #endif // CGAL_PWRLE_TURN_V2
229     if (this!=&other)
230     {
231       m_path=other.m_path;
232       m_is_closed=other.m_is_closed;
233       m_length=other.m_length;
234       m_use_only_negative=other.m_use_only_negative;
235       m_use_only_positive=other.m_use_only_positive;
236     }
237     return *this;
238   }
239 
240   Self& operator+=(Self& other)
241   {
242     // Be careful to the special case when *this==other.
243     // This is the reason of the litend computed with std::prev.
244     for (List_iterator lit=other.m_path.begin(),
245          litend=std::prev(other.m_path.end()); lit!=litend; ++lit)
246     { m_path.push_back(*lit); }
247     m_path.push_back(other.m_path.back()); // Last element
248     update_is_closed();
249     // TODO: List of flat should be simplify when possible
250     // TODO2: what to do if different values for m_use_only_positive and m_use_only_negative ??
251     //    (probably nothing since these bools are used only for our tests)
252     return *this;
253   }
254 
255   Self operator+(const Self& other) const
256   {
257     Self res=*this;
258     res+=other;
259     return res;
260   }
261 
262   /// @return true if this path is equal to other path. For closed paths, test
263   ///         all possible starting darts.
264   bool operator==(Self& other)
265   {
266     if (is_closed()!=other.is_closed() ||
267         length()!=other.length())
268       return false;
269 
270     if (is_empty() && other.is_empty())
271     { return true; }
272 
273     // Note that we need to transform the Path_on_surface_with_rle into
274     // the correspondings Path_on_surface, because a same path can have several
275     // different rle representations.
276     Path_on_surface<Map> p1(*this);
277     Path_on_surface<Map> p2(other);
278     return p1==p2;
279   }
280 
281   bool operator!=(Self&  other)
282   { return !(operator==(other)); }
283 
284   /// @return true iff the path is empty
is_empty()285   bool is_empty() const
286   { return m_path.empty(); }
287 
288   /// @return the length of the path, i.e. its number of darts
length()289   std::size_t length() const
290   { return m_length; }
291 
292   /// @return the number of darts in the double linked list.
293   /// note that size_of_list()<=length().
size_of_list()294   std::size_t size_of_list() const
295   { return m_path.size(); }
296 
297   /// @return true iff the path is closed (update after each path modification).
is_closed()298   bool is_closed() const
299   { return m_is_closed; }
300 
301   /// @return the underlying map.
get_map()302   const Map& get_map() const
303   { return m_MQ.get_local_map(); }
304 
305   /// clear the path.
clear()306   void clear()
307   {
308     m_path.clear();
309     m_is_closed=false;
310     m_length=0;
311   }
312 
313   /// @return true if the given flat is valid
is_flat_valid(const List_iterator & flat)314   bool is_flat_valid(const List_iterator& flat) const
315   {
316     CGAL_assertion(is_valid_iterator(flat));
317     Dart_const_handle dhend=flat->begin;
318     int nb=0;
319     while(nb!=flat->length)
320     {
321       if (flat->length>0)
322       { dhend=get_map().template beta<1, 2, 1>(dhend); ++nb; }
323       else
324       { dhend=get_map().template beta<2, 0, 2, 0, 2>(dhend); --nb; }
325     }
326 
327     return dhend==flat->end;
328   }
329 
330   /// Display the given flat
display_flat(std::ostream & os,const List_iterator & flat)331   void display_flat(std::ostream& os, const List_iterator& flat)
332   {
333     CGAL_assertion(is_valid_iterator(flat));
334     os<<"["<<get_map().darts().index(flat->begin)<<", "
335       <<get_map().darts().index(flat->end)<<" (l="<<flat->length<<")]";
336   }
337 
338   /// @return true iff the path is valid; i.e. a sequence of flats two by
339   ///              two adjacent.
340   /// if test_minimal is true, test that there is no two consecutive flats
341   /// that can be merged. If test_minimal is false, this test is not done.
342   bool is_valid(bool test_minimal=true)
343   {
344     if (is_empty()) { return !is_closed(); } // an empty past is not closed
345 
346     unsigned int nbdarts=0,i=0;
347     Dart_const_handle prev=(is_closed()?back():nullptr); // Last dart of the path
348     for (auto it=m_path.begin(); it!=m_path.end(); ++it)
349     {
350       if (prev!=nullptr && !get_map().template belong_to_same_cell<0>
351           (get_map().template beta<1>(prev), begin_of_flat(it)))
352       {
353         std::cerr<<"ERROR: The path is not valid: flat in position "<<i
354                  <<" does not follow the previous dart."<<std::endl;
355         return false;
356       }
357 
358       if (!is_flat_valid(it))
359       {
360         display();
361         std::cout<<"ERROR: The path is not valid: flat at position "<<i
362                 <<" with length "<<flat_length(it)
363                <<" is not correct: its end does not"
364               <<" correspond to the flat."<<std::endl;
365         return false;
366       }
367 
368       if (test_minimal && can_merge_with_next_flat(it))
369       {
370         std::cout<<"ERROR: The path is not valid: flat at position "<<i
371                 <<" with length "<<flat_length(it)<<" is not correct: it can be merged "
372                <<"with the next flat with length "
373               <<flat_length(next_iterator(it))<<" - turns between the two flats = +"
374              <<next_positive_turn(it)<<" -"<<next_negative_turn(it)<<std::endl;
375         return false;
376       }
377       prev=end_of_flat(it); // end of the previous flat
378       nbdarts+=std::abs(flat_length(it))+1; // Number of darts of the flat
379       ++i;
380     }
381 
382     if (is_closed())
383     {
384       if (prev==nullptr)
385       {
386         std::cout<<"ERROR: The path is not valid: it is empty and closed."
387                 <<std::endl;
388         return false;
389       }
390       if (!get_map().template belong_to_same_cell<0>
391           (get_map().template beta<1>(prev), begin_of_flat(m_path.begin())))
392       {
393         std::cerr<<"ERROR: The path is not valid: first flat "
394                  <<" does not follow the last dart for a closed path."<<std::endl;
395         return false;
396       }
397     }
398 
399     if (length()!=nbdarts)
400     {
401       std::cerr<<"ERROR: The path is not valid: store length=="<<length()
402                <<" different of real length=="<<nbdarts<<std::endl;
403       return false;
404     }
405 
406     return true;
407   }
408 
409   /// For debugging purpose, test if 'it' is a valid iterator.
is_valid_iterator(const List_iterator & ittotest)410   bool is_valid_iterator(const List_iterator& ittotest) const
411   {
412     if (ittotest==m_path.end()) { return false; }
413     return true;
414     // Assert too long; uncomment in case of bug.
415     /* for (auto it=m_path.begin(); it!=m_path.end(); ++it)
416     {
417       if (it==ittotest) { return true; }
418     }
419     return false; */
420   }
421 
422   /// @return the first dart of the path
423   /// @pre !is_empty()
front()424   Dart_const_handle front()
425   {
426     CGAL_assertion(!is_empty());
427     return begin_of_flat(m_path.begin());
428   }
429 
430   /// @return the last dart of the path
431   /// @pre !is_empty()
back()432   Dart_const_handle back()
433   {
434     CGAL_assertion(!is_empty());
435     return end_of_flat(std::prev(m_path.end()));
436   }
437 
438   /// @return true iff df can be added at the end of the path.
can_be_pushed(Dart_const_handle dh)439   bool can_be_pushed(Dart_const_handle dh)
440   {
441     // This assert is too long
442     // CGAL_assertion(get_map().darts().owns(dh));
443     if (is_empty()) return true;
444 
445     return get_map().template belong_to_same_cell<0>
446       (get_map().template beta<1>(back()), dh);
447   }
448 
449   /// Add the given dart at the end of this path.
450   /// @pre can_be_pushed(dh)
451   void push_back(Dart_const_handle dh, bool update_isclosed=true)
452   {
453     CGAL_assertion(dh!=Map::null_handle);
454     // This assert is too long, it is tested in the is_valid method.
455     // CGAL_assertion(can_be_pushed(dh));
456 
457     List_iterator itlast=m_path.end();
458 
459     bool positive_flat, negative_flat;
460     if (is_empty() ||
461         !is_prev_flat_can_be_extended_at_end(itlast, dh,
462                                              positive_flat, negative_flat))
463     { m_path.push_back(Flat(dh)); } // Create a new flat
464     else
465     {
466       itlast=std::prev(itlast);
467       set_end_of_flat(itlast, dh); // Move the last dart of the last flat
468       if (positive_flat && flat_length(itlast)>=0)
469       {
470         set_flat_length(itlast, flat_length(itlast)+1); // Increment the length of the flat
471       }
472       else
473       {
474         CGAL_assertion(negative_flat && flat_length(itlast)<=0);
475         set_flat_length(itlast, flat_length(itlast)-1); // Increment the length of the flat
476       }
477     }
478 
479     ++m_length;
480     if (update_isclosed) { update_is_closed(); }
481   }
482 
483   // Update m_is_closed to true iff the path is closed (i.e. the second
484   //   extremity of the last dart of the path is the same vertex than the one
485   //   of the first dart of the path).
update_is_closed()486   void update_is_closed()
487   {
488     if (is_empty()) { m_is_closed=false; }
489     else
490     {
491       Dart_const_handle pend=get_map().other_extremity(back());
492       if (pend==Map::null_handle) { m_is_closed=false; }
493       else
494       { m_is_closed=get_map().template belong_to_same_cell<0>(front(), pend); }
495     }
496   }
497 
498   /// @return true iff there is a flat after it
next_flat_exist(const List_iterator & it)499   bool next_flat_exist(const List_iterator& it) const
500   {
501     CGAL_assertion(it!=m_path.end());
502     return !is_empty() && (is_closed() || std::next(it)!=m_path.end());
503   }
504 
505   /// @return true iff there is a flat before it
prev_flat_exist(const List_iterator & it)506   bool prev_flat_exist(const List_iterator& it) const
507   {
508     return !is_empty() && (is_closed() || it!=m_path.begin());
509   }
510 
advance_iterator(List_iterator & it)511   void advance_iterator(List_iterator& it)
512   {
513     CGAL_assertion(it!=m_path.end());
514     it=std::next(it);
515     if (it==m_path.end())
516     {
517       if (is_closed())
518       { it=m_path.begin(); } // Here the path is closed, and it is the last element of the list
519     }
520   }
521 
retreat_iterator(List_iterator & it)522   void retreat_iterator(List_iterator& it)
523   {
524     if (it==m_path.begin())
525     {
526       if (is_closed())
527       { it=m_path.end(); }
528       else { return; }
529     }
530     it=std::prev(it);
531   }
532 
next_iterator(const List_iterator & it)533   List_iterator next_iterator(const List_iterator& it)
534   {
535     List_iterator res=it;
536     advance_iterator(res);
537     return res;
538   }
539 
prev_iterator(const List_iterator & it)540   List_iterator prev_iterator(const List_iterator& it)
541   {
542     List_iterator res=it;
543     retreat_iterator(res);
544     return res;
545   }
546 
547   /// @return the beginning of the flat
begin_of_flat(const List_iterator & it)548   Dart_const_handle begin_of_flat(const List_iterator& it)
549   {
550     CGAL_assertion(is_valid_iterator(it));
551     return it->begin;
552   }
553 
554   /// @return the end of the flat
end_of_flat(const List_iterator & it)555   Dart_const_handle end_of_flat(const List_iterator& it)
556   {
557     CGAL_assertion(is_valid_iterator(it));
558     return it->end;
559   }
560 
561   /// @return the length of the flat
flat_length(const List_iterator & it)562   int flat_length(const List_iterator& it)
563   {
564     CGAL_assertion(is_valid_iterator(it));
565     return it->length;
566   }
567 
set_flat_length(const List_iterator & it,int i)568   void set_flat_length(const List_iterator& it, int i)
569   {
570     CGAL_assertion(is_valid_iterator(it));
571     it->length=i;
572   }
573 
decrease_flat_length(const List_iterator & it)574   void decrease_flat_length(const List_iterator& it)
575   {
576     CGAL_assertion(is_valid_iterator(it));
577     CGAL_assertion(flat_length(it)!=0);
578     if (flat_length(it)>0) { --(it->length); }
579     else                   { ++(it->length); }
580   }
581 
increase_flat_length(const List_iterator & it)582   void increase_flat_length(const List_iterator& it)
583   {
584     CGAL_assertion(is_valid_iterator(it));
585     if (flat_length(it)>=0) { ++(it->length); }
586     else                    { --(it->length); }
587   }
588 
set_begin_of_flat(const List_iterator & it,Dart_const_handle dh)589   void set_begin_of_flat(const List_iterator& it, Dart_const_handle dh)
590   {
591     CGAL_assertion(is_valid_iterator(it));
592     it->begin=dh;
593   }
594 
set_end_of_flat(const List_iterator & it,Dart_const_handle dh)595   void set_end_of_flat(const List_iterator& it, Dart_const_handle dh)
596   {
597     CGAL_assertion(is_valid_iterator(it));
598     it->end=dh;
599   }
600 
601   /// @return the second dart of the given flat.
second_dart_of_a_flat(const List_iterator & it)602   Dart_const_handle second_dart_of_a_flat(const List_iterator& it)
603   {
604     if (flat_length(it)>0)
605     { return get_map().template beta<1, 2, 1>(begin_of_flat(it)); }
606 
607     CGAL_assertion(flat_length(it)<0);
608     return get_map().template beta<2, 0, 2, 0, 2>(begin_of_flat(it));
609   }
610 
611   /// @return the dart before the last dart of the given flat.
before_last_dart_of_a_flat(const List_iterator & it)612   Dart_const_handle before_last_dart_of_a_flat(const List_iterator& it)
613   {
614     if (flat_length(it)>0)
615     { return get_map().template beta<0, 2, 0>(end_of_flat(it)); }
616 
617     CGAL_assertion(flat_length(it)<0);
618     return get_map().template beta<2, 1, 2, 1, 2>(end_of_flat(it));
619   }
620 
621   /// @return the turn between dart it and next dart.
622   ///         (turn is position of the second edge in the cyclic ordering of
623   ///          edges starting from the first edge around the second extremity
624   ///          of the first dart)
next_positive_turn(const List_iterator & it)625   std::size_t next_positive_turn(const List_iterator& it)
626   {
627     CGAL_assertion(next_flat_exist(it));
628     return m_MQ.positive_turn(end_of_flat(it), begin_of_flat(next_iterator(it)));
629   }
630 
631   /// Same than next_positive_turn but turning in reverse orientation around vertex.
next_negative_turn(const List_iterator & it)632   std::size_t next_negative_turn(const List_iterator& it)
633   {
634     CGAL_assertion(next_flat_exist(it));
635     return m_MQ.negative_turn(end_of_flat(it), begin_of_flat(next_iterator(it)));
636   }
637 
compute_positive_turns()638   std::vector<std::size_t> compute_positive_turns()
639   {
640     std::vector<std::size_t> res;
641     if (is_empty()) return res;
642     for (auto it=m_path.begin(), itend=m_path.end(); it!=itend; ++it)
643     {
644       if (next_flat_exist(it))
645       { res.push_back(next_positive_turn(it)); }
646     }
647     return res;
648   }
649 
compute_negative_turns()650   std::vector<std::size_t> compute_negative_turns()
651   {
652     std::vector<std::size_t> res;
653     if (is_empty()) return res;
654     for (auto it=m_path.begin(), itend=m_path.end(); it!=itend; ++it)
655     {
656       if (next_flat_exist(it))
657       { res.push_back(next_negative_turn(it)); }
658     }
659     return res;
660   }
661 
compute_turns(bool positive)662   std::vector<std::size_t> compute_turns(bool positive)
663   { return (positive?compute_positive_turns():compute_negative_turns()); }
664 
flat_modified(const List_iterator & it,Set_of_it & modified_flats)665   void flat_modified(const List_iterator& it,
666                      Set_of_it& modified_flats)
667   {
668     modified_flats.insert(it);
669     if (prev_flat_exist(it))
670     { modified_flats.insert(prev_iterator(it)); }
671     if (next_flat_exist(it))
672     { modified_flats.insert(next_iterator(it)); }
673   }
674 
erase_flat(List_iterator & it,Set_of_it & modified_flats)675   void erase_flat(List_iterator& it, Set_of_it& modified_flats)
676   {
677     if (next_flat_exist(it))
678     { modified_flats.insert(next_iterator(it)); }
679     if (prev_flat_exist(it))
680     { modified_flats.insert(prev_iterator(it)); }
681 
682     modified_flats.erase(it);
683 
684     it=m_path.erase(it); // after the erase, it is the element after the erased one
685     if (prev_flat_exist(it))
686     {
687       retreat_iterator(it); // this is why we move it backward here
688     }
689   }
690 
merge_modified_flats_when_possible(Set_of_it & modified_flats)691   List_iterator merge_modified_flats_when_possible(Set_of_it& modified_flats)
692   {
693     if (m_path.empty())
694     {
695       m_is_closed=false;
696       return m_path.end();
697     }
698 
699     List_iterator smallest_it=m_path.end();
700     for (auto it=modified_flats.begin(), itend=modified_flats.end();
701          it!=itend; ++it)
702     {
703       if (prev_flat_exist(*it) && modified_flats.count(prev_iterator(*it))==0)
704       { smallest_it=*it; }
705     }
706 
707     List_iterator itcur;
708     if (smallest_it==m_path.end())
709     { // Case of a closed path, with all paths modified
710       for (auto it=modified_flats.begin(); it!=modified_flats.end(); ++it)
711       {
712         itcur=*it;
713         while(merge_with_next_flat_if_possible(itcur, modified_flats))
714         {}
715       }
716       return m_path.begin();
717     }
718 
719     for (auto it=modified_flats.begin(); it!=modified_flats.end(); ++it)
720     {
721       itcur=*it;
722       while(merge_with_next_flat_if_possible(itcur, modified_flats))
723       {}
724     }
725 
726     // Note that smallest is not necessarily the smallest, this is a flat
727     // such that the previous flat is not modified.
728     return smallest_it;
729   }
730 
731   /// Reduce the length of the flat part starting at 'it' from its beginning
732   /// 'it' moves to the previous flat if the current flat disapeared.
733   /// The path could be not valid after this operation (consistency with next
734   /// element should be ensure, by possibly updating the next flat part).
735   /// @return true iff the flat disapeared after its reduction.
reduce_flat_from_beginning(List_iterator & it,Set_of_it & modified_flats)736   bool reduce_flat_from_beginning(List_iterator& it,
737                                   Set_of_it& modified_flats)
738   {
739     CGAL_assertion(is_valid_iterator(it));
740 
741     CGAL_assertion(m_length>0);
742     --m_length;
743 
744     flat_modified(it, modified_flats);
745 
746     if (flat_length(it)==0)
747     {
748       erase_flat(it, modified_flats);
749       return true;
750     }
751 
752     set_begin_of_flat(it, second_dart_of_a_flat(it));
753     decrease_flat_length(it);
754     return false;
755   }
756 
757   /// Reduce the length of the flat part starting at 'it' from its end.
758   /// 'it' moves to the previous flat if the current flat disapeared.
759   /// The path could be not valid after this operation (consistency with next
760   /// element should be ensure, by possibly updating the next flat part).
761   /// @return true iff the flat disapeared after its reduction.
reduce_flat_from_end(List_iterator & it,Set_of_it & modified_flats)762   bool reduce_flat_from_end(List_iterator& it,
763                             Set_of_it& modified_flats)
764   {
765     CGAL_assertion(is_valid_iterator(it));
766 
767     CGAL_assertion(m_length>0);
768     --m_length;
769 
770     flat_modified(it, modified_flats);
771 
772     if (flat_length(it)==0)
773     {
774       erase_flat(it, modified_flats);
775       return true;
776     }
777 
778     set_end_of_flat(it, before_last_dart_of_a_flat(it));
779     decrease_flat_length(it);
780     return false;
781   }
782 
783   /// @return true iff the flat 'it' can be merged with its next flat.
can_merge_with_next_flat(const List_iterator & it,bool & positive2,bool & negative2)784   bool can_merge_with_next_flat(const List_iterator& it,
785                                 bool& positive2, bool& negative2)
786   {
787     if (m_path.size()<=1) { return false; }
788     if (!next_flat_exist(it)) { return false; }
789 
790     List_iterator it2=next_iterator(it);
791     CGAL_assertion(m_path.size()>1 || it==it2);
792     if (it==it2) { return false; } // only one flat in the path
793 
794     positive2=(!m_use_only_negative && (next_positive_turn(it)==2));
795     negative2=(!m_use_only_positive && (next_negative_turn(it)==2));
796     CGAL_assertion(!(positive2 && negative2));
797 
798     if (!positive2 && !negative2) { return false; } // the middle turn is not a flat
799 
800     bool positive1=(flat_length(it)>=0);
801     bool negative1=(flat_length(it)<=0);
802     bool positive3=(flat_length(it2)>=0);
803     bool negative3=(flat_length(it2)<=0);
804 
805     return ((positive1 && positive2 && positive3) ||
806             (negative1 && negative2 && negative3));
807   }
808 
809   /// @return true iff the flat 'it' can be merged with its next flat.
can_merge_with_next_flat(const List_iterator & it)810   bool can_merge_with_next_flat(const List_iterator& it)
811   {
812     bool dummy1, dummy2;
813     return can_merge_with_next_flat(it, dummy1, dummy2);
814   }
815 
816   /// Merge flat 'it' with its next flat if it is possible.
817   /// @return true if a merging was done.
merge_with_next_flat_if_possible(const List_iterator & it,Set_of_it & modified_flats)818   bool merge_with_next_flat_if_possible(const List_iterator& it,
819                                         Set_of_it& modified_flats)
820   {
821     bool positive2=false, negative2=false;
822     if (!can_merge_with_next_flat(it, positive2, negative2))
823     { return false; }
824 
825 #ifdef CGAL_TRACE_PATH_TESTS
826     std::cout<<"Merge with next flat: ";
827     display_flat(std::cout, it); std::cout<<" and ";
828     display_flat(std::cout, next_iterator(it)); std::cout<<std::endl;
829 #endif
830 
831     List_iterator it2=next_iterator(it);
832     set_flat_length(it, flat_length(it)+flat_length(it2)+
833                     (positive2?1:-1));
834     set_end_of_flat(it, end_of_flat(it2));
835 
836     if (modified_flats.count(it2)==1)
837       modified_flats.erase(it2);
838 
839     m_path.erase(it2);
840 
841     // CGAL_assertion(is_flat_valid(it));
842     return true;
843   }
844 
845   /// Merge flat 'it' with its next flat if it is possible.
846   /// @return true if a merging was done.
merge_with_next_flat_if_possible(const List_iterator & it)847   bool merge_with_next_flat_if_possible(const List_iterator& it)
848   {
849     Set_of_it dummy;
850     return merge_with_next_flat_if_possible(it, dummy);
851   }
852 
853   /// Merge the last flat of this path with its next flat if it is possible.
854   /// @return true if a merging was done.
merge_last_flat_with_next_if_possible()855   bool merge_last_flat_with_next_if_possible()
856   {
857     List_iterator lastit=std::prev(m_path.end());
858     return merge_with_next_flat_if_possible(lastit);
859   }
860 
861   /// Return true if it is the beginning of a spur.
is_spur(const List_iterator & it)862   bool is_spur(const List_iterator& it)
863   {
864     CGAL_assertion(is_valid_iterator(it));
865     return next_flat_exist(it) &&
866         get_map().template beta<2>(end_of_flat(it))==
867         begin_of_flat(next_iterator(it));
868   }
869 
870   /// Remove the spur given by 'it'.
871   /// After the operation, either 'it' stay on the same element (if the flat
872   /// still exist), or 'it' move to the previous element if the flat containing
873   /// the spur is removed.
874   /// ('it' will be equal to m_path.end() if the path becomes empty).
remove_spur(List_iterator & it)875   void remove_spur(List_iterator& it)
876   {
877 #ifdef CGAL_TRACE_PATH_TESTS
878         std::cout<<"Remove spur between flats: ";
879         display_flat(std::cout, it); std::cout<<" and ";
880         display_flat(std::cout, next_iterator(it)); std::cout<<std::endl;
881 #endif
882 
883     CGAL_assertion(is_spur(it));
884     Set_of_it modified_flats;
885     List_iterator it2=next_iterator(it); // it2 is the next flat
886 
887     // We reduce the first flat
888     reduce_flat_from_end(it, modified_flats); // If flat 'it' is erased, it is moved to the previous flat
889 
890     // We reduce the second flat
891     reduce_flat_from_beginning(it2, modified_flats);
892 
893     // We merge adjacent flats if possible
894     it=merge_modified_flats_when_possible(modified_flats);
895 
896     // CGAL_assertion(is_valid_iterator(it));
897     // CGAL_assertion(is_valid());
898   }
899 
900   /// Move 'it' to the next spur after it. Go to m_path.end() if there is no
901   /// spur in the path.
move_to_next_spur(List_iterator & it)902   void move_to_next_spur(List_iterator& it)
903   {
904     CGAL_assertion(is_valid_iterator(it));
905     List_iterator itend=(is_closed()?it:m_path.end());
906     do
907     {
908       advance_iterator(it);
909       if (it!=m_path.end() && is_spur(it)) { return; }
910     }
911     while(it!=itend);
912     it=m_path.end(); // Here there is no spur in the whole path
913   }
914 
915   /// Simplify the path by removing all spurs, if all==true; otherwise
916   /// remove only one spur.
917   /// @return true iff at least one spur was removed
918   bool remove_spurs(bool all=true)
919   {
920     bool res=false;
921     List_iterator it=m_path.begin();
922     while(it!=m_path.end())
923     {
924       if (is_spur(it))
925       {
926         remove_spur(it); res=true;
927         // CGAL_assertion(is_valid_iterator(it));
928         // CGAL_assertion(is_valid());
929         if (!all) { return true; }
930       }
931       else { move_to_next_spur(it); }
932     }
933     // CGAL_assertion(is_valid());
934     return res;
935   }
936 
937   /// Return true if 'it' is the beginning of a positive bracket.
938   /// If true, 'itend' is updated to be the end of the bracket
is_positive_bracket(const List_iterator & it,List_iterator & itend)939   bool is_positive_bracket(const List_iterator& it,
940                            List_iterator& itend)
941   {
942     CGAL_assertion(is_valid_iterator(it));
943     if (m_use_only_negative || !next_flat_exist(it) ||
944         next_positive_turn(it)!=1)
945     { return false; }
946     // Here first positive turn is 1
947 
948     itend=next_iterator(it);
949     // Here itend is the beginning of the second flat
950     if (flat_length(itend)<0)
951     { return false; } // This is not a positive bracket, we have some -2
952 
953     if (!next_flat_exist(itend) || next_positive_turn(itend)!=1)
954     { return false; }
955 
956     // Now we move itend to the beginning of the third flat.
957     advance_iterator(itend);
958     return true;
959   }
960 
961   /// Return true if it is the beginning of a negative bracket.
962   /// If true, itend is updated to be the end of the bracket
is_negative_bracket(const List_iterator & it,List_iterator & itend)963   bool is_negative_bracket(const List_iterator& it,
964                            List_iterator& itend)
965   {
966     CGAL_assertion(is_valid_iterator(it));
967     if (m_use_only_positive || !next_flat_exist(it) ||
968         next_negative_turn(it)!=1)
969     { return false; }
970     // Here first negative turn is 1
971 
972     itend=next_iterator(it);
973     // Here itend is the beginning of the second flat
974     if (flat_length(itend)>0)
975     { return false; } // This is not a negative bracket, we have some +2
976 
977     if (!next_flat_exist(itend) || next_negative_turn(itend)!=1)
978     { return false; }
979 
980     // Now we move itend to the beginning of the third flat.
981     advance_iterator(itend);
982     return true;
983   }
984 
985   /// Move 'it' to the next bracket after 'it'. Go to m_path.end() if there
986   /// is no bracket in the path.
move_to_next_bracket(List_iterator & it)987   void move_to_next_bracket(List_iterator& it)
988   {
989     CGAL_assertion(is_valid_iterator(it));
990     List_iterator itend=(is_closed()?it:m_path.end());
991     List_iterator it2;
992     do
993     {
994       advance_iterator(it);
995       if (it!=m_path.end() &&
996           (is_positive_bracket(it, it2) || is_negative_bracket(it, it2)))
997       { return; }
998     }
999     while(it!=itend);
1000     it=m_path.end(); // Here there is no bracket in the whole path
1001   }
1002 
1003   /// Remove the given negative bracket. it1 is the flat beginning
1004   /// of the bracket; it3 is the flat end of the bracket.
remove_negative_bracket(List_iterator & it1,List_iterator & it3)1005   void remove_negative_bracket(List_iterator& it1, List_iterator& it3)
1006   {
1007 #ifdef CGAL_TRACE_PATH_TESTS
1008     std::cout<<"Remove negative bracket: ";
1009     display_flat(std::cout, it1); std::cout<<", ";
1010     display_flat(std::cout, next_iterator(it1)); std::cout<<" and ";
1011     display_flat(std::cout, it3); std::cout<<std::endl;
1012 #endif
1013 
1014     Set_of_it modified_flats;
1015     List_iterator it2;
1016     CGAL_assertion(is_negative_bracket(it1, it2)); // Here it2 is unused
1017 
1018     if (size_of_list()==1) // it1==it3
1019     { // Case of cyclic bracket
1020       CGAL_assertion(size_of_list()==1);
1021       CGAL_assertion(flat_length(it1)<0);
1022       set_begin_of_flat(it1, get_map().template  beta<2,0,2,1>(begin_of_flat(it1)));
1023       set_end_of_flat(it1, get_map().template  beta<2,1,2,0>(end_of_flat(it1)));
1024       set_flat_length(it1, (-flat_length(it1))-2);
1025       flat_modified(it1, modified_flats);
1026       it1=merge_modified_flats_when_possible(modified_flats);
1027       CGAL_assertion(m_length>1);
1028       m_length-=2;
1029       return;
1030     }
1031 
1032     it2=next_iterator(it1); // it2 is the next flat after it1
1033 
1034     reduce_flat_from_end(it1, modified_flats); // decrease also m_length
1035     reduce_flat_from_beginning(it3, modified_flats);
1036 
1037     it1=it2; // Beginning of the second flat, we are sure it still exists
1038 
1039     set_begin_of_flat(it2, get_map().template  beta<2,1,1>(begin_of_flat(it2)));
1040     set_end_of_flat(it2, get_map().template  beta<2,1,1>(end_of_flat(it2)));
1041     set_flat_length(it2, -flat_length(it2));
1042 
1043     flat_modified(it2, modified_flats);
1044 
1045     it1=merge_modified_flats_when_possible(modified_flats);
1046 
1047     // CGAL_assertion(is_valid());
1048   }
1049 
1050   /// Remove the given positive bracket. 'it1' is the flat beginning
1051   /// of the bracket; 'it3' is the flat end of the bracket.
remove_positive_bracket(List_iterator & it1,List_iterator & it3)1052   void remove_positive_bracket(List_iterator& it1,
1053                                List_iterator& it3)
1054   {
1055 #ifdef CGAL_TRACE_PATH_TESTS
1056     std::cout<<"Remove positive bracket: ";
1057     display_flat(std::cout, it1); std::cout<<", ";
1058     display_flat(std::cout, next_iterator(it1)); std::cout<<" and ";
1059     display_flat(std::cout, it3); std::cout<<std::endl;
1060 #endif
1061 
1062     Set_of_it modified_flats;
1063     List_iterator it2;
1064     CGAL_assertion(is_positive_bracket(it1, it2)); // Here it2 is unused
1065 
1066     if (size_of_list()==1) // it1==it3
1067     { // Case of cyclic bracket
1068       CGAL_assertion(size_of_list()==1);
1069       CGAL_assertion(flat_length(it1)>0);
1070       set_begin_of_flat(it1, get_map().template  beta<1,2,0,2>(begin_of_flat(it1)));
1071       set_end_of_flat(it1, get_map().template  beta<0,2,1,2>(end_of_flat(it1)));
1072       set_flat_length(it1, -(flat_length(it1)-2));
1073       flat_modified(it1, modified_flats);
1074       it1=merge_modified_flats_when_possible(modified_flats);
1075       CGAL_assertion(m_length>1);
1076       m_length-=2;
1077       return;
1078     }
1079 
1080     it2=next_iterator(it1); // it2 is the next flat after it1
1081 
1082     reduce_flat_from_end(it1, modified_flats); // decrease also m_length
1083     reduce_flat_from_beginning(it3, modified_flats);
1084 
1085     it1=it2; // Beginning of the second flat, we are sure it exists
1086 
1087     set_begin_of_flat(it2, get_map().template  beta<1,1,2>(begin_of_flat(it2)));
1088     set_end_of_flat(it2, get_map().template  beta<1,1,2>(end_of_flat(it2)));
1089     set_flat_length(it2, -flat_length(it2));
1090 
1091     flat_modified(it2, modified_flats);
1092 
1093     it1=merge_modified_flats_when_possible(modified_flats);
1094 
1095     // CGAL_assertion(is_valid());
1096   }
1097 
1098   /// Simplify the path by removing all brackets, if all==true (default),
1099   /// or by removing only one bracket, if all==false.
1100   /// @return true iff at least one bracket was removed
1101   bool remove_brackets(bool all=true)
1102   {
1103     // CGAL_assertion(is_valid());
1104     bool res=false;
1105     List_iterator it1=m_path.begin();
1106     List_iterator it2;
1107     while(it1!=m_path.end())
1108     {
1109       // CGAL_assertion(is_valid());
1110       if (is_positive_bracket(it1, it2))
1111       {
1112         remove_positive_bracket(it1, it2); res=true;
1113         // CGAL_assertion(is_valid_iterator(it1));
1114         // CGAL_assertion(is_valid());
1115       }
1116       else if (is_negative_bracket(it1, it2))
1117       {
1118         remove_negative_bracket(it1, it2); res=true;
1119         // CGAL_assertion(is_valid_iterator(it1));
1120         // CGAL_assertion(is_valid());
1121       }
1122       else { move_to_next_bracket(it1); }
1123       if (!all && res) { return true; }
1124     }
1125     // CGAL_assertion(is_valid());
1126     return res;
1127   }
1128 
1129   /// @return true iff 'it' is the beginning of a l-shape.
is_l_shape(const List_iterator & it)1130   bool is_l_shape(const List_iterator& it)
1131   {
1132     CGAL_assertion(is_valid_iterator(it));
1133     if (!next_flat_exist(it)) { return false; }
1134     std::size_t t=next_negative_turn(it);
1135     return (t==1 ||
1136             (flat_length(it)<0 && size_of_list()==1 && t==2));
1137   }
1138 
1139   /// Move it to the next l_shape after it. Go to m_path.end() if there is no
1140   /// l_shape in the path.
move_to_next_l_shape(List_iterator & it)1141   void move_to_next_l_shape(List_iterator& it)
1142   {
1143     // CGAL_assertion(is_valid());
1144     CGAL_assertion(is_valid_iterator(it));
1145     List_iterator itend=(is_closed()?it:m_path.end());
1146     do
1147     {
1148       advance_iterator(it);
1149       if (it!=m_path.end() && is_l_shape(it)) { return; }
1150     }
1151     while(it!=itend);
1152     it=m_path.end(); // Here there is no spur in the whole path
1153   }
1154 
1155   /// @return true iff the flat before flat 'it' can be extended by adding
1156   ///              dart 'dh' to its end.
is_prev_flat_can_be_extended_at_end(const List_iterator & it,Dart_const_handle dh,bool & positive_flat,bool & negative_flat)1157   bool is_prev_flat_can_be_extended_at_end(const List_iterator& it,
1158                                            Dart_const_handle dh,
1159                                            bool& positive_flat,
1160                                            bool& negative_flat)
1161   {
1162     positive_flat=false; negative_flat=false;
1163     if (!prev_flat_exist(it)) { return false; }
1164     List_iterator ittemp=prev_iterator(it);
1165     if (!m_use_only_negative && m_MQ.positive_turn(end_of_flat(ittemp), dh)==2)
1166     { positive_flat=true; }
1167     if (!m_use_only_positive && m_MQ.negative_turn(end_of_flat(ittemp), dh)==2)
1168     { negative_flat=true; }
1169 
1170     if (flat_length(ittemp)==0) // Case of flat lengh 0
1171     { return positive_flat || negative_flat; }
1172 
1173     return (flat_length(ittemp)>0 && positive_flat) ||
1174       (flat_length(ittemp)<0 && negative_flat);
1175   }
1176 
1177   /// @return true iff the flat before flat 'it' can be extended by adding
1178   ///              dart 'dh' to its end.
is_prev_flat_can_be_extended_at_end(const List_iterator & it,Dart_const_handle dh)1179   bool is_prev_flat_can_be_extended_at_end(const List_iterator& it,
1180                                            Dart_const_handle dh)
1181   {
1182     bool dummy1, dummy2;
1183     return is_prev_flat_can_be_extended_at_end(it, dh, dummy1, dummy2);
1184   }
1185 
1186   /// @return true iff the flat after flat 'it' can be extended by adding
1187   ///              dart 'dh' to its beginning.
is_next_flat_can_be_extended_at_beginning(const List_iterator & it,Dart_const_handle dh,bool & positive_flat,bool & negative_flat)1188   bool is_next_flat_can_be_extended_at_beginning(const List_iterator& it,
1189                                                  Dart_const_handle dh,
1190                                                  bool& positive_flat,
1191                                                  bool& negative_flat)
1192   {
1193      CGAL_assertion(is_valid_iterator(it));
1194 
1195      positive_flat=false; negative_flat=false;
1196      if (!next_flat_exist(it)) { return false; }
1197      List_iterator ittemp=next_iterator(it);
1198      if (!m_use_only_negative && m_MQ.positive_turn(dh, begin_of_flat(ittemp))==2)
1199      { positive_flat=true; }
1200      if (!m_use_only_positive && m_MQ.negative_turn(dh, begin_of_flat(ittemp))==2)
1201      { negative_flat=true; }
1202 
1203      if (flat_length(ittemp)==0)  // Case of flat lengh 0
1204      { return positive_flat || negative_flat; }
1205 
1206      return (flat_length(ittemp)>0 && positive_flat) ||
1207        (flat_length(ittemp)<0 && negative_flat);
1208   }
1209 
1210   /// @return true iff the flat after flat 'it' can be extended by adding
1211   ///              dart 'dh' to its beginning.
is_next_flat_can_be_extended_at_beginning(const List_iterator & it,Dart_const_handle dh)1212   bool is_next_flat_can_be_extended_at_beginning(const List_iterator& it,
1213                                                  Dart_const_handle dh)
1214   {
1215     bool dummy1, dummy2;
1216     return is_next_flat_can_be_extended_at_beginning(it, dh, dummy1, dummy2);
1217   }
1218 
1219   /// @return true iff the flat 'it' forms a switchable subpath (aka left-L-shape)
is_switchable(const List_iterator & it)1220   bool is_switchable(const List_iterator& it)
1221   {
1222     CGAL_assertion(is_valid_iterator(it));
1223     if (it == m_path.begin() || std::next(it) == m_path.end()) { return false; }
1224     std::size_t t=next_positive_turn(it);
1225     return (t==1 && flat_length(it) >= 0);
1226   }
1227 
1228   /// Add the given dart 'dh' before the flat 'it'.
add_dart_before(const List_iterator & it,Dart_const_handle dh,Set_of_it & modified_flats)1229   void add_dart_before(const List_iterator& it, Dart_const_handle dh,
1230                        Set_of_it& modified_flats)
1231   {
1232     CGAL_assertion(is_valid_iterator(it));
1233 
1234     bool positive_flat, negative_flat;
1235     if (is_prev_flat_can_be_extended_at_end(it, dh,
1236                                             positive_flat, negative_flat))
1237     {
1238       List_iterator ittemp=prev_iterator(it);
1239       set_end_of_flat(ittemp, dh); // Move the last dart of the previous flat
1240       set_flat_length(ittemp, flat_length(ittemp)+(positive_flat?+1:-1)); // Increment the length of the flat
1241       flat_modified(ittemp, modified_flats);
1242     }
1243     else
1244     {
1245       // insert the new element before 'it'
1246       flat_modified(m_path.insert(it, Flat(dh)), modified_flats);
1247     }
1248     ++m_length;
1249   }
1250 
1251   /// Add the given dart 'dh' after the flat 'it'.
add_dart_after(const List_iterator & it,Dart_const_handle dh,Set_of_it & modified_flats)1252   void add_dart_after(const List_iterator& it, Dart_const_handle dh,
1253                       Set_of_it& modified_flats)
1254   {
1255     CGAL_assertion(is_valid_iterator(it));
1256     bool positive_flat, negative_flat;
1257     List_iterator ittemp=next_iterator(it);
1258     if (is_next_flat_can_be_extended_at_beginning(it, dh,
1259                                                   positive_flat, negative_flat))
1260     {
1261       set_begin_of_flat(ittemp, dh); // Move the first dart of the flat
1262       set_flat_length(ittemp, flat_length(ittemp)+(positive_flat?+1:-1)); // Increment the length of the flat
1263       flat_modified(ittemp, modified_flats);
1264     }
1265     else
1266     {
1267       // insert the new element before 'ittemp', thus after 'it'
1268       flat_modified(m_path.insert(ittemp, Flat(dh)), modified_flats);
1269     }
1270     ++m_length;
1271   }
1272 
1273   /// Right push the given l-shape.
right_push_l_shape(List_iterator & it1)1274   void right_push_l_shape(List_iterator& it1)
1275   {
1276 #ifdef CGAL_TRACE_PATH_TESTS
1277         std::cout<<"Right push l-shape: ";
1278         display_flat(std::cout, it1); std::cout<<" and ";
1279         display_flat(std::cout, next_iterator(it1)); std::cout<<std::endl;
1280 #endif
1281 
1282     // it is the beginning of a flat
1283     CGAL_assertion(is_l_shape(it1));
1284     // CGAL_assertion(is_valid());
1285     Set_of_it modified_flats;
1286 
1287     if (m_path.size()==1)
1288     {
1289       if (next_negative_turn(it1)==2)
1290       { // Special case of a global shift
1291         set_flat_length(it1, -flat_length(it1));
1292         set_begin_of_flat(it1, get_map().template beta<2,1,1>(begin_of_flat(it1)));
1293         set_end_of_flat(it1, get_map().template beta<2,1,1>(end_of_flat(it1)));
1294         // CGAL_assertion(is_valid());
1295         return;
1296       }
1297       else // Here negative turn is 1
1298       {
1299         if (flat_length(it1)>0) // Case where the first flat is positive
1300         { // We split the flat in two parts
1301           CGAL_assertion(flat_length(it1)>=1);
1302           Dart_const_handle dh1=begin_of_flat(it1);
1303           Dart_const_handle dh2=end_of_flat(it1);
1304           if (flat_length(it1)==1) // only one flat with two darts
1305           {
1306             reduce_flat_from_end(it1, modified_flats);
1307             it1=m_path.insert(it1, Flat(dh2)); // insert dh1 before it1
1308             flat_modified(it1, modified_flats);
1309             ++m_length;
1310           }
1311           else
1312           {
1313             reduce_flat_from_beginning(it1, modified_flats);
1314             reduce_flat_from_end(it1, modified_flats);
1315             flat_modified(m_path.insert(it1, Flat(dh1)), modified_flats); // insert dh1 before it1
1316             it1=m_path.insert(next_iterator(it1), Flat(dh2)); // insert dh2 after it1
1317             flat_modified(it1, modified_flats);
1318             m_length+=2;
1319           }
1320           // Now we can continue with the normal case because we have 3 flats
1321           CGAL_assertion(flat_length(it1)==0);
1322           CGAL_assertion(flat_length(next_iterator(it1))==0);
1323         }
1324         else
1325         {
1326           // Here we have only one flat, with some -2
1327           // This case is not supposed possible.
1328           CGAL_assertion(false);
1329           return;
1330         }
1331       }
1332     }
1333 
1334     List_iterator it2=next_iterator(it1);  // Beginning of the second flat
1335     CGAL_assertion(it1!=it2);
1336 
1337     if (flat_length(it1)>0) // Case where the first flat is positive
1338     { // We split the flat in two parts
1339       Dart_const_handle dh=end_of_flat(it1); // last dart of the first flat
1340       reduce_flat_from_end(it1, modified_flats);
1341       it1=m_path.insert(it2, Flat(dh)); // insert dh before it2
1342       ++m_length;
1343       flat_modified(it1, modified_flats);
1344     }
1345 
1346     if (flat_length(it2)>0) // Case where the second flat is positive, we need to
1347     { // split it into two parts
1348       Dart_const_handle dh=begin_of_flat(it2); // first dart of the second flat
1349       reduce_flat_from_beginning(it2, modified_flats);
1350       it2=m_path.insert(it2, Flat(dh)); // insert dh before the second flat
1351       ++m_length;
1352       flat_modified(it2, modified_flats);
1353     }
1354 
1355     // CGAL_assertion(is_valid(false));
1356     // CGAL_assertion(is_l_shape(it1));
1357 
1358     Dart_const_handle dh1=get_map().template beta<2,1>(begin_of_flat(it1));
1359     Dart_const_handle dh2=get_map().template beta<1>(dh1);
1360     Dart_const_handle dh3=get_map().template beta<2,1,2,0>(end_of_flat(it1));
1361     Dart_const_handle dh4=get_map().template beta<2,0,2,1>(begin_of_flat(it2));
1362     Dart_const_handle dh5=get_map().template beta<2,0,0>(end_of_flat(it2));
1363     Dart_const_handle dh6=get_map().template beta<1>(dh5);
1364 
1365     bool first_flat_zero=(flat_length(it1)==0);
1366     bool second_flat_zero=(flat_length(it2)==0);
1367 
1368     if (first_flat_zero)
1369     {
1370       if (second_flat_zero)
1371       { // Special case of a l-shape with only 2 darts
1372         set_begin_of_flat(it1, dh1);
1373         set_end_of_flat(it1, dh1);
1374         set_begin_of_flat(it2, dh6);
1375         set_end_of_flat(it2, dh6);
1376 
1377         flat_modified(it1, modified_flats);
1378         flat_modified(it2, modified_flats);
1379 
1380         it1=merge_modified_flats_when_possible(modified_flats);
1381 
1382         // CGAL_assertion(is_valid());
1383         return;
1384       }
1385       else
1386       { // Here first flat length is 0, while second flat not
1387         set_begin_of_flat(it1, dh1);
1388         set_end_of_flat(it1, dh5);
1389         set_begin_of_flat(it2, dh6);
1390         set_end_of_flat(it2, dh6);
1391         set_flat_length(it1, -flat_length(it2));
1392         set_flat_length(it2, 0);
1393 
1394         flat_modified(it1, modified_flats);
1395         flat_modified(it2, modified_flats);
1396 
1397         it1=merge_modified_flats_when_possible(modified_flats);
1398 
1399         // CGAL_assertion(is_valid());
1400         return;
1401       }
1402     }
1403     else
1404     {
1405       if (second_flat_zero)
1406       { // Here first flat length is non zero, while second flat length is 0
1407         set_begin_of_flat(it1, dh1);
1408         set_end_of_flat(it1, dh1);
1409         set_begin_of_flat(it2, dh2);
1410         set_end_of_flat(it2, dh6);
1411         set_flat_length(it2, -flat_length(it1));
1412         set_flat_length(it1, 0);
1413 
1414         flat_modified(it1, modified_flats);
1415         flat_modified(it2, modified_flats);
1416 
1417         it1=merge_modified_flats_when_possible(modified_flats);
1418 
1419         // CGAL_assertion(is_valid());
1420         return;
1421       }
1422     }
1423 
1424     // General case, with two flats with non zero length
1425     if (next_iterator(it2)!=it1 || next_negative_turn(it2)!=3)
1426     {
1427       // 1) Add the first dart before flat 'it'
1428       add_dart_before(it1, dh1, modified_flats);
1429 
1430       // 2) Add the last dart after flat 'it2'
1431       add_dart_after(it2, dh6, modified_flats);
1432     }
1433     else
1434     { // Case "-2^s -1 -2^t -3" is a special case
1435       dh2=get_map().template beta<2,0>(dh1);
1436       dh5=get_map().template beta<2,1>(dh6);
1437       increase_flat_length(it1);
1438       increase_flat_length(it2);
1439       m_length+=2;
1440       flat_modified(it1, modified_flats);
1441       flat_modified(it2, modified_flats);
1442     }
1443 
1444     // 3) Move the first flat
1445     CGAL_assertion(flat_length(it1)<0);
1446     set_begin_of_flat(it1, dh2);
1447     set_flat_length(it1, -(flat_length(it1))-1);
1448     if (flat_length(it1)==0) { set_end_of_flat(it1, dh2); }
1449     else { set_end_of_flat(it1, dh3); } // End of the moved flat
1450     flat_modified(it1, modified_flats);
1451 
1452     // 4) Move the second flat
1453     CGAL_assertion(flat_length(it2)<0);
1454     set_begin_of_flat(it2, dh4);
1455     set_flat_length(it2, -(flat_length(it2))-1);
1456     if (flat_length(it2)==0) { set_end_of_flat(it2, dh4); }
1457     else { set_end_of_flat(it2, dh5); } // End of the moved flat
1458     flat_modified(it2, modified_flats);
1459 
1460     CGAL_assertion(m_length>1);
1461     m_length-=2;
1462 
1463     it1=merge_modified_flats_when_possible(modified_flats);
1464   }
1465 
1466   /// Right push the path, if all all l-shape are pushed, otherwise only one.
1467   /// @return true iff the path was pushed
1468   bool right_push(bool all=true)
1469   {
1470     bool res=false;
1471     List_iterator it=m_path.begin();
1472     while(it!=m_path.end())
1473     {
1474       if (is_l_shape(it))
1475       {
1476         right_push_l_shape(it); res=true;
1477         // CGAL_assertion(is_valid_iterator(it));
1478         // CGAL_assertion(is_valid());
1479         if (!all) { return true; }
1480       }
1481       else { move_to_next_l_shape(it); }
1482     }
1483     // CGAL_assertion(is_valid());
1484     return res;
1485   }
1486 
1487   /// Canonize the path
canonize()1488   void canonize()
1489   {
1490     CGAL_assertion(is_valid());
1491 
1492     if (is_empty()) { return; }
1493 
1494     CGAL_assertion(is_closed());
1495 
1496      bool modified=false;
1497      remove_spurs();
1498 
1499      do
1500      {
1501        modified=remove_brackets();
1502        modified=modified || remove_spurs();
1503      }
1504      while(modified);
1505 
1506      right_push();
1507 
1508      CGAL_assertion(remove_brackets()==false);
1509      CGAL_assertion(remove_spurs()==false);
1510      CGAL_assertion(is_valid());
1511   }
1512 
display_positive_turns()1513   void display_positive_turns()
1514   {
1515     std::cout<<"+(";
1516     for (List_iterator it=m_path.begin(), itend=m_path.end();
1517          it!=itend; ++it)
1518     {
1519       if (next_flat_exist(it))
1520       { std::cout<<next_positive_turn(it)<<" "; }
1521     }
1522     std::cout<<")";
1523   }
1524 
display_negative_turns()1525   void display_negative_turns()
1526   {
1527     std::cout<<"-(";
1528     for (List_iterator it=m_path.begin(), itend=m_path.end();
1529          it!=itend; ++it)
1530     {
1531       if (next_flat_exist(it))
1532       { std::cout<<next_negative_turn(it)<<" "; }
1533     }
1534     std::cout<<")";
1535   }
1536 
display_pos_and_neg_turns()1537   void display_pos_and_neg_turns()
1538   {
1539     display_positive_turns();
1540     std::cout<<"  ";
1541     display_negative_turns();
1542   }
1543 
display()1544   void display()
1545   {
1546     if (!is_empty())
1547     {
1548       for (List_iterator it=m_path.begin(), itend=m_path.end();
1549            it!=itend; ++it)
1550       {
1551         std::cout<<"[ ";
1552         std::cout<<get_map().darts().index(begin_of_flat(it))
1553                  <<", "<<get_map().darts().index(end_of_flat(it))
1554                  <<"("<<flat_length(it)<<") ] ";
1555       }
1556       if (is_closed())
1557       { std::cout<<" c "; }
1558       std::cout<<" length("<<length()<<")";
1559     }
1560   }
1561 
1562   friend std::ostream& operator<<(std::ostream& os, const Self& p)
1563   {
1564     const_cast<Self&>(p).display(); // Problem of const correctness: todo solve
1565     return os;
1566   }
1567 
set_m_use_only_positive(bool UOP)1568   void set_m_use_only_positive(bool UOP)
1569   { m_use_only_positive=UOP; }
1570 
set_m_use_only_negative(bool UON)1571   void set_m_use_only_negative(bool UON)
1572   { m_use_only_negative=UON; }
1573 
1574 protected:
1575   const MQ& m_MQ; // The underlying map (a minimal quadrangulation)
1576   List_of_flats m_path; // The sequence of flats (a flat part is a pair of darts
1577          //  with positive or negative turn == 2). If negative value k, -k is the
1578          //  length of the flat part, for negative turns (-2).
1579   bool m_is_closed; // True iff the path is a cycle
1580   std::size_t m_length;
1581   bool m_use_only_positive;
1582   bool m_use_only_negative;
1583 
1584 #ifdef CGAL_PWRLE_TURN_V2
1585   const TDartIds& m_darts_ids;
1586 #endif //CGAL_PWRLE_TURN_V2
1587 };
1588 
1589 } // namespace internal
1590 } // namespace Surface_mesh_topology
1591 } // namespace CGAL
1592 
1593 #endif // CGAL_PATH_ON_SURFACE_WITH_RLE_H //
1594 // EOF //
1595